Last modified: Fri Jul 3 21:20:10 UTC+0200 2026 © A. Tarpai
- MOV in 64-bit mode
- 2-operand MOV in 64-bit mode
- MOV immediate in 64-bit mode
- MOV immediate to register 64-bit mode operation W=1
- 8-bit MOV immediate to register in 64-bit mode
- 64-bit PUSH
- FAR in 64-bit mode
- JMP FAR memory indirect in 64-bit mode
- CALL FAR memory indirect in 64-bit mode
- RET FAR
- IRET operation in 64-bit mode
- Interrupt-stack in 64-bit mode
- NEAR in 64-bit mode
MOV in 64-bit mode
2-operand MOV in 64-bit mode
Opcodes based on the original 8086 MOV instructions
Move data between register and register/memory. In two directions (opcode d-bit), byte- or word movement (opcode w-bit). Short form moves data between accumulator and absolute 16-bit mem offset.
8086
opcode
88..8B
+---------+ +---------+ _________ _________ _ _ _ _ _
| REG | <---> | REG/MEM | |1000_10dw| |M_REG_R/M| DISP |
+---------+ +---------+
opcode
A0..A3
+---------+ +---------+ _________ _________ _________
| ACCU | <---> | MEM | |1010_00dw| | DISP || DISP |
+---------+ +---------+
Short-form means without MODRM byte (operand encoded in the opcode itself).
64-bit mode moves all operand sizes. The default operand size for move is 32-bit.
Move data between register and register/memory:
opcode
88..8B
_________ _________ _ _ _ _ _
|1000_10d0| |M_REG_R/M| DISP | mov byte
_________ _________ _ _ _ _ _
66h |1000_10d1| |M_REG_R/M| DISP | mov word
_________ _________ _ _ _ _ _
|1000_10d1| |M_REG_R/M| DISP | mov dword
_________ _________ _ _ _ _ _
48h |1000_10d1| |M_REG_R/M| DISP | mov qword
Short-form, move data between accumulator and absolute mem offset:
opcode
A0..A3
_________ ______ ______ ______ ______ ______ ______ ______ ______
|1010_00d0| | DISP || DISP || DISP || DISP || DISP || DISP || DISP || DISP | mov byte
_________ ______ ______ ______ ______ ______ ______ ______ ______
66h |1010_00d1| | DISP || DISP || DISP || DISP || DISP || DISP || DISP || DISP | mov word
_________ ______ ______ ______ ______ ______ ______ ______ ______
|1010_00d1| | DISP || DISP || DISP || DISP || DISP || DISP || DISP || DISP | mov dword
_________ ______ ______ ______ ______ ______ ______ ______ ______
48h |1010_00d1| | DISP || DISP || DISP || DISP || DISP || DISP || DISP || DISP | mov qword
The only instruction supporting a true 64-bit absolute offset value
(in all other cases offsets are maximum 32 bits, sign-extended to 64-bit).
Note the short-form honors addr-size prefix:
_________ ______ ______ ______ ______
67h |1010_00d0| | DISP || DISP || DISP || DISP | mov byte
_________ ______ ______ ______ ______
67h 66h |1010_00d1| | DISP || DISP || DISP || DISP | mov word
_________ ______ ______ ______ ______
67h |1010_00d1| | DISP || DISP || DISP || DISP | mov dword
_________ ______ ______ ______ ______
67h 48h |1010_00d1| | DISP || DISP || DISP || DISP | mov qword
The 8086 short-form addressing mode heritage extended to 8-byte [memory displacement]. Eg. the same opcode, A1, means different sized displacements:
8086 or 16-bit address-size: A1 06 00 mov ax, [6] 386 or 32-bit address-size: A1 06 00 00 00 mov eax, [6] 64-bit mode (NASM syntax): A1 06 00 00 00 00 00 00 00 mov eax, [qword 6]
Operation
Straightforward operation in 64-bit:
W=0 moves byte between 8-bit reg and reg/mem
W=1 moves default operand-size 32- or 64-bit with REX prefix. 16-bit move with 66h prefix. Register destination zeroes HI for 32-bit move:
W=1 Default opcode moves DWORD between reg and mem with REG HI zeroed: +----+----+----+----+----+----+----+----+ +----+----+----+----+ | 00 00 00 00 | R32 | <--- | M32/R32 | +----+----+----+----+----+----+----+----+ +----+----+----+----+ +----+----+----+----+----+----+----+----+ +----+----+----+----+ | .. .. .. .. | R32 | ---> | M32 | +----+----+----+----+----+----+----+----+ +----+----+----+----+ REX.W moves QWORD between reg and reg/mem: +----+----+----+----+----+----+----+----+ +----+----+----+----+----+----+----+----+ | R64 | <--> | M32/R32 | +----+----+----+----+----+----+----+----+ +----+----+----+----+----+----+----+----+
MOV immediate in 64-bit mode
Opcodes based on the original 8086 MOV instructions
Move immediate data to register/memory. Byte- or word movement (opcode w-bit). Short form moves immediate data to register.
opcode
C6..C7
+---------+ +---------+ _________ _________ _ _ _ _ _ _________ _________
| REG/MEM | <---- | IMMED | |1100_011w| |M_000_R/M| DISP | | DATA || DATA w=1|
+---------+ +---------+
opcode
B0..BF
+---------+ +---------+ _________ _________ _________
| REG | <---- | IMMED | |1011_wreg| | DATA || DATA w=1|
+---------+ +---------+
Short-form means without MODRM byte (operand encoded in the opcode itself).
64-bit mode
Move immediate data to register/memory:
opcode
C6..C7
_________ _________ _ __ __ _ _______
|1100_0110| |M_000_R/M| DISP | | DATA | mov byte
_________ _________ _ __ __ _ _______ _______
66h |1100_0111| |M_000_R/M| DISP | | DATA || DATA | mov word
_________ _________ _ __ __ _ _______ _______ _______ _______
|1100_0111| |M_000_R/M| DISP | | DATA || DATA || DATA || DATA | mov dword
_________ _________ _ __ __ _ _______ _______ _______ _______
48h |1100_0111| |M_000_R/M| DISP | | DATA || DATA || DATA ||s DATA | mov sign-extended dword!
Short-form, move immediate data to register:
opcode
B0..BF
_________ _______
|1011_0reg| | DATA | mov byte
_________ _______ _______
66h |1011_1reg| | DATA || DATA | mov word
_________ _______ _______ _______ _______
|1011_1reg| | DATA || DATA || DATA || DATA | mov dword
_________ _______ _______ _______ _______ _______ _______ _______ _______
48h |1011_1reg| | DATA || DATA || DATA || DATA || DATA || DATA || DATA || DATA | mov qword
The only instruction that supports a 64-bit immediate value
MOV immediate to register 64-bit mode operation W=1
The most notable difference is that R/M-form cannot encode 64-bit immediate value (just as all other instructrions with immediate operand).
I.e. with REX.W these two instructions operate differently:
64-bit move 32/64-bit immediate to register:
Both defaults to 32-bit immediate move with REG HI zero:
+----+----+----+----+----+----+----+----+ +----+----+----+----+
| 00000000000000000 R32 | <-- | I32 |
+----+----+----+----+----+----+----+----+ +----+----+----+----+
C7 C0 FE FF FF FF mov eax, -2 R/M form*
B8 FE FF FF FF mov eax, -2 Short form
* no assembler would generate this form for reg move - used for mem32 move
REX.W immediate move R/M form:
+----+----+----+----+----+----+----+----+ +----+----+----+----+
| sssssssssssssssss R64/M64 | <-- |s I32 |
+----+----+----+----+----+----+----+----+ +----+----+----+----+
48 C7 C0 FE FF FF FF mov rax, -2 Sign-extend (same as to 64-bit mem)
REX.W immediate move Short-form:
+----+----+----+----+----+----+----+----+ +----+----+----+----+----+----+----+----+
| R64 | <-- | I64 |
+----+----+----+----+----+----+----+----+ +----+----+----+----+----+----+----+----+
48 B8 89 67 45 23 01 EF CD AB mov rax, 0xABCDEF0123456789
The only instruction that supports a 64-bit immediate value
Clever assembler and/or programmer can then chose between:
- move 32-bit immediate with zero-extend to 64-bit register
- move 32-bit immediate with sign-extend to 64-bit register (or memory)
- move 64-bit immediate to 64-bit register
8-bit MOV immediate to register in 64-bit mode
Opcode bit w=0.
- C6 R/M form: move 8-bit immediate to 8-bit register or memory
- B0..B7 short-form: move 8-bit immediate to 8-bit register
64-bit move 8-bit immediate to register: Short form r/m form* 8-bit immed 8-bit immed to reg8 to reg8 B0..B7 i8 C6 MODRM i8 B0 01 C6 C0 01* mov al,1 B1 01 C6 C1 01* mov cl,1 B2 01 C6 C2 01* mov dl,1 B3 01 C6 C3 01* mov bl,1 B4 01 C6 C4 01* mov ah,1 B5 01 C6 C5 01* mov ch,1 B6 01 C6 C6 01* mov dh,1 B7 01 C6 C7 01* mov bh,1 REX REX 40 B0 01* 40 C6 C0 01* mov al,1 40 B1 01* 40 C6 C1 01* mov cl,1 40 B2 01* 40 C6 C2 01* mov dl,1 40 B3 01* 40 C6 C3 01* mov bl,1 40 B4 01 40 C6 C4 01* mov spl,1 40 B5 01 40 C6 C5 01* mov bpl,1 40 B6 01 40 C6 C6 01* mov sil,1 40 B7 01 40 C6 C7 01* mov dil,1 REX.B REX.B 41 B0 01 41 C6 C0 01* mov r8b,1 41 B1 01 41 C6 C1 01* mov r9b,1 41 B2 01 41 C6 C2 01* mov r10b,1 41 B3 01 41 C6 C3 01* mov r11b,1 41 B4 01 41 C6 C4 01* mov r12b,1 41 B5 01 41 C6 C5 01* mov r13b,1 41 B6 01 41 C6 C6 01* mov r14b,1 41 B7 01 41 C6 C7 01* mov r15b,1 * no assembler would encode this form for reg move - but all tested
Notes on MOV, XCHG and NOP in 64-bit mode
Both MOV and XCHG is-a move and should follow REG HI zeroed. But xchg eax, eax (90) is the NOP instruction since the 8086 and is not supposed to change CPU-state. So what happens in 64-bit mode? Testing:
48 83 CB FF or rbx, -1 rbx = ffffffff_ffffffff
8B DB mov ebx, ebx rbx = 00000000_ffffffff
48 83 CB FF or rbx, -1 rbx = ffffffff_ffffffff
87 DB xchg ebx, ebx rbx = 00000000_ffffffff <-- it is a mov!
Results are identical for rbx. Consider with eax:
48 83 C8 FF or rax, -1 rax = ffffffff_ffffffff
8B C0 mov eax, eax rax = 00000000_ffffffff
48 83 C8 FF or rax, -1 rax = ffffffff_ffffffff
90 xchg eax, eax rbx = ffffffff_ffffffff <-- "xchg eax, eax" is NOP since 8086
So NOP is-a-NOP in 64-bit mode too. Interesting.
Another use: these 2-byte opcodes can be used to zero REG-HI.
64-bit PUSH
All implicit stack movement is 64-bit, the stack is qword-stack:
63 0 | | +-----+-----+-----+-----+ | | +-----+-----+-----+-----+ | | <-- RSP +-----+-----+-----+-----+ | |
The stack pointer is 64-bit RSP and there is no B-bit and stuff.
In 64-bit mode, push is either a 64-bit qword move (default) or a 16-bit word move (66h) – decrementing RSP by 8 or 2.
For 16-bit push use:
- NASM syntax: push word
- ml64 syntax: pushw
There is no 32-bit push in 64-bit mode and REX.W 48h has no effect (every reg/mem push is already 64-bit move).
64-bit PUSH immediate
The old 186/286 opcodes: 68 (push operand-size) and 6A (push sign-extended byte).
In 64-bit mode:
- RSP = RSP − 8 then move sign-extended DWORD(!) or sign-extended BYTE as QWORD (default)
- RSP = RSP − 2 then move WORD or sign-extended BYTE as WORD (66h)
There is no 64-bit immediate push.
64-bit immediate qword push:
DWORD BYTE
+-----+-----+ +--+
68 |s | 6A |s |
+-----+-----+ +--+
| |
v v
+-----+-----+-----+-----+ +-----+-----+-----+--+--+
| sssssssss | | sssssssssssssssssss | <- RSP - 8
+-----+-----+-----+-----+ +-----+-----+-----+--+--+ QWORD move
64-bit immediate word push:
WORD BYTE
+-----+ +--+
66 68 | | 66 6A |s |
+-----+ +--+
| |
v v
+-----+-----+-----+-----+ +-----+-----+-----+--+--+
| . . . . . . . . | | | . . . . . . . . |sss | <- RSP - 2
+-----+-----+-----+-----+ +-----+-----+-----+--+--+ WORD move
Examples:
ml64 syntax ml64 disassembly
68 F4 01 00 00 push 500 push 1F4h
68 34 12 CD AB push 0ABCD1234h* push 0FFFFFFFFABCD1234h
6A FF push -1 push 0FFFFFFFFFFFFFFFFh
66 68 F4 01 pushw 500 push 1F4h
66 68 CD AB pushw 0ABCDh push 0ABCDh
66 6A FF pushw -1 push 0FFFFh
* NASM gives warning
PUSH reg short form
Default opcode means 64-bit register push. 66h can push 16-bit register part to RSP - 2. No sign-extension here.
50 push rax ; RSP = RSP - 8 66 50 push ax ; RSP = RSP - 2 REX.B for other registers: 41 53 push r11
PUSH r/m
Push [mem]: default opcode means 64-bit push from memory location, so RAX.W has no meaning. NB: 64-bit memory read (watch eg. APIC registers).
66h will push 16-bit word from memory to RSP - 2.
ml64 syntax: FF 35 45 00 00 00 push qword ptr [p (07FF6D304105Ch)] ; uses rip-rel 66 FF 35 01 00 00 00 push word ptr [p (07FF647B62319h)] ; rsp = rsp - 2 NASM syntax: FF 34 25 06 00 00 00 push qword [6] ; uses SIB Direct Memory 66 FF 34 25 06 00 00 00 push word [6]
Push register r/m form, hand-made code disassembly:
FF F0 push rax 66 FF F0 push ax
FAR in 64-bit mode
Analyzing direct control transfers only (far-CALL or a far-JMP instruction without using a call gate) and all CPL=0. This is mainly about how the cpu works and interprets code bytes.
Using traditional instructions, CPU can pull a far target address from memory to jump:
- from [ESP] for retf and iret
- from [EIP] for jmp/call far immediate*
- from [MEM] for jmp/call far memory indirect specified by addressing mode
* Far immediate is illegal in 64-bit mode.
JMP FAR memory indirect in 64-bit mode
CPU can load RIP from memory in all operand sizes (Intel only) with RIP HI zeroed. CS is always WORD.
64-bit mode JMP FAR memory indirect and how JMP FAR loads RIP:
DEFAULT
66h jmp far word [mem] jmp far dword [mem] REX.W jmp far qword [mem]
/ |
AMD! / | Intel only
/ |
/ v
/
READS 2 READS 3 v READS 5
MEMORY WORDS MEMORY WORDS MEMORY WORDS
+-----+-----+ +-----+-----+-----+ +-----+-----+-----+-----+-----+
| IP | CS | | EIP | CS | | RIP | CS |
+-----+-----+ +-----+-----+-----+ +-----+-----+-----+-----+-----+
| | |
| load CS | load CS | load CS
| load RIP | load RIP | load RIP
v v v
+-----+-----+-----+-----+ +-----+-----+-----+-----+ +-----+-----+-----+-----+
| 000000000000000 | | 000000000 | | | RIP
+-----+-----+-----+-----+ +-----+-----+-----+-----+ +-----+-----+-----+-----+
JMP to <64K JMP to <4GB JMP to full 64-bit
virtual address virtual address virtual address
NASM examples:
66 FF2C25[69010000] jmp far word [sorry]
FF2C25[69010000] jmp far dword [sorry]
48 FF2C25[69010000] jmp far qword [sorry]
48 FF2C25[69010000] jmp far [sorry]*
* NB. NASM defaults to QWORD and that is for Intel only
AMD:
It is not like REX JMP FAR instruction fails, but the offset read is not qword but dword (the default).
REX prefix is simply ignored by AMD for direct far call: 48FF2C25[69010000] with EIP:CS works. These were all tested.
CALL FAR memory indirect in 64-bit mode
The instruction works in all operand sizes.
- Push CS and operand size RIP
- Jump as far jmp based on operand size. NB: Intel/AMD difference again!
64-bit mode CALL FAR memory indirect with 64-bit stack view and how CALL FAR pushes and loads RIP:
DEFAULT
66h call far word [mem] call far dword [mem] REX.W call far qword [mem]
+-----+-----+-----+-----+ +-----+-----+-----+-----+ +-----+-----+-----+-----+
| xxxxxxxxxxxxxxx | | xxxxxxxxx | | | RIP
+-----+-----+-----+-----+ +-----+-----+-----+-----+ +-----+-----+-----+-----+
x: RIP HI lost x: RIP HI lost
| | |
push word CS | push dword CS | push qword CS |
push word RIP | push dword RIP | push qword RIP |
| | |
v v v
63 0
+-----+-----+-----+-----+
63 15 0 63 0 | CS | +8
+-----+-----+-----+-----+ +-----+-----+-----+-----+ +-----+-----+-----+-----+
| | | CS | IP | | CS | EIP | | RIP | <-- rsp
+-----+-----+-----+-----+ +-----+-----+-----+-----+ +-----+-----+-----+-----+
rsp = rsp - 4 rsp = rsp - 8 rsp = rsp - 16
| | / |
| | AMD! / | Intel only
| | / |
v v / v
/
READS 2 READS 3 v READS 5
MEMORY WORDS MEMORY WORDS MEMORY WORDS
+-----+-----+ +-----+-----+-----+ +-----+-----+-----+-----+-----+
| IP | CS | | EIP | CS | | RIP | CS |
+-----+-----+ +-----+-----+-----+ +-----+-----+-----+-----+-----+
| | |
| load CS | load CS | load CS
| load RIP | load RIP | load RIP
v v v
+-----+-----+-----+-----+ +-----+-----+-----+-----+ +-----+-----+-----+-----+
| 000000000000000 | | 000000000 | | | RIP
+-----+-----+-----+-----+ +-----+-----+-----+-----+ +-----+-----+-----+-----+
JMP to <64K JMP to <4GB JMP to full 64-bit
virtual address virtual address virtual address
The jump mechanism is the same as for far jmp based on operand size – so this is a little tricky on AMD:
REX prefix is partly ignored: REX CALL FAR pushes 64-bit CS and RIP, but, similar to REX JMP FAR instruction, the offset read is not qword but dword (default). Eg. 48FF1C25[6D010000] call far [mem] with EIPCS works. The 64-bit callee, located at <4GB virtual address, should far return by retfq the stack frame is 64-bit.
Note that far calls are not really meant to be executed as direct control transfers; that's what indirect call gates are for. They hold full 64-bit addresses and work the same on both AMD and Intel.
RET FAR
Both Intel and AMD can pop RIP from stack in all operand sizes – so there is no hardware difference here.
64-bit mode RETF with 64-bit stack view and how RETF loads RIP:
63 0
+-----+-----+-----+-----+
63 0 63 0 | | CS | +8
+-----+-----+-----+-----+ +-----+-----+-----+-----+ +-----+-----+-----+-----+
| | | CS | IP | | | CS | EIP | | RIP | <- rsp
+-----+-----+-----+-----+ +-----+-----+-----+-----+ +-----+-----+-----+-----+
rsp = rsp + 4 rsp = rsp + 8 rsp = rsp + 16
retfw retfd* retfq
66 CB CB 48 CB
pop word RIP pop dword RIP pop qword RIP
pop word CS pop dword CS pop qword CS
| | |
v v v
+-----+-----+-----+-----+ +-----+-----+-----+-----+ +-----+-----+-----+-----+
| 000000000000000 | | 000000000 | | | RIP
+-----+-----+-----+-----+ +-----+-----+-----+-----+ +-----+-----+-----+-----+
Returns to <64K Returns to <4GB Returns to any 64-bit
virtual address virtual address virtual address
* In 64-bit the default operand size is 32-bit for RETF
IRET operation in 64-bit mode
Interestingly, iret in 64-bit mode can pull up parameters in all operand sizes (don't know why, the hardware makes 64-bit stack frames only when LME=1).
SS:SP/ESP/RSP is unconditionally pop-ed by all iret in 64-bit mode (but NOT when legacy 16/32-code is running).
These were all tested by manually creating the following 16/32/64-bit stack frames, and successfully returned from 64-bit code (see GitHub):
64-bit stack view for IRET and how IRET loads RIP, RSP and RFLAGS:
63 0
+-----+-----+-----+-----+
| SS | +32
+-----+-----+-----+-----+
63 0 | RETURN RSP | +24
+-----+-----+-----+-----+ +-----+-----+-----+-----+
63 0 | | SS | | RFLAGS | +16
+-----+-----+-----+-----+ +-----+-----+-----+-----+ +-----+-----+-----+-----+
| | SS | | ESP | EFLAGS | | CS | +8
+-----+-----+-----+-----+ +-----+-----+-----+-----+ +-----+-----+-----+-----+
| SP |FLAGS| CS | IP | | CS | EIP | | RIP | <-- rsp
+-----+-----+-----+-----+ +-----+-----+-----+-----+ +-----+-----+-----+-----+
iretw iret* iretq
66 CF CF 48 CF
pops 5 words pops 5 dwords pops 5 qwords
| | |
v v v
+-----+-----+-----+-----+ +-----+-----+-----+-----+ +-----+-----+-----+-----+
| 000000000000000 | | 000000000 | | | RIP
+-----+-----+-----+-----+ +-----+-----+-----+-----+ +-----+-----+-----+-----+
Returns to <64K Returns to <4GB Returns to any 64-bit
virtual address virtual address virtual address
| | |
v v v
+-----+-----+-----+-----+ +-----+-----+-----+-----+ +-----+-----+-----+-----+
| ............... FLAGS| | ......... EFLAGS | | | RFLAGS
+-----+-----+-----+-----+ +-----+-----+-----+-----+ +-----+-----+-----+-----+
Bits 15..0 restored Bits 31..0 restored Bits 63..0 restored
| | |
v v v
+-----+-----+-----+-----+ +-----+-----+-----+-----+ +-----+-----+-----+-----+
| 000000000000000 | | 000000000 | | | RSP
+-----+-----+-----+-----+ +-----+-----+-----+-----+ +-----+-----+-----+-----+
Stack pointer to <64K Stack pointer to <4GB Stack pointer to any
virtual address virtual address 64-bit virtual address
* In 64-bit the default operand size is 32-bit for IRET
This was investigated for any software stack-switch for bare metal programming.
Interrupt-stack in 64-bit mode
Because PE must be set in 64-bit mode, only the IDT calling mechanism works and any other gate than a 64-bit interrupt gate or a 64-bit trap gate causes general-protection exception (#GP).
Interrupt-stack frame creation in 64-bit mode
When LME=1, hardware makes 64-bit stack frames for all exceptions and interrupts, including INT N, regardless of 16/32/64-bit code running (when LMA=1).
Eg. INT N: any 66h, REX prefix ignored - tried.
This also means that all interrupt handlers must be 64-bit code – and return by iretq (there is no way to encode iretq in 16/32-bit code).
In long mode, the return-program stack pointer (SS:RSP) is also pushed unconditionally for any 16/32/64-bit code transfer.
63 0
| |
+-----+-----+-----+-----+
| | <-- rsp before <------------+
+-----+-----+-----+-----+ |
| possibly align | +40 |
+-----+-----+-----+-----+ |
| SS | +32 |
+-----+-----+-----+-----+ |
| RETURN RSP | +24 ------>----------------+
+-----+-----+-----+-----+ points here
| RFLAGS | +16
+-----+-----+-----+-----+
| CS | +8
+-----+-----+-----+-----+
| RIP | <-- rsp after: in interrupt handler
+-----+-----+-----+-----+
64-bit Interrupt-stack frame detailed operation:
1. save current, possibly un-aligned rsp (50% chance)
2. align new rsp by masking with FFFF_FFFF_FFFF_FFF0h (= dummy qword push) to 16-byte boundary
3. start pushing qwords..
- push qword current SS
- push qword the saved RSP
- push qword EFLAGS (rfalgs)
- push qword current CS
- push qword RIP of next instruction
4. Make the far jmp.
64-bit qword REX.W IRET:
- qword pop and load RIP
- qword pop and load CS: fetch, check and cache a valid code descriptor
- qword pop EFLAGS
- qword pop RETURN RSP: restore rsp
- qword pop SS. If value is non-null: fetch, check and cache a valid stack descriptor – otherwise store zero in SS.
Fetch next instruction from RIP.
RETURN RSP
The 64-bit interrupt-stack frame is double-qword-aligned. The CPU therefore saves the original RSP before aligning the stack pointer, then push. The 64-bit stack frame is 40- or 48 bytes. On iret RSP is pop-ed and stack is restored.
RETURN SS
Although SS means almost nothing in 64-bit mode, hw will check the correctness of the descriptor beyond the P-bit (tested). Not on push but on iret: PE=1 and the cpu will attempt to fetch a valid descriptor in the legacy sense, when the pop-ed SS is non-null – to possibly return to 16/32-bit legacy code as well.
But 64-bit allows to pop null-selector on iret, i.e. zero value to load into SS. I used this for some bare metal tests with all CPL=0 (GitHub). See details in AMD/Intel docs.
NEAR in 64-bit mode
NEAR RET
For RET, default operand size is 64-bit and nothing can change that (66h, REX ignored - tried)
CPU pops a qword and replaces RIP.
How RET loads RIP
63 0
+-----+-----+-----+-----+
| RIP | <- RSP
+-----+-----+-----+-----+
ret
pop qword RIP
|
v
+-----+-----+-----+-----+
| | RIP
+-----+-----+-----+-----+
Any prefix ignored. Makes sense: near calls are only between the same 64-bit code.
Far calls could mean between 16/32/64-bit code and all types of RETF supported.
NEAR JMP/CALL rel32
For E9 JMP, operand size is 32-bit signed value and nothing can change that (66h, REX ignored - tried). EA target calculation is similar to RIP-relative:
63 31 0
+----+----+----+----+----+----+----+----+
| sssssssssssss <-- |s | sign-extended DWORD
+----+----+----+----+----+----+----+----+
| RIP of next instr |
+----+----+----+----+----+----+----+----+
_______________________________________________________________________
63 0
+----+----+----+----+----+----+----+----+
| |
+----+----+----+----+----+----+----+----+
RIP
jmp rel32
call rel32
NEAR JMP/CALL register indirect
For [FF] [R 2/4 M] register indirect default operand size is 64-bit, register replaces RIP and nothing can change that (66h, REX ignored - tried).
+------+------+------+------+ +------+------+------+------+
| | <--- | RIP |
+------+------+------+------+ +------+------+------+------+
RIP register
jmp [reg]
call [reg]
NEAR JMP/CALL memory indirect
For [FF] [R 2/4 M] default operand size is 64-bit, cpu reads a qword from memory and replaces RIP. Nothing can change that (66h, REX ignored - tried).
4 WORDS: m64
+------+------+------+------+ +------+------+------+------+
| | <--- | RIP |
+------+------+------+------+ +------+------+------+------+
RIP
jmp [mem]
call [mem]