我有一个任务,我告诉我写一个程序,并且有3个阵列'使用堆栈给我的地址,但是他们没有告诉我它们是如何放入堆栈的。有没有办法让我知道如何在程序中检索这些地址?
答案 0 :(得分:2)
您需要知道的第一件事是传递参数的 order 。没有它,您将能够获得三个值,但您不会知道如何解释它们。但是,让我们继续......
因为8086是16位微处理器,所以指针的长度为16位。在汇编语言术语中,这将是WORD大小,或2个字节。
通常,当调用者想要将参数传递给函数时,它会在PUSH
函数之前CALL
将它们放到堆栈中。所以,这里有一个例子,说明如何将三个WORD大小的值压入堆栈,然后调用foo
函数:
push 3
push 2
push 1
call foo
add sp, 6 ; clean up stack after call, adding 6 because we pushed 3 2-byte values
; (could also do POP+POP+POP, but that trashes a register and is slower)
非常简单,对吧?请注意,我显然已按相反的顺序推送参数。这样,当它们在foo
函数中被检索时,它们看起来将是正确的顺序(1,2,3)。当使用汇编语言调用函数时,这是传统的,这是C编译器在生成调用函数的代码时将始终执行的操作,因此您可以假设(如果没有另行说明)这就是你的参数传递方式。
现在,关于你的问题 - foo
函数如何从堆栈中检索这些参数?
首先,这里是foo
函数的启动方式(这称为 prologue 代码,它基本上用于需要与堆栈交互的每个函数的顶部):
foo:
push bp ; save original value of BP
mov bp, sp ; copy current value of SP (stack pointer) into BP (base pointer)
...
要检索第一个参数(并且,比如在AX
中加载它),您将执行以下操作:
mov ax, WORD PTR [bp+4]
为什么bp+4
?好吧,在序言代码之后,bp
包含指向堆栈顶部的指针。什么在堆栈上?请考虑以下图表:
Low |====================|
addresses | Unused space |
| |
|====================| ← SP points here
↑ | Function's |
↑ | local variables |
↑ | | ↑ BP - x
direction |--------------------| ← BP points here
of stack | Original/saved BP | ↓ BP + x
growth |--------------------|
↑ | Return pointer |
↑ |--------------------|
↑ | Function's |
| parameters |
| |
|====================|
| Parent |
| function's data |
|====================|
| Grandparent |
High | function's data |
addresses |====================|
从该图中,您可以看到:
BP+0
==您在BP
函数的序言中保存的foo
的原始值,将其推送到堆栈的顶部(push bp
)BP+2
==返回指针(由CALL
隐式推送,RET
结束时foo
将隐式使用该指针BP+4
==第一个由调用者在堆栈中传递给foo
函数的参数BP+6
==第二个参数同样,因为你在16位8086上,所有这些值都是WORD大小的,所以它们每个都是2个字节。
如果已在堆栈上分配空间以在{{1}内部存储一些局部变量,则将使用“函数的局部变量”部分(负偏离bp
)功能。但是为了简单起见,我们会在这个答案中忽略它。
所以,让我们把它们放在一起,看看foo
的可能实现:
foo
请注意在执行“有趣”操作后出现的结尾代码 - 特别是foo:
push bp ; save original value of BP
mov bp, sp ; set base pointer to top of stack
mov ax, WORD PTR [bp+4] ; get first parameter
mov bx, WORD PTR [bp+6] ; get second parameter
mov cx, WORD PTR [bp+8] ; get third parameter
; do something interesting
leave
ret
指令。这颠倒了我们之前看到的序言代码。 LEAVE
相当于:
LEAVE
但字节更少,因此它通常用作8086上的优化(不是在较新的处理器上,因为它比至少386及更高版本的扩展形式慢)。
这就是你从堆栈中访问参数的方法!
还有一件事。您说这些参数是指针(地址),因此您需要确保了解如何在汇编语言中取消引用指针。当我们之前加载寄存器时,我们加载的是指针。如果你想获得第一个数组的第一个元素,你可以这样做:
mov sp, bp
pop bp
请注意,在16位模式下,您非常restricted to certain addressing modes。您可以用来访问内存的唯一寄存器是mov bx, WORD PTR [bp+4] ; get first parameter (pointer to array)
mov ax, WORD PTR [bx] ; dereference pointer to array,
; putting value of first element in BX
,BP
,BX
和SI
。这就是为什么我们必须将堆栈指针放在DI
中(而不是直接从BP
使用它),为什么在最后一个例子中,我将指针加载到SP
所以我可以从BX
加载。