0

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?

  • It's completely wrong. You're reading from the wrong location in your first solution, and reading and writing the wrong locations in the second location. –  Sep 13 '16 at 22:01
  • Oh that hurts, alright thank you for fast comment! Let me try to correct it. –  Sep 13 '16 at 22:03
  • if ebx=1000, then the `mov eax,[ebx+8]` loads eax with 32b value from address 1008. Your `mov eax,[ebx]` would load eax with 32b value from address 1000. If I understand that wording of task correctly, then it's in some sense less to change, than you did in your first try. – Ped7g Sep 13 '16 at 22:12
  • 2
    Edited version still works on wrong memory spot. Imagine memory as street with small houses, each has house number from 0 to N, that's address. And inside the house lives some number. With that ebx=1000 example you are by `[ebx+8]` taking out number from house 1008. With `[ebx]` you take out number from completely different house, at address 1000. – Ped7g Sep 13 '16 at 22:14
  • And once you get familiar with that, bend that mental image a bit more, because by `mov eax,[ebx+8]` you are actually taking out four 8 bit numbers from houses 1008, 1009, 1010 and 1011, concatenating them together in a way that number from house 1011 is the highest significant byte (bits 31..24) and house 1008 provided the least significant byte (bits 7..0). – Ped7g Sep 13 '16 at 22:17
  • 1
    I better go sleep now it's very late and I shouldn't have asked at a time like that because it's just more confusing. I will try to solve and understand this tomorrow it will be better I believe. Thank you a lot by the way, you helped me very much today!! :) –  Sep 13 '16 at 22:20

1 Answers1

2

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)

  • Mod=01 (base+disp8)
  • R/M = 011 (EBX)
  • REG = 000 (EAX as the register destination)

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 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.

Community
  • 1
  • 1
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847