我正在开发一个操作系统,同时还在学习更多的汇编语言。我学习了推入和弹出,正如我所理解的,弹出取栈中任何其他值的顶部,所以它得到最后一个推入的值。但我想知道,我们真的能直接弹出栈底的值吗(即第一个推入的值)?如:
push 'A' push 'B' push 'C' pop bx ; Gets C
我想用A而不是C作为例子。(我在16位模式下,汇编程序是NASM,也在标记中指定。)
mf98qq941#
试着写下堆栈在内存中的布局。你会发现,“弹出”堆栈中间的任何元素都需要在内存中移动大量的堆栈元素。处理器不支持这一点。然而,你可以在任何时候将堆栈上的任何元素加载到寄存器中,而不弹出它(也就是说,它以后仍然在堆栈上)。为此,首先将堆栈指针复制到某个可用于寻址内存的寄存器(bp是标准选择),然后从该寄存器访问堆栈。
bp
mov bp, sp ; make the stack accessible mov bx, [bp+4] ; load stack element A into bx
通常,bp是为当前函数建立堆栈帧的一部分,因此您可以随时使用它来访问堆栈。您也可以使用寄存器bx、si和di,但请记住,在这种情况下,需要使用ss段覆盖(对于bp,它是隐式的)。现在,我们为什么要加4才能得到项A呢?回想一下,堆栈向内存中较低的地址增长(即,随着每个push,sp减小),并且在16位模式下,每个堆栈槽占用2个字节的内存。因此,假设在推送序列之后为sp = 1000h,我们有以下内存布局:
bx
si
di
ss
push
sp
sp = 1000h
1004h A 1002h B 1000h C <-- SP
因此,通过简单的运算,SP + 4指向堆栈槽A,SP + 2指向堆栈槽B。
SP + 4
SP + 2
unftdfkk2#
在16位模式下,没有sp相对寻址模式,但是你可以将sp中的值复制到bp中,然后使用bp相对寻址来访问A的位置,当然这不会弹出A,但是你可以读或写A。这就是为什么x86函数在其序言(函数启动代码)中经常包含mov bp,sp的原因之一。一旦bp被设置为引用其堆栈,函数就可以使用bp相对寻址(bp +位移)来访问其调用者推送的参数。
mov bp,sp
2条答案
按热度按时间mf98qq941#
试着写下堆栈在内存中的布局。你会发现,“弹出”堆栈中间的任何元素都需要在内存中移动大量的堆栈元素。处理器不支持这一点。然而,你可以在任何时候将堆栈上的任何元素加载到寄存器中,而不弹出它(也就是说,它以后仍然在堆栈上)。
为此,首先将堆栈指针复制到某个可用于寻址内存的寄存器(
bp
是标准选择),然后从该寄存器访问堆栈。通常,
bp
是为当前函数建立堆栈帧的一部分,因此您可以随时使用它来访问堆栈。您也可以使用寄存器bx
、si
和di
,但请记住,在这种情况下,需要使用ss
段覆盖(对于bp
,它是隐式的)。现在,我们为什么要加4才能得到项A呢?回想一下,堆栈向内存中较低的地址增长(即,随着每个
push
,sp
减小),并且在16位模式下,每个堆栈槽占用2个字节的内存。因此,假设在推送序列之后为sp = 1000h
,我们有以下内存布局:因此,通过简单的运算,
SP + 4
指向堆栈槽A,SP + 2
指向堆栈槽B。unftdfkk2#
在16位模式下,没有
sp
相对寻址模式,但是你可以将sp
中的值复制到bp
中,然后使用bp
相对寻址来访问A的位置,当然这不会弹出A,但是你可以读或写A。这就是为什么x86函数在其序言(函数启动代码)中经常包含
mov bp,sp
的原因之一。一旦bp
被设置为引用其堆栈,函数就可以使用bp
相对寻址(bp
+位移)来访问其调用者推送的参数。