Emu8086 pusha popa

Now*_*orz 1 assembly emu8086

我在装配程序中寻找一种简单的pusha popa方式.我想使用emu8086来查找程序中的错误,所以不允许使用.286.我试过了:

push_a proc
push ax
push cx
push dx
push bx
push sp
push bp
push si
push di
push_a endp

pop_a proc
pop di
pop si
pop bp
pop sp
pop bx
pop dx
pop cx
pop ax
pop_a endp
Run Code Online (Sandbox Code Playgroud)

但它不起作用,因此当我们"调用push_a"时,我们将堆栈推送到我们现在的地址.还有另一种简单易行的方法吗?我不想每次都写8次推送和8次弹出.

Mar*_*oom 6

可以将过程实现为宏

pusha MACRO
   push ax
   push cx
   push dx
   push bx
   push sp
   push bp
   push si
   push di
pusha ENDM

popa MACRO
   pop di
   pop si
   pop bp
   pop sp
   pop bx
   pop dx
   pop cx
   pop ax
popa ENDM
Run Code Online (Sandbox Code Playgroud)

但这不是pusha/ 的架构行为,popa它是真正的8086上的破解代码.

真实的语义 pusha

push sppusha应推栈指针的状态,在开始的的pusha宏.

温度←(SP);
推(AX);
推(CX);
推(DX);
推(BX);
推(临时);
推(BP);
推(SI);
推(DI);

                                                                                             英特尔伪代码pusha- 英特尔手册2B

使用临时存储位置

如果你有一个临时存储位置,你可以做类似的事情:

pusha MACRO
   mov WORD PTR [pusha_scratch], sp
   push ax
   push cx
   push dx
   push bx
   push WORD PTR [pusha_scratch]
   push bp
   push si
   push di 
pusha ENDM
Run Code Online (Sandbox Code Playgroud)

确保在任何情况下都可以pusha_scratch访问DS它是一件非常努力的事情.
没有考虑单独的定义pusha_scratch.

使用位置无关的就地版本

或者,人们可以通过sp与位置无关并且就地操作的东西来解释修改后的值.
如果我正确地完成了我的数学运算 - 仔细检查它 - 这个代码应该可以解决问题

pusha MACRO
   push ax
   push cx
   push dx
   push bx

   push bp                  ;Top of stack = BP
   mov bp, sp               ;BP points to TOS
   lea bp, [bp+0ah]         ;BP is equal to the starting value of SP
   xchg bp, [bp-0ah]        ;BP = Original BP, [SP] = Starting value of BP

   push bp
   push si
   push di
pusha ENDM
Run Code Online (Sandbox Code Playgroud)

非常感谢Fifoernik通过注意反转数学并防止改变EFLAGS寄存器来纠正损坏的代码.

上面的代码片段的优点在于它避免了push sp完全使用,因为它在向后兼容性方面有点问题.

的问题 push sp

在真正的8086上,指令的push sp行为有所不同:

P6系列,奔腾,Intel486,Intel386和Intel 286处理器在堆栈上推送的PUSH SP指令值不同于8086处理器.
在作为推送操作的一部分递减之前,32位处理器会推送SP寄存器的值;
8086处理器在递减后推送SP寄存器的值.

                                                                                             英特尔兼容性部分 - 英特尔手册3 - 22.17

我不知道emu8086作为8086的模拟器有多准确,但我会避免push sp.

的语义 popa

特殊的方式pusha实现了对特殊实现的要求popa.
在一个真正的8086上pop,一旦pop sp执行了一个天真的s中断列表.

事实上,CPU利用了这样一个事实:sp即使在a之后你也无法真正修改pusha- 否则就无法获得这些值 - 并且注意到在所有pops 之后隐含地恢复了原始值.

DI←Pop();
SI←Pop();
BP←Pop();
增加ESP 2; (*跳过下一个2字节的堆栈*)
BX←Pop();
DX←Pop();
CX←Pop();
AX←Pop();

                                                                                             英特尔伪代码popa- 英特尔手册2B

所以更正确的实现popa

popa MACRO
   pop di
   pop si
   pop bp
   add sp, 02h         ;Beware, this affects EFLAGS
   pop bx
   pop dx
   pop cx
   pop ax
popa ENDM
Run Code Online (Sandbox Code Playgroud)

请注意,您必须避免pop sp在真实的8086中作为push sp后跟a pop sp不是幂等的

POP ESP在将旧堆栈顶部的数据写入目标之前,该指令递增堆栈指针(ESP).

                                                                                             英特尔说明pop- 英特尔手册2B

最后,人们会期望pusha/ popa在中断方面是原子的,因此在宏体周围需要cli/ sti对.


如果您使用pusha/ popa仅作为保存所有寄存器的简写,那么可以跳过所有麻烦并且足够的实现是

 push_all MACRO
   push ax
   push cx
   push dx
   push bx
   ;NOTE: Missing SP
   push bp
   push si
   push di
push_all ENDM

pop_all MACRO
   pop di
   pop si
   pop bp
   ;NOTE: Missing SP
   pop bx
   pop dx
   pop cx
   pop ax
pop_all ENDM
Run Code Online (Sandbox Code Playgroud)