CPU寄存器与寻址方式
本文属于《 X86架构指令基础系列教程》之一,欢迎查看其它文章。
1 CPU寄存器
以下为X64架构下,CPU具备的通用目的寄存器情况。
- X64为64位寻址,因此寄存器为64位,如红色框所示,具备rax~r15共16个寄存器;
- X86为32位寻址,因此寄存器为32位,如蓝色框所示,具备eax~esp共8个寄存器;
- 8086为16位寻址,因此寄存器为16位,如橙色框所示,具备ax~sp共8个寄存器。
X64将X86的8个通用寄存器扩展为64位,增加了8个新的通用64位寄存器。64位寄存器的名称都以字母“r”开头(当然,寄存器名称不区分大小写,也可以是“R”开头),例如,eax的64位扩展寄存器命名为rax,新增加的寄存器命名从r8到r15。
每个寄存器的低32位,低16位,低8位可在操作数中直接寻址。这包括像esi这样的寄存器,其低8位在以前是不能直接寻址的。
2 表达数据位置
汇编语言中用3个概念来表达数据的位置。
- (1)立即数(idata)
对于直接包含在机器指令中的数据(执行前在CPU的指令缓冲器中),在汇编语言中称为:立即数(idata),在汇编指令中直接给出。
例:
mov ax,1
add bx,2000h
or bx,00010000b
mov al,'a’
- (2)寄存器
指令要处理的数据在寄存器中,在汇编指令中给出相应的寄存器名。
例:
mov ax,bx
mov ds,ax
push bx
mov ds: [0],bx
push ds
mov ss,ax
mov sp,ax
- (3)段地址(SA)和偏移地址(EA)
指令要处理的数据在内存中,在汇编指令中可用[X]的格式给出EA,SA在某个段寄存器中。
存放段地址的寄存器可以是默认的,比如:
mov ax, [0]
mov ax, [di]
mov ax, [bx+8]
mov ax, [bx+si]
mov ax, [bx+si+8]
等指令,段地址默认在 ds 中。
mov ax, [bp]
mov ax, [bp+8]
mov ax, [bp+si]
mov ax, [bp+si+8]
等指令,段地址默认在 ss 中。
存放段地址的寄存器也可以是显性给出的,比如以下的指令。
mov ax,ds: [bp] 含义:(ax)=((ds)*16+(bp))
mov ax,es: [bx] 含义:(ax)=((es)*16+(bx))
mov ax,ss: [bx+si] 含义: (ax)=((ss) *16+ (bx) + (si))
mov ax,cs: [bx+si+8] 含义: (ax)=((cs) *16+ (bx) + (si) +8)
3 寻址方式
以下为8086 CPU支持的常见寻址方式,有以下五种。
当数据存放在内存中的时候,我们可以用多种方式,来给定这个内存单元的偏移地址,这种定位内存单元的方法,一般被称为寻址方式。
接下来,我们对每种寻址,进行举例说明。
3.1 直接寻址
指令形式:[idata],比如:
mov ax, [2000h]
段地址默认在 ds 中,含义:(ax) = ((ds) * 16 + 2000h),表示将ds段寄存器中基址 * 16 + 2000h,所指向的内存,拷贝到ax寄存器中,长度为2字节。
3.2 寄存器间接寻址
以 [bx] 为例,比如:
mov ax, [bx]
段地址默认在 ds 中,含义:(ax) = ((ds) * 16 + (bx)),表示将ds段寄存器中基址 * 16 + bx寄存器中偏移,所指向的内存,拷贝到ax寄存器中,长度为2字节。
相比[idata]直接寻址,多了一步,即需要先读取bx寄存器中的偏移,因此叫间接寻址。
3.3 寄存器相对寻址
以 [bx+idata] 为例,比如:
mov ax, [bx+2000h]
段地址默认在 ds 中,含义:(ax) = ((ds) * 16 + (bx) + 2000h),表示将ds段寄存器中基址 * 16 + bx寄存器中偏移 + 2000h,所指向的内存,拷贝到ax寄存器中,长度为2字节。
相比[bx]间接寻址,又多了一步,即额外还要加一个相对bx寄存器的常量偏移,因此叫寄存器相对寻址。
3.4 基址变址寻址
以 [bx+si] 为例,比如:
mov ax, [bx+si]
段地址默认在 ds 中,含义:(ax) = ((ds) * 16 + (bx) + (si)),表示将ds段寄存器中基址 * 16 + bx寄存器中偏移 + si寄存器中偏移,所指向的内存,拷贝到ax寄存器中,长度为2字节。
3.5 相对基址变址寻址
以 [bx+si+idata] 为例,比如:
mov ax, [bx+si+2000h]
段地址默认在 ds 中,含义:(ax) = ((ds)*16 + (bx) + (si) + 2000h),表示将ds段寄存器中基址 * 16 + bx寄存器中偏移 + si寄存器中偏移 + 2000h,所指向的内存,拷贝到ax寄存器中,长度为2字节。
相比[bx+si]寻址,多了一步,即额外还要加一个相对[bx+si]寄存器的常量偏移,因此叫相对基址变址寻址。
3.6 按比例变址寻址(SIB)
上面5种寻址方式是8086以上的CPU,均具备的基本寻址方式。
按比例变址寻址,也称为"scale-index-base"(SIB)寻址,是仅在32和64位CPU架构才支持的内存寻址方式。
在SIB寻址中,地址计算分为三个部分:基址、索引和比例因子。
-
基址(base),包含一个或多个寄存器,用于指定基址。在指令中,可以使用一个或多个寄存器来作为基址。这些寄存器可以是通用寄存器(如RAX、RBX等)或系统寄存器(如CR3等)。
-
索引(index),包含一个或多个寄存器,用于指定在基址上的偏移量。在指令中,可以使用一个或多个寄存器来作为索引。这些寄存器可以是通用寄存器(如RCX、RDX等)或系统寄存器(如RSI、RDI等)。
-
比例因子(scale),是一个常数,用于指定比例因子。在指令中,可以使用一个字节或多个字节来表示比例因子。比例因子的值可以是1、2、4、8等,具体取决于指令和操作的要求。
-
位移量(displacement),可选,可以是立即数(常量)或寄存器值,它表示相对于基址的偏移量。
通过将基址、索引和比例因子结合起来,可以计算出最终的内存地址。
计算公式为:内存地址 = 基址 + 索引 * 比例因子 + 位移量(若存在)。其中,“+”表示加法运算,“*”表示乘法运算。
按比例变址寻址,我们可以用[- -][- -]符号来表示。
例如:
mov eax,[ebx+ecx*2]
含义:(eax) = ((ebx) + (ecx) * 2),表示将ebx寄存器中基址 + ecx寄存器值 * 2,所指向的内存,拷贝到eax寄存器中,长度为4字节;位移量为0。
mov eax,[ebx+ecx*2+100h]
含义:(eax) = ((ebx) + (ecx) * 2 + 100h),表示将ebx寄存器中基址 + ecx寄存器值 * 2 + 100h,所指向的内存,拷贝到eax寄存器中,长度为4字节;位移量为100h。
参考文档:
- 《汇编语言 第3版》 王爽 清华大学出版社
- 《X64汇编语言寄存器结构及其与X86架构编程区别》
- 《汇编语言的寻址模式及示例》