调用例程后访问推送的参数



我正在学习使用FreeDOS和nasm的x86汇编。我有一个小测试程序,它所做的就是将A打印到屏幕上并退出。

如果我不使用Write例程,它就可以正常工作。

但似乎发生的是,当我把A推到堆栈上,然后调用Write时,它会把下一个IP放在堆栈上,当我在例程中弹出A时,我得到的IP不是我推的值。

我相信这很简单,但我不认为有什么问题。

segment data                    ; start of data segment
segment code                    ; start of code segment
..start:
label1:
top:
push 'A'
call Write
mov ah, 4ch
mov al, 0
int 21h
Write:
pop dx
mov ah, 02h
int 21h
ret
end:
mov ah, 4ch             ;exit
mov al, 0               ;exit code 0
int 21h                 ;call intr
segment stack class=stack       ; start of stack segment
resb 512

这就是应该的工作方式。对函数的调用将在堆栈上推送返回地址。因此,当您的函数被输入时,堆栈的顶部将是返回地址,而不是您之前推送的地址。

在32位代码中,您现在可以直接使用堆栈指针来访问以前推送的值(类似于16位模式中的[esp+4][esp+2](,但这对于只有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

此处使用mov dx, [bp+4]而不是pop dx。此时,[bp]将是上一个bp值(因为它是在bp被分配给sp之前最后一次推送的(,[bp+2]将是返回地址,[bp+4]将是第一个参数。

(记住堆栈向下增长,这就是为什么这里需要+4而不是-4。(

此外,当您返回时,您必须确保该参数已从堆栈中删除。您可以让调用者清理,也可以使用具有要删除的字节数的ret作为参数。这是在弹出返回地址之后的一个额外的sp += n。在您的情况下,ret 2将为该函数实现被调用者弹出。

相关内容

  • 没有找到相关文章

最新更新