旧考试中的任务(如果你不相信我,我可以提供源代码):
在不使用寄存器索引寻址的情况下更改以下汇编代码。不要使用超过4
的指令。
mov eax, [ebx+8]
add eax, 1
add ebx, 8
mov [ebx], eax
我把它改成:
mov eax, [ebx]
add eax, 9
add ebx, 8
mov [ebx], eax
我无法想象这是这么容易做到的,所以我不确定:D
如果我这样做是对的,另一种选择是:
mov eax, [ebx]
add eax, 1
add ebx, 16
mov [ebx], eax
还是现在完全错了?非常感谢大家!
编辑:更正版本:
mov eax, [ebx]
add [ebx], 9
add ebx, 8
mov [ebx], eax
吗?
首先,原始的寻址模式都不使用索引寄存器。[ebx+8]
可以(并且将)使用EBX作为基寄存器,而不是索引寄存器,因为它可以在机器码编码中不使用SIB字节。所以寻址模式是base+ dis8,所以ModRM字节的编码将是(二进制)
- Mod = 1(基础+ disp8)
- r/m = 011 (ebx)
- REG = 000 (EAX为目的寄存器)
因此ModRM字节将是0x43,如表2-2所示。带有ModR/M字节的32位寻址表单,参见Intel的指令集参考手册(卷2)(参见x86标签wiki以获取pdf的链接)。
如果EBX上有缩放因子(如[ebx*2 + 8]
),则必须使用dis32 +索引寻址模式。(参见x86寻址模式)。
大概你的意思是你不能在寻址模式中使用位移。
在这种情况下,第一个指令不能被加载,因为你必须先计算寄存器中的地址。通过稍后使用ADD指令计算作为地址所需的相同的ebx+8
值,这个问题对您来说很容易。因此,您只需重新排序,而不必修改EBX两次。
add ebx, 8
mov eax, [ebx]
add eax, 1
mov [ebx], eax
或者更慢但更少的指令:
add ebx, 8
add dword [ebx], 1 ; read-modify-write
mov eax, [ebx] ; and then reload
x86有许多奇怪而奇妙的指令,包括XADD。你甚至可以这样做:
; LEA ebx, [ebx+8] might count as breaking the rules, even though it's not a load.
sub ebx, -8 ; use a different instruction just for variety.
mov eax, 1
xadd dword [ebx], eax ; read-modify-write, leaving the old value in eax
inc eax ; do the same add again, so the register matches what xadd put into memory.
但是不要这样做。XADD比普通的简单指令慢。它的主要目的是多线程同步。c++ 11的std::atomic::fetch_add与XADD实现的操作相同,因此fetch_add()可以在x86上有效地编译为lock xadd
。