mirror of
https://github.com/libretro/x86-assembly-cheat.git
synced 2026-11-07 12:47:59 -10:00
103 lines
2.8 KiB
NASM
103 lines
2.8 KiB
NASM
; # Addressing
|
|
|
|
; # Indirect addressing
|
|
|
|
; # Effective address
|
|
|
|
; # Addressing modes
|
|
|
|
; This considers how memory addresses can be specified to read from or write to memory.
|
|
|
|
; x86 allows encoding address operations of the following form on a single instruction:
|
|
|
|
; s:[a + b*c + d]
|
|
|
|
; Where the instruction encoding allows for:
|
|
|
|
; - `a`: any 8 or 32-bit general purpose register
|
|
; - `b`: any 32-bit general purpose register except `ESP`
|
|
; - `c`: 1, 2, 4 or 8 (encoded in 2 SIB bits)
|
|
; - `d`: an immediate constant
|
|
; - `s`: a segment register. Cannot be tested simply from userland, so we won't talk about them here. See:
|
|
; https://github.com/cirosantilli/x86-bare-metal-examples/blob/6606a2647d44bc14e6fd695c0ea2b6b7a5f04ca3/segment_registers_real_mode.S
|
|
|
|
; Major application of this complicated schema: manipulation of an array of structs:
|
|
|
|
; - `a`: start of the array in memory.
|
|
; - `b`: index of the element. Register because it varies as we loop through the array.
|
|
; - `c`: size of each element. Known at compile time. TODO what gets compiled if >8 ?
|
|
; - `d`: field of the array to be accessed. Known at compile time, so an immediate.
|
|
|
|
; Several sets of a, b, c and d can be omitted. To really understand what is going on,
|
|
; you should learn how those instructions are encoded.
|
|
|
|
|
|
%include "lib/asm_io.inc"
|
|
|
|
ENTRY
|
|
; The simplest way to try them out is with `lea`.
|
|
|
|
; Full form (except no segment, DS used by default):
|
|
|
|
mov eax, 1
|
|
mov ebx, 3
|
|
lea eax, [eax + 2*ebx + 4]
|
|
ASSERT_EQ 11
|
|
|
|
; NASM is quite flexible about the ordering of operands:
|
|
|
|
mov eax, 1
|
|
mov ebx, 3
|
|
lea eax, [4 + eax + 2*ebx]
|
|
ASSERT_EQ 11
|
|
|
|
; but avoid that and use the `[a + b*c + d]` form proposed,
|
|
; as that is the simplest one to interpret as array of struct + field access.
|
|
|
|
; # Magic multipliers
|
|
|
|
; NASM can also do pure magic like:
|
|
|
|
mov eax, 1
|
|
lea eax, [3*eax]
|
|
ASSERT_EQ 3
|
|
|
|
; Which compiles to:
|
|
|
|
mov eax, 1
|
|
lea eax, [eax + 2*eax]
|
|
ASSERT_EQ 3
|
|
|
|
; since `b` must be a power of 2.
|
|
; This is documented at: http://www.nasm.us/doc/nasmdoc3.html#section-3.3
|
|
|
|
; This can lead us up to 9:
|
|
|
|
mov eax, 1
|
|
lea eax, [9*eax]
|
|
ASSERT_EQ 9
|
|
|
|
; Which compiles to [eax + eax*8].
|
|
|
|
; But the following are not possible:
|
|
|
|
;lea eax, [6*eax]
|
|
;lea eax, [7*eax]
|
|
;lea eax, [10*eax]
|
|
|
|
; TODO Possible to use 8-bit or 16-bit registers?
|
|
|
|
mov ax, 0
|
|
mov bx, 1
|
|
lea ax, [bx]
|
|
ASSERT_EQ ax, 1
|
|
|
|
; TODO why is this invalid while the above works?
|
|
;lea bx, [ax]
|
|
|
|
; Invalid.
|
|
;lea bl, [al]
|
|
;lea al, [bl]
|
|
|
|
EXIT
|