Процедура отображения в Nasm (язык ассемблера) [дубликат]

В вашем основном методе вы пытаетесь получить доступ, например, к club (который защищен), когда вы должны получить доступ к myclub, который является публичным свойством, которое вы создали.

12
задан nrz 31 October 2012 в 21:35
поделиться

5 ответов

Не могу комментировать, поэтому я отправляю ответ таким образом. @Ira Baxter, отличный ответ. Я просто хочу добавить, что вам не нужно разделить 10 раз, когда вы разместили, что вы установили регистр cx в значение 10. Просто разделите число в топе до «ax == 0»

loop1: call dividebyten
       ...
       cmp ax,0
       jnz loop1

Вам также нужно сохранить количество цифр в исходном номере.

       mov cx,0
loop1: call dividebyten
       inc cx

В любом случае, вы помогли мне Ире Бакстер, есть только несколько способов оптимизации кода:)

Это касается не только оптимизации, но и форматирования. Если вы хотите напечатать номер 54, вы хотите напечатать 54 не 0000000054:)

1
ответ дан Gondil 22 August 2018 в 00:32
поделиться

Вам нужно написать двоичную процедуру преобразования в десятичный код, а затем использовать десятичные цифры для создания «цифровых символов» для печати. ​​

Вы должны предположить, что что-то где-то будет печатать символ на ваше устройство вывода по выбору. Вызовите эту подпрограмму «print_character»; предполагает, что он принимает код символа в EAX и сохраняет все регистры .. (Если у вас нет такой подпрограммы, у вас есть дополнительная проблема, которая должна быть основой другого вопроса).

Если у вас есть двоичный код для цифры (например, значение от 0 до 9) в регистре (скажем, EAX), вы можете преобразовать это значение в символ для цифры, добавив код ASCII для символа «нуль», чтобы регистр. Это также просто:

       add     eax, 0x30    ; convert digit in EAX to corresponding character digit

Затем вы можете вызвать print_character для печати символьного кода.

Чтобы вывести произвольное значение, вам нужно выбрать цифры и распечатать их .

Выбор цифр принципиально требует работы с полномочиями в десять. Проще всего работать с одной мощностью десять, например, самой 10. Представьте, что у нас есть подпрограмма «разделить на 10», которая взяла значение в EAX, и произвела коэффициент в EDX и остальную часть EAX. Я оставляю это как упражнение для вас, чтобы выяснить, как реализовать такую ​​процедуру.

Тогда простая процедура с правильной идеей состоит в том, чтобы произвести одну цифру для всех цифр, которые могут иметь значение. 32-битный регистр хранит значения до 4 миллиардов, поэтому вы можете напечатать 10 цифр. Итак:

         mov    eax, valuetoprint
         mov    ecx, 10        ;  digit count to produce
loop:    call   dividebyten
         add    eax, 0x30
         call   printcharacter
         mov    eax, edx
         dec    ecx
         jne    loop

Это работает ... но печатает цифры в обратном порядке. К сожалению! Ну, мы можем воспользоваться стеком pushdown для хранения произведенных цифр, а затем вывести их в обратном порядке:

         mov    eax, valuetoprint
         mov    ecx, 10        ;  digit count to generate
loop1:   call   dividebyten
         add    eax, 0x30
         push   eax
         mov    eax, edx
         dec    ecx
         jne    loop1
         mov    ecx, 10        ;  digit count to print
loop2:   pop    eax
         call   printcharacter
         dec    ecx
         jne    loop2

Слева в качестве упражнения для читателя: подавляйте ведущие нули. Кроме того, поскольку мы пишем цифры в памяти, вместо того, чтобы записывать их в стек, мы можем записать их в буфер, а затем распечатать содержимое буфера. Также оставлен в качестве упражнения для читателя.

11
ответ дан Ira Baxter 22 August 2018 в 00:32
поделиться

1 -9 равны 1 -9. после этого должно быть какое-то преобразование, о котором я тоже не знаю. Скажем, у вас есть 41H в AX (EAX), и вы хотите распечатать 65, а не «A», не выполняя какой-либо служебный вызов. Я думаю, вам нужно напечатать представление символа 6 и 5, что бы это ни было. Должно быть постоянное число, которое можно добавить туда. Вам нужен оператор модуля (как вы это делаете в сборке) и цикл для всех цифр.

Не уверен, но это мое предположение.

0
ответ дан Joe 22 August 2018 в 00:32
поделиться
  • 1
    Да, это верно. В ASCII кодировки символов от '0' до '9' смежны, поэтому вы можете просто вычислить 6 + '0', чтобы получить '6'. т. е. использовать div или что угодно, чтобы получить остаток, затем add edx, '0' и сохранить этот байт в буфер. '0' = 0x30, но большинство ассемблеров принимают символьные константы, поэтому лучше писать код таким образом. (Это может быть полезно для OR или AND вместо ADD / SUB, что также работает, потому что 0x30 не имеет ни одного из его низких 4 битов.) – Peter Cordes 19 September 2017 в 03:52

Я полагаю, вы хотите напечатать значение для stdout? Если это так, вы должны использовать системный вызов для этого. Системные вызовы зависят от ОС.

, например. Linux: Таблица системных вызовов Linux

Всемирная программа hello в этом учебнике может дать вам некоторые идеи.

0
ответ дан Nicoretti 22 August 2018 в 00:32
поделиться
  • 1
    Спасибо за предложение! Im работает на Windows 7 (x86) на данный момент! Придется взломать экзамен ALP и вам придется собирать коды в среде Win в лаборатории! Однако посмотрим на учебники! Очень признателен! :) – Kaustav Majumder 31 October 2012 в 22:45

Большинство операционных систем / сред не имеют системного вызова, который принимает целые числа и преобразует их в десятичные для вас. Вы должны сделать это самостоятельно, прежде чем отправлять байты в ОС или копировать их в видеопамять самостоятельно или рисовать соответствующие символы шрифта в видеопамяти ...

На сегодняшний день наиболее эффективным способом является создание один системный вызов, который выполняет всю строку сразу, потому что системный вызов, который записывает 8 байтов, в основном такой же, как и запись 1 байта.

Это означает, что нам нужен буфер, но это не добавляет к нашей сложности вообще. 2 ^ 32-1 - это только 4294967295, что составляет всего 10 десятичных цифр. Наш буфер не должен быть большим, поэтому мы можем просто использовать стек.

Обычный алгоритм производит цифры LSD-first. Поскольку порядок печати MSD-first, мы можем просто начать работу в конце буфера и работать в обратном направлении. Для печати или копирования в другом месте просто отслеживайте, где он начинается, и не беспокойтесь о том, чтобы получить его до начала фиксированного буфера.

char *itoa_end(unsigned long val, char *p_end) {
  const unsigned base = 10;
  char *p = p_end;
  do {
    *--p = (val % base) + '0';
    val /= base;
  } while(val);                  // runs at least once to print '0' for val=0.

  // write(1, p,  p_end-p);
  return p;  // let the caller know where the leading digit is
}

gcc / clang выполняет отличную работу, использует магический постоянный множитель ] вместо div, чтобы эффективно делить на 10. ( Godbolt compiler explorer для выхода asm).

Вот прокомментированная версия NASM, в которой используется div (медленный, но более короткий код) для 32-разрядных целых чисел без знака и Системный вызов Linux write. Это должно быть легко переносить на 32-битный режим, просто изменив регистры на ecx вместо rcx. (Вы также должны сохранить / восстановить esi для обычных 32-битных соглашений вызова, если только вы не делаете это в функции макроса или внутреннего использования.)

Часть системного вызова специфичный для 64-разрядной Linux. Замените это на все, что подходит для вашей системы, например. вызовите страницу VDSO для эффективных системных вызовов в 32-разрядной Linux или используйте int 0x80 непосредственно для неэффективных системных вызовов. См. вызывающие соглашения для 32 и 64-битных системных вызовов в Unix / Linux .

ALIGN 16
; void print_uint32(uint32_t edi)
; x86-64 System V calling convention.  Clobbers RSI, RCX, RDX, RAX.
global print_uint32
print_uint32:
    mov    eax, edi              ; function arg

    mov    ecx, 0xa              ; base 10
    push   rcx                   ; newline = 0xa = base
    mov    rsi, rsp
    sub    rsp, 16               ; not needed on 64-bit Linux, the red-zone is big enough.  Change the LEA below if you remove this.

;;; rsi is pointing at '\n' on the stack, with 16B of "allocated" space below that.
.toascii_digit:                ; do {
    xor    edx, edx
    div    ecx                   ; edx=remainder = low digit = 0..9.  eax/=10
                                 ;; DIV IS SLOW.  use a multiplicative inverse if performance is relevant.
    add    edx, '0'
    dec    rsi                 ; store digits in MSD-first printing order, working backwards from the end of the string
    mov    [rsi], dl

    test   eax,eax             ; } while(x);
    jnz  .toascii_digit
;;; rsi points to the first digit


    mov    eax, 1               ; __NR_write from /usr/include/asm/unistd_64.h
    mov    edi, 1               ; fd = STDOUT_FILENO
    lea    edx, [rsp+16 + 1]    ; yes, it's safe to truncate pointers before subtracting to find length.
    sub    edx, esi             ; length, including the \n
    syscall                     ; write(1, string,  digits + 1)

    add  rsp, 24                ; undo the push and the buffer reservation
    ret

Public domain. Не стесняйтесь копировать / вставлять это во все, над чем вы работаете. Если он ломается, вы можете сохранить обе части.

И вот код, который вызывается в цикле, считая до 0 (включая 0). Вставить его в один и тот же файл.

ALIGN 16
global _start
_start:
    mov    ebx, 100
.repeat:
    lea    edi, [rbx + 0]      ; put whatever constant you want here.
    call   print_uint32
    dec    ebx
    jge   .repeat


    xor    edi, edi
    mov    eax, 231
    syscall                             ; sys_exit_group(0)

Собрать и связать с

yasm -felf64 -Worphan-labels -gdwarf2 print-integer.asm &&
ld -o print-integer print-integer.o

./print_integer
100
99
...
1
0

Использовать strace, чтобы увидеть, что единственными системными вызовами этой программы являются write() и exit(). (См. Также советы gdb / debugging в нижней части вики , а также другие ссылки там.)


Я опубликовал версию AT & amp; T-syntax из этого для 64-битных целых чисел в качестве ответа на Печать целого числа в виде строки с синтаксисом AT & amp; T с системными вызовами Linux вместо printf . См. Это для получения дополнительных комментариев о производительности и эталонного кода div по сравнению с компилятором с использованием mul.

2
ответ дан Peter Cordes 22 August 2018 в 00:32
поделиться
Другие вопросы по тегам:

Похожие вопросы: