Task from an old exam (I can provide source if you don't believe me):
Change the following assembler code without using register-indexed addressing. Do not use more than 4
instructions.
mov eax, [ebx+8]
add eax, 1
add ebx, 8
mov [ebx], eax
I have changed that to:
mov eax, [ebx]
add eax, 9
add ebx, 8
mov [ebx], eax
I cannot imagine it's done that easily that's why I'm not sure :D
if that's right what I did, an alternative would be:
mov eax, [ebx]
add eax, 1
add ebx, 16
mov [ebx], eax
Or is it now completely wrong? Thank you very much everyone!
Edit: Corrected version:
mov eax, [ebx]
add [ebx], 9
add ebx, 8
mov [ebx], eax
Now?
First of all, none of the addressing modes in the original use an index register. [ebx+8]
can (and will) use EBX as a base register, not an index register, because it can do that without a SIB byte in the machine-code encoding. So the addressing mode is base+disp8, so the encoding of the ModRM byte would be (in binary)
So the ModRM byte would be 0x43, as per Table 2-2. 32-Bit Addressing Forms with the ModR/M Byte, in Intel's instruction-set reference manual (Volume 2). (See the x86 tag wiki for links to the PDFs).
If there was a scale factor on EBX (like [ebx*2 + 8]
), it would have to use the disp32 + index addressing mode. (See also x86 addressing modes).
Presumably you actually mean you can't use a displacement in your addressing mode.
In that case, the first instruction can't be the load, because you have to calculate the address in a register first. The question makes it easy for you, by later calculating the same ebx+8
value that you need as an address, using an ADD instruction. So you can just reorder, instead of having to modify EBX twice.
add ebx, 8
mov eax, [ebx]
add eax, 1
mov [ebx], eax
Or slower but fewer instructions:
add ebx, 8
add dword [ebx], 1 ; read-modify-write
mov eax, [ebx] ; and then reload
x86 has many weird and wonderful instructions, including XADD. You could even do this:
; 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.
But don't do this. XADD is slower than normal simple instructions. It's main purpose is multi-threaded synchronization. C++11's std::atomic::fetch_add is the same operation that XADD implements, so fetch_add() can compile efficiently on x86 to a lock xadd
.