调用例程后访问推入的args

问题描述 投票:0回答:1

我正在使用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
assembly x86 nasm x86-16 calling-convention
1个回答
3
投票

这就是应该的工作方式。调用函数会将返回地址压入堆栈。因此,当您输入函数时,堆栈的顶部将是返回地址,而不是您之前输入的内容。

在32位代码中,您现在可以直接使用堆栈指针直接访问先前推入的值(在16位模式下类似[esp+4][esp+2]之类的东西),但是对于纯16位汇编来说这是不可能的仅具有16位寻址模式且其寄存器选择有限(不包括[sp])。

通常的方法是将bp设置为frame pointer,您可以从中随机访问堆栈框架,包括堆栈args或您为其保留空间的任何本地var。

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 after。在您的情况下,ret 2将为此功能实现被调用者弹出窗口。

© www.soinside.com 2019 - 2024. All rights reserved.