3.2 寻址方式
计算机在运行过程中需要的数据称为操作数,寻找指令中所需要的操作数或操作数地址的方式称为寻址方式。80486微处理器指令系统的寻址方式包括三种类型:操作数寻址、转移地址寻址及I/O端口寻址。
3.2.1 关于操作数的寻址方式
指令中所需要的操作数来自以下几个方面。
① 操作数包含在指令中。在取指令的同时,操作数也随着取出,这种操作数被称为立即数。
② 操作数包含在CPU的某个内部寄存器中。由于寄存器在CPU的内部,因此取操作数也比较简单。
③ 操作数在内存储器中。由于内存储器在CPU的外部,因此,在寻找这种操作数时需要执行一个总线周期。首先找到该操作数在内存中存放的地址,再从该地址中取出操作数。
在80486微机系统中,任何内存单元的地址都由段基址和偏移地址(又称偏移量)组成。其中,段基址由段寄存器提供,而偏移地址则由以下4个部分组合而成。
① 基址寄存器
② 间址寄存器(或称变址寄存器)
③ 比例因子
④ 位移量
以上4个部分称为偏移地址四元素。一般将这4个元素按某种计算方法组合形成的偏移地址称为有效地址EA。有效地址的组合方式和计算方法如下:
EA=基址+(间址×比例因子)+位移量
采用16位寻址时,位移量是8位或16位,用BX和BP作为基址寄存器,SI和DI作为间址寄存器,比例因子为1。
采用32位寻址时,可使用8位和32位的位移量,32位通用寄存器都可以作为基址寄存器或间址寄存器(ESP不用于间址),并且可采用2、4或8三种不同的比例因子。
以上4个因素可优化组合出9种存储器寻址方式,加上立即寻址和寄存器寻址,共有11种寻址方式。
以汇编语言中的MOV传送指令为例,其指令格式为:
MOV 目标操作数,源操作数;将源操作数传送到目标操作数
1.立即数寻址(immediate addressing)
立即数寻址的特点:操作数就在指令中,跟在操作码后面,称为立即数。在指令格式中,立即数一般为源操作数。
注意:在汇编语言中,立即数是以常数形式出现的。常数可以是二进制数(后缀字母B或b)、十进制数(不用后缀字母,或用D或d)、十六进制数(后缀字母H或h,以A~F开头时,前面要加一个0)、字符串(用单引号括起来的字符,表示对应的ASCII码值)。
例如:MOV AL,0FH;将8位立即数0FH传送到AL寄存器中
MOV AX,0102H;将16位立即数0102H传送到AX寄存器中
这两条指令的指令码在内存中的存放格式及指令执行过程如图3.1所示。
图3.1 立即数寻址操作
注意:立即数寻址时,只允许源操作数为立即数,而目标操作数必须是寄存器或存储器操作数,其作用是给寄存器或存储单元赋值。
2.寄存器寻址(register addressing)
寄存器寻址的特点:指令中所需的操作数在CPU的某个寄存器中,由于存取此类操作数是在CPU内部进行的,因此执行速度较快。
例如:MOV AX,CX;将CX中的内容传送到AX中
该指令的寻址及执行过程如图3.2所示。
图3.2 MOV AX,CX指令的寻址及执行过程
3.直接寻址(direct addressing)
直接寻址的特点:操作数一般存放在存储器的数据段中,而操作数的有效地址EA由指令给出。
物理地址=(DS)×16+EA
例如:MOV AX,[2000H];将EA=2000H字单元中的内容传送到AX寄存器中
在汇编语言中,带方括号“[ ]”的操作数称为存储器操作数,括号中的内容作为存储单元的有效地址EA。存储器操作数本身并不能表明地址的类型,而需要通过另一个寄存器操作数的类型或别的方式来确定。上例中由于目标操作数AX为字类型,源操作数也应与之对应,因此有效地址EA=2000H为字单元。设DS=3000H,该指令的寻址及执行过程如图3.3所示。
物理地址=3000H×16+2000H=32000H
图3.3 MOV AX,[2000H]指令的寻址及执行过程
该指令的功能是将存储器32000H和32001H两个存储单元的内容,按照高位字节对应高地址,低位字节对应低地址的原则,送到AX寄存器中。
直接寻址允许用符号地址来代替数值地址,例如:MOV AX,[DATA],变量DATA为存放操作数的存储单元的符号地址,还可写成MOV AX,DATA。直接寻址适用于处理单个变量。
4.寄存器间接寻址(register indirect addressing)
寄存器间接寻址的特点:操作数在存储器中,其有效地址EA存放在某个寄存器中。
注意:寄存器的使用在16位寻址和32位寻址时不一样。
(1)16位寻址
有效地址存放在SI、DI、BX、BP中。
如果指令中指定的寄存器是BX、SI、DI,则操作数在数据段中,段基址在DS中,操作数的物理地址为:
如果指令中指定的寄存器是BP,则操作数在堆栈中,段基址在SS中,操作数的物理地址为:
物理地址=(SS)×16+(BP)
例如:MOV AX,[BP]
设SS=3000H,BP=1000H该指令寻址及执行过程如图3.4所示。
(2)32位寻址
8个32位通用寄存器均可作为寄存器间接寻址使用。除ESP和EBP默认段寄存器为SS外,其余6个通用寄存器均默认段寄存器为DS。
寄存器间接寻址方式用于表格处理,执行完一条指令后,只需要修改寄存器内容就可取出表格中的下一项。
图3.4 MOV AX,[BP]指令的寻址及执行过程
5.基址寻址(based relative)
基址寻址的特点:操作数在存储单元中,操作数的有效地址EA由基址寄存器的内容和指令中给出的位移量之和算出。
(1)16位寻址
BX和BP作为基址寄存器,其中BX以DS作为默认段寄存器,BP以SS作为默认段寄存器。位移量可以是8位或16位。
EA=(BX或BP)+位移量(8或16位)
(2)32位寻址
8个32位通用寄存器均可作为基址寄存器,其中ESP、EBP以SS作为默认段寄存器,其余均以DS作为默认段寄存器。位移量为8位或32位。
EA=(基址寄存器)+位移量(8位或32位)
6.间址寻址(indexed relative)
在间址寻址中,有效地址的计算公式如下:
EA=(间址寄存器)+位移量(8位或32位)
(1)16位寻址
只有SI和DI可作为间址寄存器,默认DS为段基址寄存器。
(2)32位寻址
除ESP以外的其他7个32位的寄存器均可作为间址寄存器,EBP默认SS作为段基址寄存器,其余以DS作为段基址寄存器。
基址寻址和间址寻址适用于对一维数组的数组元素进行检索操作。通常,用位移量表示数组起始地址的偏移量,基址/间址寄存器表示数组元素的下标,可通过修改下标来获取数组元素的值。
7.比例间接寻址(proportion indirect addressing)
在比例间接寻址中,有效地址的计算公式如下:
EA=(间址寄存器)×比例因子(2,4或8)+位移量(8位或32位)
比例间接寻址方式只适用于32位寻址。
例如:MOV EAX,TABLE [ESI×4]
其中,TABLE为位移量,4是比例因子,ESI乘以4的操作在CPU内部完成。
8.基址加间址寻址(based indexed addressing)
基址加间址寻址方式也包括16位寻址和32位寻址,有效地址的计算公式如下:
EA=(基址寄存器)+(间址寄存器)
例如:MOV AX,[BX+SI]
MOV DX,[BX+DI]
如果DS=1000H,BX=2000H,DI=0010H,则
物理地址=DS×16+BX+DI
=10000H+2000H+0010H
=12010H
其寻址及执行过程如图3.5所示。
图3.5 MOV DX,[BX+DI]指令的寻址及执行过程
注意:当基址寄存器和间址寄存器默认的段寄存器不同时,一般以基址寄存器决定的段寄存器作为段基址寄存器。
例如:MOV EAX,[EBP][ECX]
由于基址寄存器是EBP,因此默认SS为段基址寄存器。
基址加间址寻址主要用于二维数组的操作。
9.基址加比例间址寻址(based indexed proportion addressing)
在基址加比例间址寻址方式中,有效地址的计算公式如下:
EA=(基址寄存器)+(间址寄存器)×比例因子
例如:MOV EAX,[EBX] [EDI×8]
基址加比例间址寻址方式只适用于32位寻址。
10.带位移的基址加间址寻址(based indexed relative)
在带位移的基址加间址寻址方式中,有效地址的计算公式如下:
EA=(基址寄存器)+(间址寄存器)+位移量
带位移的基址加间址寻址方式分为16位和32位寻址两种情况。
例如:MOV AX,[BX+SI+MASK]
ADD EDX,[ESI][EBP+0FFF0000H]
MOV DX,RSSA [BX][SI]
(也可写成MOV DX,RSSA [BX+SI]或MOV DX,[RSSA+BX+SI])
如果DS=3000H,BX=2000H,SI=1000H,位移量RSSA=0250H时,则
物理地址=DS×16+BX+SI+RSSA
=30000H+2000H+1000H+0250H
=33250H
其寻址及执行过程如图3.6所示。
图3.6 MOV DX,RSSA[BX][SI]指令的寻址及执行过程
注意:当基址寄存器和间址寄存器默认的段寄存器不同时,一般以基址寄存器决定的段寄存器作为段基址寄存器。
11.带位移的基址加比例间址寻址(based indexed proportion relative)
EA=(间址寄存器)×比例因子+(基址寄存器)+位移量
这种寻址方式只有32位寻址一种情况。
例如:INC BYTE PTR [EDI×8][EDX+40H]
以上11种寻址方式可以分为两大类。
(1)非存储器操作寻址方式:包括立即数寻址和寄存器寻址。这类寻址方式不需要访问存储器,故执行速度快。
(2)访问存储器操作寻址方式:后9种寻址方式属于这一类。在进行访问存储器操作时,除要计算有效地址EA外,还必须确定操作数所在的段,即确定有关的段寄存器。
编写程序时应注意以下5点。
① 在一般情况下,指令不特别指出段寄存器。80486微处理器约定了默认的段寄存器。
② 有的指令允许段超越寻址,这时指令中应加上超越前缀。
③ 程序只能存放在代码段中,使用IP(EIP)作为偏移地址寄存器。
④ 堆栈操作数只能在堆栈中,使用SP或BP(ESP或EBP)作为偏移地址寄存器。
⑤ 在串操作中,目标操作数只能在附加数据段ES中,其他操作虽然也有默认段,但都是允许段超越的。
3.2.2 对程序转移地址的寻址方式
通常,CPU执行程序的顺序是由代码段寄存器CS和指令指针IP(EIP)的内容所确定的。指令指针IP(EIP)具有自动加1的功能,每当BIU取完一条指令以后,IP(EIP)的内容都会自动加1指向下一条指令,以便使程序按照指令存放的次序由低地址到高地址依次执行。当程序中有跳转指令时,就需要改变以上顺序执行的过程,按照指令的要求修改IP(EIP)的内容或同时修改IP(EIP)和CS的内容,从而将CPU引导到指令所规定的地址去执行。程序转移地址的寻址方式,寻找的是程序转移的目标地址,而不是操作数。
在80486微机系统中,由于存储器采用了分段结构,因此,对转移地址的寻址方式分为段内寻址和段间寻址两类。图3.7以16位寻址为例,展示了转移地址的寻址方式。
1.段内直接寻址
段内直接寻址也称为相对寻址。转向的有效地址是当前IP寄存器中的内容和指令指定的8位或16位位移量之和,如图3.7(a)所示。
当位移量是8位时,称为短程转移,常在转向的符号地址前加操作符SHORT。当位移量是16位时,称为近程转移,常在转向的符号地址前加操作符NEAR PTR。
例如:JMP SHORT LOOP1
JMP NEAR PTR LOOP2
其中,LOOP1和LOOP2均为程序转向的符号地址。
2.段内间接寻址
段内间接寻址方式的特点是程序转向的有效地址存放在寄存器或存储单元中。指令执行时,可用寄存器或存储单元中的内容去更新指令指针IP值,从而正确地实现程序转移,如图3.7(b)所示。
例如:JMP BX
JMP WORD PTR[SI+2000H]
其中,WORD PTR是一个操作符,说明跟在其后的存储器操作数所取得的转向地址是一个字类型的有效地址,从而表明这种寻址方式是一种段内转移。
图3.7 程序转移地址的寻址方式
以上两种寻址方式均为段内寻址,由于转向的目标地址与跳转指令同在一个代码段内,因此不需修改CS的内容,只需要修改指令指针IP的内容,根据指令的寻址方式求得转向的有效地址EA并送到IP寄存器就可以了。转向的物理地址计算公式为:
物理地址=(CS)×16+IP
3.段间直接寻址
段间直接寻址方式的特点是,在跳转指令中直接给出转向的段基址和偏移地址,16位的段基址用来更新CS,16位的偏移地址用来更新IP,从而完成从一个段到另一段的转移。在这种寻址方式的指令中,常在转向的符号地址前加上操作符FAR PTR,如图3.7(c)所示。
例如:JMP FAR PTR LOOP3;LOOP3为转向的符号地址
4.段间间接寻址
段间间接寻址方式的特点是,由指令寻址方式确定的连续两个字的内容来取代IP和CS寄存器中的原有内容,低位字单元中的16位数据作为转向的偏移地址用以取代IP中的内容,高位字单元中的16位数据作为段基址用以取代CS中的内容,从而实现段间程序转移,如图3.7(d)所示。
例如:JMP DWORD PTR [BX]其中,DWORD PTR为双字操作符,说明后面紧跟的存储器操作数所取得的转向地址是一个双字的有效地址。
以上两种寻址方式均为段间寻址,跳转指令和转向地址分别在两个不同的代码段内,所以既需要修改IP的内容,又需要修改CS的内容,这样才能实现段间转移。
3.2.3 关于I/O端口的寻址方式
80486微处理器允许使用地址总线的低16位A15~A0来访问I/O端口,共有65536(216)个端口地址,地址范围为0000H~FFFFH。80486微处理器采取独立编址方式,对I/O端口可采用两种寻址方式。
1.直接寻址
端口的直接寻址方式仅适合于访问地址范围为00H~FFH的端口,在输入/输出指令中,端口地址以8位立即数的形式出现。
例如:IN AL,80H
表示由地址为80H的端口读取一个字节数据到AL中。
2.间接寻址
端口的间接寻址方式适合于访问地址范围为0000H~FFFFH的全部端口,当端口地址为0100H~FFFFH时,必须采用间接寻址方式。在输入/输出指令中,端口是16位的立即数。端口间接寻址只可使用DX寄存器,16位的I/O端口地址必须预置在DX中。
例如:MOV DX,2000H
OUT DX,AX
表示将AX中的16位数据由(DX+1)和(DX)确定的2001H和2000H两个端口输出。