Last modified: Thu Jun 18 18:27:08 UTC+0200 2026 © A. Tarpai
386 LEA
Load effective address into register: lea reg, r/m (source operand r/m should be memory location).
- operand-size determines destination register size (eg. AX/EAX)
- address-size determines addressing mode used and calculated offset size (16/32-bits)
operand-size = 16 operand-size = 32
D=0 or D=1 and 66h D=1 or D=0 and 66h
Destination register Destination register
is 16-bit is 32-bit
|--------------------------------------|--------------------------------------|
| | |
address-size = 16 | +---------+ | +---------+ |
D=0 or D=1 and 67h | | EA | | | EA | |
| +---------+ | +---------+ |
| | | | |
| v | v |
| +---------+---------+ | +---------+---------+ |
| | . . . . EA | REG | | 0 0 0 0 EA | REG |
| +---------+---------+ | +---------+---------+ |
| LEA r16, m16 | LEA r32, m16 |
| | |
| REG-HI unchanged (.) | EA-HI zero-padded |
| 16-bit 8086 emulation | REG-HI zeroed (0) |
|--------------------------------------|--------------------------------------|
| | |
address-size = 32 | +-------------------+ | +-------------------+ |
D=1 or D=0 and 67h | | EA | | | EA | |
| +-------------------+ | +-------------------+ |
| | | | |
| v | v |
| +---------+---------+ | +-------------------+ |
| | . . . . EA | REG | | EA | REG |
| +---------+---------+ | +-------------------+ |
| LEA r16, m32 | LEA r32, m32 |
| | |
| EA-HI lost | |
| REG-HI unchanged (.) | |
|--------------------------------------|--------------------------------------|
How the assembler works:
- when we write a 16-bit addressing mode, eg. [BX+SI], address-size = 16; otherwise 32-bit addressing modes, eg. [EAX], address-size = 32
- based on destination register operand-size = 16 or 32
- based on [BITS 16/32] the assembler inserts 66/67h prefixes:
NASM
[BITS 32] [BITS 16]
8D00 lea eax, [eax] 8D00 LEA AX, [BX+SI]
66 8D00 lea ax, [eax] 66 8D00 LEA EAX, [BX+SI]
67 8D00 lea eax, [BX+SI] 67 8D00 LEA AX, [EAX]
6667 8D00 lea ax, [BX+SI] 6667 8D00 LEA EAX, [EAX]
Conformance tests for LEA
Test with a zero modr/m byte addressing mode both in 16- and 32-bit addressing modes (easy to compare). That is MOD=00 zero disp, REG=(E)AX (000). For R/M:
- 16-bit: [BX+SI]
- 32-bit: [eax]
LEA opcode: 8D
Test run-time with these values:
FF00
+ 8000
___________
1 7F00 <-- 16-bit overflow
For 16-bit addressing modes this should result in 7F00 (64K wrap-around emulation).
VC++ (D=1 32-bit) test for REG HI zeroed:
or eax, -1
mov ebx, 0xFF00
mov esi, 0x8000
// lea eax, [BX+SI]
_emit 0x67
_emit 0x8D
_emit 0x00
eax = 0000_7F00 <-- REG HI zeroed
VC++ (D=1 32-bit) test for 16-bit 8086 emulation and REG HI unchanged:
or eax, -1
mov ebx, 0xFF00
mov esi, 0x8000
// lea ax, [BX+SI]
_emit 0x66
_emit 0x67
_emit 0x8D
_emit 0x00
eax = FFFF_7F00 <-- REG HI unchanged
VC++ (D=1 32-bit) test for EA HI lost (and REG HI unchanged):
or eax, -1
mov ecx, 0xaaaaaaaa
// lea ax, [ecx]
_emit 0x66
_emit 0x8D
_emit 0x01
eax = FFFF_AAAA <-- REG HI unchanged and EA HI lost