s0s*_*s0s 2 x86 assembly stack push call
简而言之,我正在研究Singh和Triebel的一本名为"8088和8086微处理器"的书,以学习那些特定CPU的旧组件.现在,我正在练习的计算机是我最近构建的主计算机,因此寄存器更大.
也就是说,这本书(我觉得非常有用)说调用标签操作数会导致调用后的指令地址被放在堆栈上,然后THEN SP递减2(ESP并在我的CPU上递减4) .在我正在研究的一些代码中,一个调用操作数后面紧跟一个push.当CPU遇到a时push,书中说明SP会减2(再次,ESP在我的CPU上减4).
; ESP=0xffffd840 right now
call iprint
mov eax, 0Ah
iprint:
push eax ; say eax contains 1
Run Code Online (Sandbox Code Playgroud)
现在,ESP=0xffffd840在通话前说.地址EIP保存在堆栈中(CALL操作数后面的指令地址).然后ESP减少4.此时,ESP=0xffffd83c.然后遇到推送操作数.按照本书所说的,首先递减堆栈指针,然后将寄存器的内容压入堆栈.所以现在ESP=0xffffd838,1被推入堆栈.
If it helps:
Stack addr Contents
********** ********
0xffffd840 address of mov eax, 0Ah
0xffffd83c ?
0xffffd838 1
Run Code Online (Sandbox Code Playgroud)
现在,我的问题是,0xffffd83c跳过了吗?根据这本书,ESP在保存下一条指令之后递减call,然后在数据从堆栈中放入之前push,它再次递减.
我一直在调试类似的情况一段时间,密切注意寄存器的值,但我无法判断调试器是否符合本书所说的内容(在执行操作之前或之后递减).
这是因为在某些情况下,RET在子程序之后给出一个参数,导致堆栈指针递增?如果堆栈指针在放入数据之前确实递减了两次,这是我能看到的唯一原因.
如果我有这个错误,有人可以确认或解释一下吗?
谢谢
这call <address>就像:push eip jmp <address>,所以在你的情况下,如果esp是0xffffd840提前call,下一条指令的返回地址被推送到0xffffd83c(因为伪" push eip"将首先递减esp以创建新的堆栈顶部,然后它将存储当前值的eip那里(顺便说一下,eip已经指向下一条指令,因为fetch + decode指令阶段call已经完成,所以它实际上是需要的值ret).
您也可以在调试器中查看内存.而"堆叠"只是普通的记忆.因此,如果你有esp相同的0xffffd840,你可以打开例如内存视图0xffffd824,你将看到32字节的堆栈内存,28字节未使用,最后4字节是当前"堆栈顶部".
我在任何地方都使用4个字节的组,因为这是在32b保护模式下CPU"字"的本机大小(dword在x86术语中,word仅为16位).IIRC你仍然可以强制CPU执行push ax或使用sub/add esp,immediate甚至通过单字节移动它,但通常它涉及性能损失,并且在64b模式下几个调用约定甚至需要16字节对齐,所以我建议坚持+ -4 esp操作在32b模式下.
但是如果你的书大概是8086,你可能想用来dosbox模拟旧的DOS 16位环境,以便在开始时为你节省一些特定于平台的问题.虽然你可能应该为你的操作系统找到一些32/64位的最新书,因为x86上的32b保护模式更容易学习(只有图形输出不像DOS时代那样简单,但如果你愿意的话将你的asm文件与C++"loader"混合,例如将一些窗口表面初始化为ARGB内存数组,你可以将该指针传递给asm例程并用像素玩具,用同样简单的方法,如何旧的320x200"模式13h"在DOS中工作.更容易(没有调色板,没有64k段限制).
| 归档时间: |
|
| 查看次数: |
1056 次 |
| 最近记录: |