pushl
Y86指令将堆栈指针递减4并将寄存器值写入存储器。因此,当执行指令pushl %esp
时,处理器应该做什么并不清楚,因为正在按下的寄存器被同一指令改变。可能发生两种可能的事件:
(1)推送%esp
的原始值,或(2)按下%esp
的递减值。
鉴于此,我们如何修改此代码相当于pushl REG
来解释和容纳这些歧义(REG可以是%esp以及任何其他寄存器)?:
subl $4,%esp Decrement stack pointer
movl REG,(%esp) Store REG on stack
类似地,指令popl %esp
可以将%esp
设置为从内存读取的值或增加的堆栈指针。如何更改此代码以适应这些歧义?:
movl (%esp),REG Read REG from stack
addl $4,%esp Increment stack pointer
答案 0 :(得分:0)
y86基于x86。 x86 instruction-set reference manual entry for push
说(正确):
PUSH ESP指令按下执行指令之前存在的ESP寄存器的值。
POP ESP指令在堆栈旧堆栈的数据写入目标之前递增堆栈指针(ESP)。
所以在pop %esp
的情况下,增量会丢失。此序列具有相同的效果,但大多数实际CPU可能会加载到临时内部存储器中,而不是在寻址模式下实际使用更新的ESP值。
add $4, %esp
movl -4(%esp), %esp
但pop %esp
在没有更新FLAGS的情况下做到了这一点,并且没有可能在add和mov之间产生中断或信号处理程序。 (单独的add / mov序列在上下文中不安全,其中当前%esp
以下的任何内容都可以被中断处理程序异步覆盖。)
据推测,y86与x86完全相同。 您可以轻松(并且应该)使用调试器查看您最喜欢的y86模拟器如何处理此角落案例。通过查看内存(或在其后面添加push %esp
)可以轻松测试pop %eax
。
同时测试两者会让人感到困惑,如果弹出的值与旧的堆栈指针相同,则无法区分。可能会推送0
(或将0
存储到(%esp)
),然后pop %esp
,并使用调试器查看寄存器中的值。 (如果您的代码在之后崩溃并不重要,那么您只需使用调试器。)
我没有检查y86是否支持x push $0
或movl $0, (%esp)
。我想如果支持它会立即immovl $0, (%esp)
(直接记忆)。如果没有,那么将寄存器归零并推送它。