2016-03-21 3 views
2
section .data 

    array dw 1,2,3,4,5,6,7,8,9,10 ; array of integers 
    msg db " numbers are : %d %d ",10,0 

section .text 

global main 
extern printf ; for c printf 

main: 

    push ebp 
    mov ebp,esp  ;intialise stack 

    mov ax,11 
    push ax  ;push ax with value 11 

    mov ax,22 
    push ax  ;push ax with value 12 
    push msg 
    call printf  ; calling printf function 

    add esp ,12 

    mov esp,ebp  ;restore stack 
    pop ebp 

Когда я нажал немедленные значения вместо проталкивания через AX он прекрасно работает. Почему это?Printf отображает значение мусора

+0

Можете ли вы привести нам пример того, как выглядит ваш код, что не удается? ;) – Tommylee2k

+2

Почему вы нажимаете 16-битные регистры, когда используете '% d' (что соответствует' int')? – Michael

+3

На самом деле вам не следует вводить 16-битные регистры, независимо от того, какая строка формата (если вы не знаете, что вы делаете). Кроме того, вы правильно использовали 'add esp, 12', и предполагается, что вы нажали 12 байтов. TL; DR: используйте 'eax' всюду. – Jester

ответ

4

Как отметил Jester: не следует PUSH 16-разрядные значения в стек с 32-битным кодом, если вы не знаете, что делаете. AX - это 16-разрядный регистр (нижняя половина 32-разрядного регистра EAX). Когда вы делаете:

push ax 

16-бит помещается в стек, потому что AX является 16-разрядный регистр. Это предотвратит доступ printf к значению, потому что данные не были 32-битными. Если вы должны были сделать:

push 11 

Вы нашли бы, что это работает. Когда NASM генерирует 32-битный код, он предполагает, что при вводе в стек мгновенные значения равны 32 битам. Вот почему этот сценарий сработал для вас.

Если у вас есть PUSH 32-битный регистр, то полные 32-битные данные будут помещены в верхнюю часть стека. В качестве примера:

push eax 

Похоже, что ваша цель может быть, чтобы получить доступ или пересекать СЛОВА массива (WORD = 16-разрядных значений) и распечатать их с помощью спецификатора в %d преобразовании, используемом printf. %d отпечатки 32-разрядные DWORDS as подписан значения. Вам нужно будет загрузить их в память как WORD s и конвертировать их в DWORDS, прежде чем вы нажмете их на стек.

Язык ассемблера не имеет понятия переменных в общепринятом смысле языка программирования более высокого уровня. Вы придаете значение ячейке памяти, содержащей WORD (16-разрядное значение). Независимо от того, подписан он или нет, определяется кодом, который вы используете для взаимодействия с этими данными.

У 386 есть две инструкции, которые помогут вам. MOVSX Используется для знак удлиняет меньший операнд для большего операнда. Это используется, когда вы хотите сохранить SIGN (положительный или отрицательный). MOVZX используется для нулевой удлинитель меньший операнд для большего размера. Эта команда предназначена для значений без знака, а во время преобразования просто устанавливает все верхние биты операнда-адресата равным нулю.

В качестве примера этого я написал код, сосредоточенный вокруг массива слов:

section .data 
    array dw -1,0,1,2,3,4,5,6,7,8,9,10,-32768,32767,32768 
            ; array of integers 
    arraylen equ ($-array)/2   ; number of word elements in array 
    msg db " numbers are : %d %d ",10,0 

section .text 

global main 
extern printf   ; for c printf 

main: 

    push ebp 
    mov ebp,esp  ; intialise stack 
    push ebx   ; ebx is caller saved register. We destroy it so 
         ;  we must restore it before our function exits 

    xor ebx, ebx  ; index = 0 

    ; Make the equivalent of a for loop to traverse array 
.loop1: 
    cmp ebx, arraylen ; We'll process all the elements of the array 
    je .endloop  ; End when our index = arraylen 

    movzx eax, word [array + ebx * 2] ; Use EBX as index into WORD array 
         ; zero extend 16-bit array value into 32-bit register 
    push eax   ; parameter 3 = unsigned DWORD 
    movsx eax, word [array + ebx * 2] ; Use EBX as index into WORD array 
         ; sign extend 16-bit array value into 32-bit register 
    ; movsx eax, ax ; The line above would have also worked this way 
    push eax   ; parameter 2 = signed DWORD onto stack 

    push msg   ; parameter 1 = pointer to format string 
    call printf  ; calling printf function 
    add esp, 12 

    inc ebx   ; index += 1 
    jmp .loop1   ; continue for loop 

.endloop: 

    pop ebx   ; Restore ebx 
    mov esp,ebp  ; restore stack 
    pop ebp 

код гарантирует, что любой из регистров (EBX в коде выше), которые вызываемая сохраняемых в CDECL calling convention сохраняются и восстанавливаются в начале и конце функции.Более подробно об этом можно найти в недавнем StackOverflow answer, который я написал.

Я закодировал эквивалент цикла for (вы могли бы закодировать его как do-while) или любую другую конструкцию цикла для перемещения массива. Я использую как MOVZX, так и MOVSX и отображать результаты с помощью строки формата printf.

Примечание:: MOVZX также может быть сделано с помощью обнуления операнд назначения и перемещения исходного операнда в пункт назначения после. В качестве примера:

movzx eax, word [array + ebx * 2] 

Может быть закодированы как:

xor eax, eax ; eax = 0 
mov ax, word [array + ebx * 2] 

Нужно быть в состоянии собрать и связать с:

nasm -f elf32 testmov.asm 
gcc -m32 -o testmov testmov.o 

При запуске в качестве ./testmov результатов должен выглядеть так:

numbers are : -1 65535 
numbers are : 0 0 
numbers are : 1 1 
numbers are : 2 2 
numbers are : 3 3 
numbers are : 4 4 
numbers are : 5 5 
numbers are : 6 6 
numbers are : 7 7 
numbers are : 8 8 
numbers are : 9 9 
numbers are : 10 10 
numbers are : -32768 32768 
numbers are : 32767 32767 
numbers are : -32768 32768 

Если вы хотите печатать без знака WORD s (16-разрядные значения) с printf вы можете использовать %hu (беззнаковое короткое), и для знакового WORD ы вы можете использовать %hd (подписанный короткий). Хотя вам все равно нужно передать DWORD для параметров, вам не нужно беспокоиться о нулевом расширении (или расширении знака), так как printf будет смотреть только на нижние 2 байта DWORD s, которые вы передаете в качестве параметров.

0

Вы можете нажать 16-битный регистр на 32-разрядный стек, но printf позже выберет его как полный 32-разрядный элемент из стека в любом случае. Именно здесь вы получаете значение для мусора - более высокая часть элемента подбирается функцией printf. Это ваша проблема. Таким образом, чтобы предотвратить будущую проблему, всегда нажимайте 32-битный элемент на 32-разрядный процессор, 64-разрядный элемент на 64-разрядный процессор и так далее. Для немедленного, ваш компилятор по умолчанию будет 32-бит немедленным, поэтому ваша программа работает с немедленными.

Смежные вопросы