我正在制作一个有趣的操作系统,同时正在学习更多的Assembly。我学习了推送和弹出,正如我所理解的,弹出取堆栈上任何其他值之上的值,所以它得到最后一个推送的值。但我想知道,我们真的可以直接在堆栈底部弹出值吗(即第一个推送的值(?
类似:
push 'A'
push 'B'
push 'C'
pop bx ; Gets C
我想用A来代替C。(我处于16位模式,汇编程序是NASM,在标签中也有指定。(
试着在内存中写下堆栈的布局。您将看到,在堆栈中间"弹出"任何元素都需要在内存中打乱许多堆栈元素。处理器对此没有支持。但是,您可以在任何时候将堆栈上的任何元素加载到寄存器中,而无需弹出它(即,之后它仍将在堆栈上(。
为此,首先将堆栈指针复制到可用于寻址内存的某个寄存器中(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
在您的推送序列之后,我们有以下内存布局:
1004h A
1002h B
1000h C <-- SP
所以通过简单的算术SP + 4
指向堆栈时隙A,SP + 2
指向堆栈时隙B。
在16位模式中,没有sp
相对寻址模式,但您可以将sp
中的值复制到bp
中,并使用bp
相对寻址来访问A的位置。当然,这不会弹出A,但你可以读或写A。
这就是为什么x86函数的序言(函数启动代码(中经常有mov bp,sp
的原因之一。一旦bp
被设置为引用其堆栈,函数就可以访问其调用方使用bp
相对寻址(bp
+位移(推送的参数。