一.80x86/Pentium 各种寻址方式
1.立即寻址
立即寻址方式下,操作数作为立即数直接包含在指令中,紧跟在操作码之后与其一起
存放在代码段区域。因此,立即数总是和操作码一起被存入 CPU 的指令队列,在指令执行
时不需再访问存储器。
若是 16 位,则低位字节存放在相邻 2 字节存储单元的低地址单元中;
若是 32 位,则低位字存放在相邻 2 字存储单元的低地址单元中。立即寻址方式仅用于源操作数,常用来给寄存器赋初值。
例如:
MOV BL, 12H
MOV AX, 1234H
MOV EDX, 12345678H
其中,MOV EDX, 12345678H 的执行过程如图所示。
2.寄存器寻址方式
(1)直接寻址:
指令中操作数部分直接给出操作数的有效地址 EA,它是 16 位或 32 位的位移量数据,与操作码一起放在代码段中。操作数一般在数据段(DS)中,这是一种默认方式。如果要对除 DS 段之外的其他段(CS、ES、SS、FS、GS)中的数据寻址,应在指令中增加段超越前缀指出段寄存器名。
MOV AX, [2000H] ; 将 DS 段中 2000H和2001H 单元内容分别送 AL 和 AH
MOV AX, ES:[2000H] ; 将 ES 段中 2000H和2001H 单元内容分别送 AL 和 AH
汇编语言程序中,直接寻址中的有效地址 EA 也可用变量名形式给出。例如:
VALUE DB 12H ; 定义变量 VALUE 并赋初值
MOV AL, VALUE ; 将变量 VALUE 所指的字节单元内容 12H 存入 AL
16 位寻址时,EA 放在 SI、DI、BP、BX 中。
若以 SI、DI、BX 间接寻址,则默认操作数在数据段中。若以 BP 间接寻址,则默认操作数在堆栈段中。
若操作数不在上述规定的默认段,则必须在指令的相应操作数前加上段超越前缀。
例如: MOV CX, DS:[BP] ; “DS:”是该指令的段超越前缀
(3)基址寻址
在这种寻址方式下,操作数的有效地址 EA=(基址寄存器)+位移量。式中,位移量在指
令中给出并与操作码一起存放在代码段中。16 位寻址时,BP 和 BX 作为基址寄存器。默认情况下,BX 以 DS 作为段寄存器,BP 以 SS 作为段寄存器,可以段超越。位移量是 8 位或 16 位。
例如: MOV AX, [BX+24] ; 也可写成 MOV AX, 24[BX]
(4)变址寻址
变址寻址方式下,有效地址 EA=(变址寄存器)+位移量。它的指令格式及寻址过程与基
址寻址相同,区别仅在于将基址寄存器改成变址寄存器。16 位寻址时,SI 和 DI 作为变址寄存器,且默认 DS 作为段寄存器。
例如: MOV AX, COUNT[SI]
基址或变址寻址非常适合对一维数组元素进行检索操作,常用位移量表示数组起始地
址,基址或变址寄存器表示数组元素的可变下标,通过修改基址或变址寄存器的内容可方
便地访问数组中的任意元素。
(5)比例变址寻址
比例变址寻址只适合 32 位寻址的情况。有效地址 EA=(变址寄存器)×比例因子+位移量,乘以比例因子的操作是在 CPU 内部靠硬件完成的。
例如: MOV EAX, TABLE[ESI*4]
(6)基址加变址寻址
基址加变址寻址方式中,操作数的有效地址 EA=(基址寄存器)+(变址寄存器)。每种情况下基址、变址寄存器的使用规定和段寄存器的默认规定与前面所述相同。
注意:当一种寻址方式中基址、变址寄存器默认的段寄存器不同时,一般由基址寄存器来决定默认哪一个段寄存器作为段地址。若在指令中规定了段超越,则可用其他段寄存器作为段地址。
例如: MOV AX, [BX+SI] ; 或写成 MOV AX, [BX][SI],BX 决定默认 DS 为段寄存器
MOV EAX, [EDX][EBP] ; 由 EBP 决定默认 SS 为段寄存器
基址加变址寻址适合检索二维数组元素和双重循环等操作。
(7)基址加比例变址寻址
这种寻址方式只适合 32 位寻址的情况。有效地址 EA=(基址寄存器)+(变址寄存器)×比
例因子。例如:
MOV ECX, [EDX*4][EAX] ; 或 MOV ECX, [EDX*4+EAX]
MOV AX, [EBX*8][ESI] ; 或 MOV AX, [EBX*8+ESI]
基址加比例变址寻址适合检索多字节二维数组元素等操作。
二、8086/8088 CPU 指令系统
Intel 8086/8088 CPU 指令系统是 80x86/Pentium 系列 CPU 的基本指令集。指令的操作数宽度是 8 位或 16 位,偏移地址宽度是 16 位。可以分成 6 个功能组:数据传送类、算术运算类、逻辑运算与移位类、串操作类、控制转移类和处理器控制类。
1.数据传送类指令:
通用数据传送指令共5条,除了XCHG,是唯一允许以段寄存器作为操作数的指令。
(1)通用数据传送指令
①MOV 指令
指令格式:MOV OPRD1, OPRD2
功能:将源操作数传送给目标操作数,即 OPRD2→OPRD1,源操作数保持不变。OPRD1和 OPRD2 可以是字节或字,但两者必须等长(同为字节传送或字传送)。
源操作数可以是通用寄存器、段寄存器、存储器以及立即操作数;目标操作数可以是通用寄存器、段寄存器(CS 除外)或存储器。
例:
MOV AL, CH ; 通用寄存器之间传送字节数据
MOV DS, AX ; 通用寄存器与段寄存器(CS 不能是目标)之间传送数据
MOV AX, 0FF3BH ; 立即数 FF3BH 传送到通用寄存器
MOV AL, BUFFER ; 存储器与通用寄存器之间传送数据,BUFFER为字节存储单元名
MOV DAT[BP+DI], ES ; 段寄存器与存储器之间传送数据,DAT为常量或字存储单元名
使用 MOV 指令传送数据时应注意:
②堆栈操作指令 PUSH/POP
堆栈是内存的一个数据区,按照“后进先出”原则组织的一段内存区域。80x86/Pentium CPU 规定,堆栈设置在堆栈段(SS)内,向下生长,堆栈指针 SP 始终指向堆栈的顶部。
堆栈操作指令有两条:入栈指令 PUSH 和出栈指令 POP。当需要在一个操作数和堆栈之间传送数据时,常使用它们。
功能:PUSH 指令使 SP-2→SP,然后将 16 位源操作数压入堆栈。入栈时,高字节存放
在高地址,低字节存放在低地址。源操作数可以是通用寄存器、段寄存器和存储器。出栈指令 POP 的执行过程与 PUSH 相反,它从栈顶弹出 16 位操作数到目标操作数。弹出时,低地址字节送低字节,高地址字节送高字节,同时修改 SP+2→SP,使 SP 指向新的栈顶。目标操作数可以是通用寄存器、段寄存器(CS 除外)或存储器。
③交换指令 XCHG:
功能:将长度相同(同为字节或同为字)的源操作数与目标操作数进行交换。
例如:
MOV AX, 2244H ; AX=2244H
MOV BX, 3366H ; BX=3366H
XCHG AX, BX ; 交换后,AX=3366H,BX=2244H
注意:① 段寄存器和立即数不能作为操作数;② 不能在两个存储单元之间直接交换数据。
④查表转换指令 XLAT(或称为换码指令)
指令格式:XLAT 或 XLAT OPRD ; AL←[BX+AL]
功能:完成 1 字节的查表转换,将数据段中偏移地址为(BX+AL)的存储单元的内容送入 AL 寄存器,即 DS:[BX+AL]→AL。
XLAT 指令对于一些无规律的代码转换(换码)特别方便。使用时,先在数据段建立一个长度小于 256 字节的表格,表的首地址置于 BX 中,AL 中存放查找对象在表中的下标。指令执行后,所查找的对象存于 AL 中,BX 内容保持不变。
例如,将数字 0~9 的 BCD 码转换为 7 段 LED 显示器的显示代码。
数字 0~9 的 BCD 码对应的 7 段 LED 显示代码为 40H,79H,24H,30H,19H,12H,02H,78H,00H,18H,将它们依次存放在数据段中偏移地址为 0800H 开始的区域。若待转换的 BCD 码为 0100B,则实现代码转换的程序段为:
MOV BX, 0800H
MOV AL, 4
XLAT ; AL 中为 0100B 对应的 7 段代码 19H
地址传送指令用于传送存储器的逻辑地址信息,指令中的源操作数只能是存储器操作数。
①有效地址传送指令 LEA
指令格式:LEA OPRD1, OPRD2
功能:将源操作数在当前段内的有效地址(即偏移地址)传送至目标操作数。
注意:这条指令同 MOV 指令的区别是,MOV 指令传送操作数的内容,而 LEA 传送的是操作数的存储地址。例如:
MOV DI, TABLE ; 将变量 TABLE 的内容传送至 DI
LEA DI, TABLE ; 将变量 TABLE 的偏移地址传送至 DI
LEA DX, [1000H] ; 将 1000H 单元的偏移地址→DX,DX=1000H
②地址指针传送指令 LDS 和 LES
这两条指令的功能类似,都是将由源操作数有效地址决定的双字存储单元中的第一个字的内容送入指令指定的 16 位通用寄存器,第二个字的内容传送给段寄存器 DS 或 ES。通常,双字存储单元中是一个 32 位的地址指针。
例如: TABLE DD 12345678H
LDS BX, TABLE ; BX←5678H,DS←1234H
③输入/输出数据传送指令 IN/OUT
这组指令专门用于在 AL 或 AX 寄存器与 I/O 端口之间传送数据。
一台计算机可以配接许多外部设备,每个外部设备与 CPU 之间要交换数据、状态信息和控制命令,每一种这样的信息交换都要通过一个端口来进行。计算机系统中的各端口也像存储器那样用地址来区分。80x86/Pentium CPU 中有 16 条 I/O 地址线,最多可提供 64K个传送 8 位数据的端口地址或 32K 个传送 16 位数据的端口地址。当端口地址小于 256 时,采用直接寻址方式,在指令中直接指定端口地址;当端口地址大于或等于 256 时,采用间接寻址方式,端口地址放在 DX 中。
例如:
IN AX, 20H ; 从端口 20H 和 21H 输入 16 位数据到 AL 和 AH(AX)
MOV DX, 3F0H
IN AL, DX ; 从端口 03F0H 输入 8 位数据到 AL
OUT 27H, AL ; 将 8 位数据从 AL 输出到端口 27H
OUT DX, AX ; 将 16 位数据从 AX 输出到 DX 和 DX+1 指定的端口
算术运算类指令支持加、减、乘、除等基本算术运算,操作对象可以是字节或字无符号和有符号的二进制整数,也可以是无符号的压缩、非压缩 BCD 数。非压缩 BCD 数的高 4 位在进行乘/除运算时必须全为 0,进行加/减运算时可以是任何值。
①加/减法指令 ADD/SUB
功能:完成两个操作数的加/减运算,结果送入目标操作数
要求源操作数和目标操作数同为带符号数或同为无符号数,且长度相等。源操作数可以是寄存器、存储器或立即数,目标操作数只能是寄存器或存储器,且两个操作数不能同时为存储器。运算影响全部状态标志位。
例如,设 AX=65A0H,BX=B79EH,则指令 ADD BX, AX 执行后,BX=1D3EH,
CF=1,SF=0,OF=0,AF=0,PF=0,ZF=0。
结果表明,若 BX 和 AX 中是无符号数,则运算结果大于 65535(CF=1);若 BX 和AX 中是有符号数,则运算结果在 16 位补码所能表示的范围内(–32768~+32767),运算结果正确(OF=0,SF=0)。
结果表明,若两个操作数是无符号数,CF=1 表示不够减,运算结果是以 216 为模的差值的补码;若两个操作数是有符号数,则运算结果超出 16 位补码所能表示的范围(OF=1),AX 中存放的数(AE02H)并不是正确的运算结果。
②带进位、借位的加/减法指令 ADC/SBB
ADC/SBB 指令常与 ADD/SUB 配合,用于多精度数间的运算。除了加法操作时要加上进位位或减法操作时再减去借位位,它们同 ADD/SUB 指令的使用注意事项和对标志位的影响情况相同。
例如,有两个 32 位数(多精度数)分别存放在自 FIRST 和 SECOND 开始的存储区中,变量定义如下(DW 是汇编语言伪指令,它为变量 FIRST、SECOND 按字分配存储单元):
FIRST DW 2211H, 4433H ; 定义变量 FIRST=44332211H
SECOND DW 6655H, 8877H ; 定义变量 SECOND=88776655H
用字相加指令实现两个变量的和,并将和存放于 FIRST 变量的程序段为:
MOV AX, SECOND ; 取第二个加数的低 16 位