Home > OS >  indirect adressing in assembly (x86)
indirect adressing in assembly (x86)

Time:11-16

This is the format for indirect adressing:

example: 

| Base |   Index * Scale |   Discplacement  |
|------|-----------------|------------------|
| EAX  |    EBX  *   2   |8-Bit Displacement|

But there are some things I don't understand yet:

  • If we have an address in EAX and add EBX*2 will this take the address/value located in EBX*2 and add it to the value/address in EAX?

  • What exactly is the Displacement, why is there 8 Bit,32 Bit or No Displacement possible and not let's say 4 Bit or 2 Bit displacement?

  • If we have values that are 4 Byte big each - in the EAX register we would be multiplying EAX*4 but why is this multiplication with byte values (I know each cell has 1 byte). Does this mean that it automatically transforms byte to bit?

CodePudding user response:

The full formula would be like "address = (base index << scale displacement) & mask".

Note 1: "scale" is a shift count and there's no multiplication. It's merely described as multiplication for human convenience.

Note 2: "mask" is there to represent wrapping/truncation to the destination size. E.g. if base index << scale displacement works out to 0x0000000212345678 but the CPU needs a 32-bit address then mask would be 0x00000000FFFFFFFF and the actual result would be 0x12345678.

If we have an address in EAX and add EBX*2 will this take the address/value located in EBX*2 and add it to the value/address in EAX?

Yes. E.g. if EAX = 3 and EBX = 10, then EAX EBX*2 would be 23.

What exactly is the Displacement, why is there 8 Bit,32 Bit or No Displacement possible and not let's say 4 Bit or 2 Bit displacement?

Displacement is just a number added to the rest. For memory accesses; it's often used for accessing structure fields (e.g. "address of structure offset to nth field in the structure") and stack locations (e.g. "stack top - offset from stack top").

A CPU mostly just reads a sequence of numbers; and then needs to be able to decode the numbers to figure out what the instruction is and what its operands are (and once the numbers are decoded it executes the corresponding instruction). More possibilities makes decoding harder and this needs to be justified by usefulness. 2-bit and 4-bit displacements can be trivially rounded up to 8-bit, making it hard to justify the extra complexity in decoding.

There are only 2 bits (the mode field in the ModRM byte) to signal register vs. memory with no disp, memory with 8-bit disp, or memory with 32-bit disp. Having more choices would have taken more bits away from something else; the disp is a simple binary value (that gets sign-extended), it doesn't itself use a variable-length encoding. (Fortunately, otherwise instruction-length decoding would have to look at it, not just the prefix opcode ModRM byte.)

If we have values that are 4 Byte big each - in the EAX register we would be multiplying EAX*4 but why is this multiplication with byte values (I know each cell has 1 byte). Does this mean that it automatically transforms byte to bit?

The "EAX * 4" is actually "EAX << 2". Small shift counts are very common (e.g. shl eax,cl) because "shift count too large" (e.g. larger than 32 on a 32-bit calculation) combined with the masking to the destination size makes it pointless. For an example, consider a fictitious "address = (EAX << 32) & 0xFFFFFFFF" (which is pointless because it could be replaced with "address = 0").

Essentially, for 32-bit address calculations, shift counts from 0 to 31 are potentially useful, and smaller shift counts (from 0 to 3) are the most useful. At this point we can go back to the "More possibilities makes decoding harder and this needs to be justified by usefulness" to understand why 80x86 only supports shift counts from 0 to 3 (or multipliers 1, 2, 4 and 8); even though other shift counts (e.g. "EAX << 4" or "EAX*16") might be useful occasionally.

2, 4, and 8 are common element sizes for arrays, so this 2-bit shift count makes it possible to index arrays of dwords for example using a register that holds an element-number instead of a byte offset.

  • Related