对于一条指令,我们重点关注它两点:执行什么样的操作,操作数在哪里。
操作数存放的位置即为存放地址,一般是CPU内寄存器、主存、或者I/O设备端口。当操作数在主存时,我们重点关注段址/段选择符、段内偏移。
寻找操作数存放地址的方式称为寻址方式。
双操作数的指令格式为:操作符 OPD OPS,如ADD EAX EBX,执行(OPD) + (OPS) ➡ OPD( (EAX) (目的操作数地址) + (EBX)(源操作数地址) ➡ EAX)
立即寻址
操作数直接放在指令中,在指令的操作码后;
操作数是指令的一部分,位于代码段中;
指令中的操作数是8位、16位或32位二进制数。
立即操作数只能为源操作数。
使用格式:
例如: MOV EAX , 12H 机器码: B8 12 00 00 00
int global; // 注:global 是全局变量
global = 2 * 5 ;
/*
00521A17 C7 05 B8 A5 52 00 0A 00 00 00
mov dword ptr [global (052A5B8h)], 0Ah
*/
寄存器寻址
使用格式: R
功 能: 寄存器R中的内容即为操作数。
说 明: 除个别指令外,R可为任意寄存器。
例如:DEC BL
- 执行前 : (BL) = 43H
- 执 行: (BL) – 1 = 43 H –1 = 42H ➡ BL
- 执行后: (BL) = 42H
直接寻址
操作数在内存中;
操作数的偏移地址EA紧跟在指令操作码后面。
int global;
void address_compare()
{
int local;
global = 2*5;
*((char *)&global + 1) = 20;
printf("global = %d %08x\n", global, global);
local = 10;
*((char*)&local + 1) = 20;
printf("local = %d %08x\n", local, local);
}
全局变量的访问——直接寻址
局部变量的访问——变址寻址(见后)
寄存器间接寻址
格式:[R]
功能:操作数在内存中,操作数的偏移地址在寄存器R中。即(R) 为操作数的偏移地址。
例如:MOV AX, [ESI]
R 可以是:8个32位通用寄存器中的任意一个
操作数的偏移地址:在指令指明的寄存器中
操作数所在的段: 扁平内存管理模式下,(DS)=(SS)
变址寻址
格式:V [ R × F ]
功能:R中的内容 × F + V 为操作数的偏移地址。
例如:MOV AL, 5[EBX*2]
R 可以是8个32位通用寄存器,F 可为 1,2,4,8
- 全局变量编译后对应一个数值化的地址
- 局部变量编译后对应一个地址为 [ebp – n]
基址加变址寻址
格式: [BR+IR × F + V]
或 V[BR][IR×F] 或 V [IR×F][BR] 或 V[BR+IR×F]
功 能:操作数的偏移 = 变址寄存器IR中的内容 × 比例因子F + 位移量V + 基址寄存器BR中的内容 。
EA = (IR)*F + V + (BR)
例如:MOV EAX, -6[EDI*2][EBP]
F 可为 1,2,4,8;当使用32位寄存器时:BR可以是 EAX, EBX, ECX, EDX, ESI, EDI, ESP, EBP 之一;
IR 可以是除ESP外的任一32位寄存器;
未带比例因子的寄存器是 BR;
当没有比例因子时,写在前面的寄存器是BR。
操作数的类型:若V为变量,则操作数类型为变量的类型;
若V为常量,类型未知。
当 V 中存在全局变量或标号时,用的段都是 DS,在 VS2019中, (DS)=(SS)。CS中的全局变量等同 DS段中的变量。
x86机器指令编码规则
① 指令前缀(prefix,非必需,0个或多个字节)
② 操作码(opcode,必须,1字节 ~ 3字节)
③ 内存/寄存器操作数(ModR/M,非必需): 指明寻址方式
④ 索引寻址描述 (SIB,非必需):指明基址寄存器、变址寄存器、比例因子
⑤ 地址偏移量(Displacement,非必需)
⑥ 立即数(Immediate,非必需)
操作码:
- 指明了要进行的操作。
- 指明操作数的类型(一般看最后一个二进制)0:字节操作; 1:字操作(32位指令中为双字操作);有指令前缀 66H时,对字操作。
- 指明源操作数是寄存器寻址,还是目的操作数是寄存器寻址 (操作码的倒数第二个二进制位)1:目的操作数是寄存器寻址,0:源操作数寄存器寻址;与[寻址方式字节]配合使用
内存/寄存器操作数(ModR/M) :
Mod由2个二进制位组成,取值是00、01、10、11。Mod与为R/M配合使用,明确一个操作的获取方法。Reg/Opcode 确定另外一个寄存器寻址的寄存器编码
- Mod=00, R/M =000,表示 用 [EAX] 寻址
- Mod=00,R/M=100, 表示用[--][--] ,无位移量的基址加变址,在 SIB 字节指明基址/变址寄存器的编码
Reg/Opcode 的编码:同一编码有多个寄存器,用哪一个寄存器,取决于指令前缀和操作码中的编码
mov eax, [ebx] ; 机器码是:8B 03 ➡ 00 000 011 [EBX]
mov [ebx], eax ; 机器码是:89 03 ➡ 1000 1001 VS 1000 1011
操作码的倒数第二位: 1:OPD是寄存器, 0:OPS是寄存器