Linux x86 NASM-подпрограмма: печать dword из EAX

Итак, я изучаю сборку x86 Linux с синтаксисом NASM (О боже, только не это, вы все думаете). Я пытаюсь сделать подпрограмму, которая просто напечатает значение в EAX в stdout. Код запускается и выходит без ошибок, но ничего не печатается. Не могу понять почему. Прежде всего, вот файл, в котором я работаю:

segment .bss
    to_print:   resd 1

segment .text
    global print_eax_val

print_eax_val:                  ;       (top)
    push    dword ebx           ;Stack:  edx
    push    dword ecx           ;        ecx
    push    dword edx           ;        ebx
                                ;       (bot)

    mov     ecx,eax             ;ecx = eax

    mov     [to_print],ecx      ;to_print = ecx

    mov     eax, 4              ;sys_write
    mov     ebx, 1              ;to stdout
    add     ecx, 47             ;add 47 for ASCII numbers
    mov     edx, 2              ;double word = 2 bytes
    int     0x80

    mov     eax, [to_print]     ;eax = original val
    pop     edx                 ;pop the registers back from the stack
    pop     ecx
    pop     ebx                 ;Stack: empty

    ret

это вызывается из моего основного файла, который выглядит так (это, вероятно, не имеет значения, если я чего-то не хватает резкий.)

segment .data
        hello   db      "Hello world!", 0
        newline db      0xA
        len     equ $ - hello
        len2    equ $ - newline

segment .text
        extern print_nl
        extern print_eax_val
        global main

main:
        enter   0,0

        call    print_nl

        mov     eax, 1

        call    print_eax_val

        mov     ebx, 0          ;exit code = 0 (normal)
        mov     eax, 1          ;exit command
        int     0x80            ;ask kernel to quit

print_nl - это еще одна подпрограмма, которая определяет и печатает новую строку. Это успешно работает и печатает новую строку, как ожидалось.

проблема связана с параметром length для my sys_write звонок? Я даю ему 2, который имеет размер dword, что составляет размер EAX регистрация и мой to_print ярлык, который я забронировал с resd 1. В отчаянии я попытался изменить длину на 1, 4, 8, 16 и 32... Ничего работал.

EDIT: для всех, кто интересуется, вот как я исправил код: (я поставлю звездочки на строки, которые я изменил):

segment .bss
    to_print:   resd 1

segment .text
        global print_eax_val

print_eax_val:                      ;       (top)
        push    dword ebx           ;Stack:  edx
        push    dword ecx           ;        ecx
        push    dword edx           ;        ebx
                                    ;       (bot)

        mov     ecx,eax             ;ecx = eax

        mov     [to_print],ecx        ;to_print = ecx

****    add     dword [to_print], 48

        mov     eax, 4              ;sys_write
        mov     ebx, 1              ;to stdout
****    mov     ecx, to_print
        mov     edx, 2
        int     0x80

****    sub     dword [to_print], 48
        mov     eax, [to_print]     ;eax = original val
        pop     edx                 ;pop the registers back from the stack
        pop     ecx
        pop     ebx                 ;Stack: empty

        ret

по сути, ecx должны быть адрес блока, который вы хотите напечатать, а не само значение. Как указано в выбранном ответе, это будет только работа, если eax находится в диапазоне 0-9.

правка 2: поэтому я был немного смущен 2-й параметр для sys_write (тот, который хранится в edx). Я думаю, что это просто относится к числу байтов. Так dword, как я использовал, было бы правильно использовать 4 есть, ибо двойное слово-4 байта, или 32 бита. Я предполагаю, что это сработало, потому что x86 мало-endian. Итак, в памяти шестнадцатеричное значение to_print будет выглядеть так:

90 00 00 00

и с предоставленной длиной два, sys_write получает:

90 00

таким образом, значение к счастью, не повреждается.

позже я изменил код на store to_print как побайтно, используя resb 1 и доступ к нему с помощью byte вместо dword... Байт здесь хорош, потому что я знаю, что не собираюсь давать to_print значение выше 9.

2 ответов


Я очень ржавый в ассемблере, но похоже, что ECX содержит значение 48, когда оно должно содержать адрес буфера для записи.

Я предполагаю, что вы намеревались в print_eax_val Это взять двоичное значение EAX, добавить смещение ASCII в цифру 0 (который должен был быть 48, а не 47), а затем распечатать этот единственный символ. Для этого добавьте 48 перед сохранением значения в to_print, поместить адрес to_print в ECX и установите длину (EDX) в 1, потому что вы пишу только одного персонажа.

теперь помните, что это будет работать только для значений EAX между 0x0000 и 0x0009. Когда вы пройдете мимо 9, вы получите другие символы ASCII.

объяснение того, как взять произвольное двоичное значение EAX и преобразовать его в десятичную строку, которая может быть напечатана, выходит далеко за рамки SO.


на write системный вызов принимает буфер символов для печати, указал на %ecx С длиной выданный %edx. Регистр, как %eax С другой стороны содержит 32-разрядное целое число.

если вы действительно хотите напечатать %eax, вы должны сначала преобразовать это число в последовательность символов. Вы можете скрыть его до ASCII decimal, или hexadecimal, или любой другой базы, которую вы хотите, но вам нужно сделать его символами.