Вот как предполагается работать. Вызов в функцию помещает адрес возврата в стек. Поэтому, когда ваша функция введена, вершина стека будет адресом возврата, а не тем, что вы ранее выдавали.
В 32-битном коде вы теперь можете просто использовать указатель стека напрямую для доступа к ранее переданному значению (что-то вроде [esp+4]
или [esp+2]
в 16-битном режиме), но это невозможно при чисто 16-битной сборке только с 16-битными режимами адресации и ограниченным выбором регистров (не включая [sp]
).
Обычным способом является установка bp
в качестве указателя фрейма , с которого у вас есть произвольный доступ к фрейму стека, включая аргументы стека или любые локальные переменные, для которых вы резервируете место.
Write:
push bp ; Save previous value of bp so it won't get lost
mov bp, sp ; Set bp ("base pointer") to current stack pointer position
mov dx, [bp+4] ; Get argument from stack
mov ah, 02h
int 21h
mov sp, bp ; Restore stack pointer
pop bp ; Restore value of base pointer
ret 2 ; Indicate how many bytes should be popped from stack after return
Вместо pop dx
мы используем mov dx, [bp+4]
здесь. На этом этапе [bp]
будет предыдущим значением bp
(так как оно было в последний раз перед тем, как bp
было присвоено sp
), [bp+2]
будет адресом возврата, а [bp+4]
вашим первым аргументом.
(Помните, что стек растет вниз, поэтому вам нужно +4
, а не -4
здесь.)
Кроме того, когда вы возвращаетесь, вы должны быть уверены, что аргумент удаляется из стека. Вы можете либо разрешить вызывающей стороне очистку, либо использовать ret
с количеством байтов, которое нужно удалить в качестве аргумента. Это дополнительный sp += n
после получения обратного адреса. В вашем случае ret 2
будет реализовывать вызовы вызовов для этой функции.