长话短说,我正在学习 Singh 和 Triebel 写的一本名为“8088 和 8086 微处理器”的书,以学习old那些特定CPU的汇编。现在,我练习的计算机是我最近组装的主计算机,因此寄存器更大。
也就是说,这本书(我发现这非常有帮助)说调用标签操作数导致调用后的指令地址被放置在堆栈上,然后SP
减 2 (ESP
,并在我的 CPU 上减 4)。在我正在研究的一些代码中,调用操作数后面紧跟着一个push
。当CPU遇到一个push
,书中指出SP
减二(同样,ESP
在我的 CPU 上减 4)。
; ESP=0xffffd840 right now
call iprint
mov eax, 0Ah
iprint:
push eax ; say eax contains 1
现在,说ESP=0xffffd840
在通话之前。地址为EIP
保存在堆栈上(紧随其后的指令的地址CALL
操作数)。然后ESP
减4。此时,ESP=0xffffd83c
。然后遇到push操作数。按照书上的说法,首先将堆栈指针递减,然后将寄存器的内容压入堆栈。所以现在ESP=0xffffd838
并且 1 被压入堆栈。
If it helps:
Stack addr Contents
********** ********
0xffffd840 address of mov eax, 0Ah
0xffffd83c ?
0xffffd838 1
现在,我的问题是0xffffd83c
跳过?据书中所述,ESP
保存后的下一条指令后递减call
,然后在将数据放入堆栈之前push
,再次减少。
我已经调试类似的场景有一段时间了,密切关注寄存器的值,但我无法判断调试器是否遵循书中所说的内容(在执行操作之前或之后递减)。
这是因为在某些情况下参数是在后面给出的RET
在子程序中,导致堆栈指针递增?如果堆栈指针在放入数据之前确实递减两次,那么这是我能看到的唯一原因。
如果我有这个错误,有人可以确认或解释一下吗?
谢谢
The call <address>
就好像:push eip
jmp <address>
,所以在你的情况下如果esp
is 0xffffd840
领先于call
,下一条指令的返回地址被压入0xffffd83c
(因为伪“push eip
" 将首先递减esp
创建新的栈顶,然后它将存储当前值eip
那里(之间,eip
已经指向下一条指令,作为获取+解码指令阶段call
已完成,因此它实际上是所需的值ret
).
您还可以在调试器中查看内存。而“栈”只是普通的内存。所以如果你有esp
等于0xffffd840
,您可以打开例如内存视图0xffffd824
,您将看到 32 字节的堆栈内存,其中 28 字节尚未使用,最后 4 字节是当前的“堆栈顶部”。
我到处都使用 4 个字节组,因为这是 CPU“字”的本机大小(dword
在 x86 术语中,word
仅 16 位)在 32b 保护模式下。 IIRC你仍然可以强制CPU做push ax
or use sub/add esp,immediate
甚至可以将其移动单个字节,但通常会涉及性能损失,并且在 64b 模式下,多个调用约定甚至需要 16 字节对齐,因此我建议坚持使用 +-4esp
32b 模式下的操作。
但如果你的书是关于8086的,你可能想使用dosbox
模拟旧的 DOS 16 位环境,以避免一开始出现一些特定于平台的问题。虽然也许你应该为你的操作系统找到一些最近的 32/64 位书籍,因为 x86 上的 32b 保护模式更容易学习(只是图形输出不像 DOS 时代那么简单,但如果你愿意的话)将你的asm文件与C++“加载器”混合,例如将一些窗口表面初始化为ARGB内存数组,你可以将该指针向下传递给asm例程并玩弄像素,以同样简单的方式,旧的320x200“ DOS 中的模式 13h" 有效。甚至更简单(没有调色板,也没有 64k 段限制)。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)