Как отметил 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, которые вы передаете в качестве параметров.
Можете ли вы привести нам пример того, как выглядит ваш код, что не удается? ;) – Tommylee2k
Почему вы нажимаете 16-битные регистры, когда используете '% d' (что соответствует' int')? – Michael
На самом деле вам не следует вводить 16-битные регистры, независимо от того, какая строка формата (если вы не знаете, что вы делаете). Кроме того, вы правильно использовали 'add esp, 12', и предполагается, что вы нажали 12 байтов. TL; DR: используйте 'eax' всюду. – Jester