It's not the whole instruction that has a certain addressing mode, it's each operand separately. In your mov eax, 123
example, you'd say that the source is an immediate operand, and the destination is a register operand.
Or you could say that the machine code for that instruction will use the mov r, imm32
encoding of mov
, if you want to talk about the form the whole instruction takes. (There's also a mov r/m, imm32
form of mov
, but it's longer so a good assembler will only pick that if the destination actually is memory).
However, when one of the operands is a register, you could for convenience and brevity say "the instruction uses a [base+index]
addressing mode" if you want. But really it's the memory operand that you're talking about, not really the whole instruction. Especially if you count register and immediate as "addressing modes", even though there is no memory address involved.
Moreover, usually when people say "addressing mode", they're talking about a memory address. Technically in x86, most instructions have one register and one register/memory operand, so the difference between add eax, ecx
and add eax, [ecx]
is only I think 1 bit in the mod/rm
byte (which follows the opcode).
Some instructions have two memory operands. For example, push qword [rdi + rax*8]
explicitly loads from [rdi + rax*8]
and implicitly stores to [rsp]
. Another example are the string instructions movs
and cmps
, which use [rdi]
and [rsi]
implicitly.
But no instruction has two general r/m operands that let you use an arbitrary choice of the normal addressing modes. So an x86 instruction has at most one mod/rm byte.
It's debatable whether an immediate operand should be called an "addressing mode", because the data doesn't come from anywhere. It's part of the instruction. Also, the immediate-operand form of an instruction has a different opcode from the reg, reg/mem form.
Also note that most integer instructions that can have a memory source or memory destination have two opcodes: one for op r/m, r
and one for op r, r/m
. (e.g., see the ref manual entry for and
, and more links to docs in the x86 tag wiki.) Anyway, and eax, ecx
can be encoded with either of the two opcodes, and it's up to the assembler to pick. The choice makes no difference for performance.