我想知道是否有可能(如果是这样,如何)编写一系列与push
具有相同效果的指令。例如,如果ax
的内容为1200,而我执行push ax
,我可以使用哪些其他说明来完成push ax
的操作?
答案 0 :(得分:7)
其他一些答案使用[sp]
进行堆栈寻址,但在16位模式下也不可能,在32位或64位模式下也是如此。但是,在32位模式下,您可以使用[esp]
,在x86-64中,您可以使用[rsp]
进行内存寻址,但在16位模式下,没有使用sp
的内存寻址。有关16位模式下可能的存储器寻址模式,请参见here。
那么,您需要做什么:将bp
的值存储在某处,将sp
复制到bp
,然后使用bp
来寻址堆栈,最后还原bp
的原始值。
如果您有一个存放bp
的地方,这很容易(这是YASM / NASM语法):
mov [bp_storage], bp
sub sp,2
mov bp,sp
mov [bp],ax
mov bp,[bp_storage]
...
bp_storage dw 0
使用寄存器代替bp_storage
这样的内存地址也很简单。
编辑:添加了不修改标记的版本(如下所示),因为push
也不会修改标记。
上面的代码修改了标志,而push ax
不修改任何标志。这可以通过将第一个ah
存储到内存中,然后使用ah
将标志加载到lahf
中,然后将标记从ah
存储到内存中,然后如上所述修改堆栈来解决,然后使用ah
通过sahf
从内存中恢复标记,最后从内存中恢复ah
。
修改:要在不更改标记的情况下模拟push ax
,ah
必须在lahf
之前保存并在mov [bp],ax
之前加载。固定的。
mov [ah_storage],ah
lahf
mov [flags_storage],ah
mov [bp_storage],bp
sub sp,2
mov bp,sp
mov ah,[ah_storage]
mov [bp],ax
mov bp,[bp_storage]
mov ah,[flags_storage]
sahf
mov ah,[ah_storage]
...
bp_storage dw 0
ah_storage db 0
flags_storage db 0
sub
修改AF
,CF
,OF
,PF
,SF
,ZF
,而lahf
}加载和sahf
仅存储AF
,CF
,PF
,SF
,ZF
(无OF
)。但是,sp
永远不会在正常的堆栈使用中溢出。
但是,如果您无法访问内存,并希望使用堆栈来存储bp
,那么您可以这样做,但如果您既没有可用的免费寄存器,那么事情会变得复杂。但是,如果您使用的是实模式操作系统,则可以使用cli
阻止中断,交换bp
和sp
,使用bp
进行堆栈寻址,交换bp
再次sp
并允许sti
再次中断。
修改:sp
的值需要减去2才能模拟push ax
。固定。此版本不会修改标志(中断标志除外)。
cli
xchg bp,sp
lea bp,[bp-2]
mov [bp],ax
xchg bp,sp
sti
答案 1 :(得分:2)
至少如果内存服务,它大致相当于:
sub sp, 2
mov [sp], ax
答案 2 :(得分:1)
减去一个等于sp所需数据写入大小的值,然后在堆栈上移动/写入所需的对象。编译器总是这样做。查看-S输出示例。如果你这样做,请注意原子/线程问题...
答案 3 :(得分:1)
如果我没有忘记英特尔语法:
lea sp, [sp-2]
mov [sp], ax
我使用了lea
来避免触及FLAGS
(push
也不mov
或lea
触摸它们,但是sub
和{ {1}}做)。
编辑:事实证明,我忘记了更重要的事情:没有dec
寻址模式。正确的答案是@nrz的答案,我的可以应用于[sp]
和esp
(将eax
)在80386及以上。