【发布时间】:2013-01-30 15:06:35
【问题描述】:
我想知道是否可以(如果可以,如何)编写与push 具有相同效果的指令序列。比如ax的内容是1200,我做了一个push ax,我还能用什么其他指令来完成push ax做的事情?
【问题讨论】:
我想知道是否可以(如果可以,如何)编写与push 具有相同效果的指令序列。比如ax的内容是1200,我做了一个push ax,我还能用什么其他指令来完成push ax做的事情?
【问题讨论】:
其他一些答案使用[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 存储到内存中来解决,然后使用lahf 将标志加载到ah,然后将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 仅存储CF,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
【讨论】:
至少如果有记忆的话,大致相当于:
sub sp, 2
mov [sp], ax
【讨论】:
ax是一个字,所以dec sp两次
mov [sp], ax 无效。 x86 中没有这样的指令。 esp 和 rsp 可用于内存寻址,sp 不能。看我的回答。
从 sp 中减去一个等于所需数据写入大小的值,然后在堆栈上移动/写入所需的对象。编译器一直在做这种事情。查看 -S 输出示例。如果您这样做,请注意原子/线程问题...
【讨论】:
如果我没有忘记 Intel 语法:
lea sp, [sp-2]
mov [sp], ax
我使用lea 来避免触摸FLAGS(push 或mov 或lea 都不会触摸它们,但sub 和dec 会触摸它们)。
编辑: 原来我忘记了更重要的事情:没有[sp] 寻址模式。正确答案是@nrz 的答案,我的可以应用于 80386 及以上的esp 和eax(将是lea esp,[esp-4])。
【讨论】:
mov [sp], ax 无效。 x86 中没有这样的指令。 esp 和 rsp 可以用于内存寻址,sp 不能。看我的回答。