第三章 微处理器的指令系统
3.1 8086/8088的寻址方式
- 指令格式包括操作码和操作数(或地址)两个部分,根据操作码所指定的功能去寻找操作数所在地址的方式就是寻址方式
- 寄存器操作数,8086CPU 的 8 个 16 位的通用数据寄存器 AX、BX、CX、DX、SP、BP、SI、DI 和 4 个段寄存器 CS、DS、SS、ES 可以作为 16 位寄存器操作数,AH、BH、CH、DH 和 AL、BL、CL、DL 可以作为 8 位寄存器操作数。控制寄存器 IP、Flags 只在特定指令中作为操作数。寄存器操作数在指令中可以作为源操作数也可以作为目的操作数,段寄存器 CS 除外,它只能作为源操作数。
3.1.1 数据寻址方式
- 数据寻址方式有多种,下图给出了各种数据寻址方式的类型,指令举例,以及存储器地址生成方式与数据流向.所有操作数的流向都是从源到目标,源和目标可以是寄存器或存储器,但不能同时为存储器(除个别串操作指令MOVS外)
1. 立即寻址
操作数是立即数,可以是 8 位或 16 位的二进制数,也可以是字符常数。例如:
MOV AX, 2000H ;2000H 是立即数操作数
MOV AH, ’A’ ;’A’是字符常数,等于 41H
ADD AL, 6 ;6 是立即数操作数
立即数作为操作数,这个操作数的寻址方式称为立即寻址,其实它不用寻址。立即数操作数只能作源操作数,不能作为目的操作数。
2. 直接寻址
操作数在内存中,指令中直接给出操作数所在的内存单元的偏移地址。可以是数值形式的地址,也可以用符号表示。用符号表示的地址称为符号地址。例如:
MOV BL, [2000H]
将偏移地址为 2000H 的内存单元的内容传送给 BL 寄存器。操作数[2000H]的寻址方式为直接寻址,方括号表示地址。如果 2000H 单元存储的数据为 55H,上面指令执行后 BL=55H。再如:
MOV BX, [3200H]
将偏移地址为3200H为首地址的连续两个内存单元的内容传送给BX寄存器。操作数[3200H] 的寻址方式为直接寻址。如图 3-3 所示,3200H 单元存储的数据为 F8H,3201H 单元存储的数据为 03H,上面的指令执行后 BX=03F8H。
注意:BX 为 16 位的寄存器,决定了这条指令为 16 位的数据传送指令。
汇编语言中常常用一个符号代替数值,如 BUFF 代替 3200H,则上述指令可写为:
MOV BX, [BUFF] ;或写为 MOV BX, BUFF
BUFF 称为符号地址,它的寻址方式仍为直接寻址方式。BUFF 需要在程序开始处予以定义。 8088/8086CPU 采用分段的方式管理内存储器,CPU 读写内存时使用段基址和段内偏移地址,段基址指明段的起始位置,段内偏移地址指明相对于段起始地址的位移量,通常将段基址和段内偏移地址称为逻辑地址。汇编指令中存储器操作数的地址都是逻辑地址,例如上面指令中的[2000H]和[3200H],都是段内偏移地址,它们的段基址由 DS 指明。在通常情况下,存储器操作数的默认在数据段,段基址在 DS。在特殊说明的情况下,存储器操作数的段基址也可以替换为 CS、ES 或 SS。表 3-1 说明了 8088/8086CPU 系统中逻辑地址的来源。
表 3-1 8088/8086CPU 系统中逻辑地址的来源
CPU 执行指令时,由段基址和偏移地址产生 20 位的物理地址,从中取出操作数。例如:
MOV BL, [2000H]
设 DS=1200H,上面指令的执行过程为:CPU 通过内部的地址加法器,由逻辑地址计算得到 20 位的物理地址:1200HX10H+2000H=14000H,从 14000H 内存单元取出 55H 传送给 BL 寄存器。
如果操作数的段基址不是 DS 段,指令要特别说明。例如在 ES 段,指令应书写为:
MOV BL, ES:[2000H]
这种用法称为段超越,物理地址为 ESX10H+2000H。
3. 寄存器寻址
操作数在 CPU 内部的寄存器中,例如:
MOV BL, [2000H] ;操作数 BL 的寻址方式为寄存器寻址。
ADD AX, BX ;源和目的操作数的寻址方式都是寄存器寻址。
4. 寄存器间接寻址
操作数在内存中,内存单元的偏移地址存放在寄存器中。例如:
MOV AX, [SI];操作数[SI]的寻址方式为寄存器间接寻址。
上述指令的功能为:SI 的内容为内存单元的偏移地址,DS 为段基址,以 DSX10H+SI 为首地址,取出连续两个内存单元的数据传送给 AX。如果 DS=2000,SI=02H,参照图 3-4,上面指令的执行结果为 AX=C396H
再如:
MOV DX, [DI] ;将 DS:[DI]指明的连续两个内存单元的数据传送到 DX。
MOV [BX], AX ;AX 的内容传送到 DS:[BX]指明的连续两个内存单元中。
MOV CX, [BP] ;将 SS:[BP]指明的连续两个内存单元的数据传送到 CX。
操作数[DI]、 [BX]、 [BP]的寻址方式都是寄存器间接寻址。8086CPU 中能够作为寄存器间接寻址方式使用的寄存器只有 4 个:BX、BP、SI、DI。这 4 个寄存器在作为间接寻址使用时,要用[ ]申明,这时常称为它们为地址指针或间址寄存器。BP 在作为间址寄存器时,段基址默认为 SS;其它 3 个的默认段基址为 DS。都可以段超越。例如:
MOV DX, ES:[DI]
上述指令的功能为:从物理地址为ESX10H+DI的内存单元取出两个字节的数据传送给DX。
5. 寄存器相对寻址
操作数在内存中,内存单元的偏移地址一部分由间接寻址寄存器提供,一部分是指令给定的 8 位或 16 位地址位移量,二者相加形成操作数的有效地址。例如:
MOV AX, [BX+DATA];将以BX+DATA为首地址的连续两个内存单元的数据传送给 AX。
MOV AL, [SI+20H];将以SI+20H为地址的内存单元的数据传送给AL。
MOV CX, [DI+DATA];将以DI+DATA为首地址的连续两个内存单元的数据传送给 CX。
MOV DX, [BP+DATA];将以BP+DATA为首地址的连续两个内存单元的数传送;给 DX。
上述指令的书写格式很灵活,也可以如下书写:
MOV AX, [BX]+DATA
MOV AL, 20H [SI]
MOV CX, DATA [DI]
MOV DX, DATA+ [BP]
寄存器相对寻址方式对寄存器的要求与寄存器间接寻址一样,只有 4 个寄存器:BX、 BP、SI、DI 可以使用,并且要用[ ]申明。BP 在寄存器相对寻址时,段基址默认为 SS,其它 3 种情况段基址默认为 DS。
这种寻址方式可用于存取数据表中的数据,用间址寄存器存放数据表首地址,地址位移量指明要存取表中的哪一个数据,可以方便地存取数据表中的任何数据。
6. 基址变址寻址
操作数在内存中,基址寄存器和变址寄存器相加作为操作数的偏移地址。例如:
MOV AX, [BX][SI];将 BX+SI 为首地址的连续两个内存单元的数据送给AX。
MOV CX, [BP][DI];将 BP+DI 为首地址的连续两个内存单元的数据送给CX。
8086CPU 中寄存器 BX 和 BP 为基址寄存器,SI 和 DI 为变址寄存器。这种寻址方式中,一个基址寄存器加一个变址寄存器构成操作数,操作数的形式只有 4 种:
[BX][SI]
[BX][DI]
[BP][SI]
[BP][DI]
这种寻址方式里段基址是 DS 还是 SS 呢?8086 汇编规定以基地址为主,如果基址寄存器为 BP,则操作数的段基址默认由 SS 提供;若 BX 为基址寄存器,则段基址默认由 DS 提供。例如:
MOV AX, [BX][DI];源操作数的物理地址为:DSX10H+BX+DI
MOV [BP][SI],DX;目的操作数的物理地址为:SSX10H+B+SI
基址变址寻址方式要求必须一个基址和一个变址组合,不允许两个基址或两个变址组合。下面指令是错误的。
MOV AX, [BX][BP]
MOV AX, [SI][DI]
基址变址寻址方式的主要用途是寻址存储器数组中的元素。将数组的首地址装入基址寄存器,而把要存取的元素的序号存入变址寄存器中,通过修改变址寄存器的内容来访问数组中的各个元素,它比寄存器相对寻址更加灵活。
7. 基址变址相对寻址
操作数在内存中,操作数的地址由基址寄存器加上变址寄存器再加上地址位移量构成。如果基址寄存器为 BP,则段基址默认由 SS 提供;若 BX 为基址寄存器,则段基址默认由
DS 提供。例如:
MOV AX, DATA[BX][SI]
MOV AX, [BP][DI] DATA
指令也可以书写为:
MOV AX, [DATA+BX+SI]
MOV AX, [DATA+BX][SI]
MOV AX, DATA[BX+SI]
这种寻址方式主要用于二维数组操作。地址位移量作为数组首地址,基址寄存器寻址行,变址寄存器寻址列,可以很方便实现数据阵列检索。
基址变址相对寻址方式要求必须一个基址和一个变址组合,不允许两个基址或两个变址组合。
8. 隐含寻址
操作码隐含地指明操作数的地址,例如乘法指令、字符串操作指令。
MUL BL;AL乘以BL,结果存在 AX 中。
MOVSB;把 DS:SI 指明的内存单元的数据传送到 ES:DI 指明的内存单元中。
3.2 数据传送类指令
- 数据传送指令是汇编语言中使用最频繁的指令,通常细分为通用数据传送指令、端口输入输出指令、地址传送指令和标志寄存器传送指令。
3.2.1 通用数据传送指令
(1)MOV
指令格式:MOV dst, src
功能:数据传送,把 src 的内容传送到 dst 中。
说明:把源操作数复制到目的操作数中,它可以实现:
① 立即数到通用寄存器的数据传送
例如:
MOV AL, 4 ;AL=4
MOV AX, 1000H ;AX=1000H
MOV SI, 037BH ;SI=037BH
但是: MOV DS, 2000H ;语法错误,不能用立即数给段寄存器赋值。
应该为:
MOV AX, 2000
MOV DS, AX
② 立即数到存储单元的数据传送
例如:
MOV WORD PTR[DI], 2000H
将立即数 2000H 传送到内存单元,内存单元的地址以间接寻址的方式由 DI 提供。
PTR 是属性运算符,功能为修改操作数的类型。WORD PTR 的作用是将操作数的类型设置为字类型,BYTE PTR 将操作数的类型设置为字节类型。
例如:
MOV BYTE PTR[SI], 4AH
将立即数 4AH 传送到内存单元,内存单元的地址以间接寻址的方式由 SI 提供,传送一个字节。
MOV [DI], 04AH ;语法错误:源和目的操作数的类型都不确定。
这条指令不可用,因为源和目的操作数的类型都不确定,指令执行结果也不确定,这种情况称为二异性。指令执行时可能传送一个字节,将立即数 4AH 传送给[DI]指明的内存单元,也可能传送两个字节,4A 赋给内存单元[DI],0 赋给[DI+1]。
MOV 指令中的两个操作数的类型必须至少有一个是确定的,另一个依附这一个。属性运算符 PTR 帮助我们确定存储器操作数的类型。
③ CPU 内部寄存器之间的数据传送
例如:
MOV AL, BL ;BL 传送给 AL,传送一个字节。
MOV AX, BX ;BX 传送给 AX,传送一个字。
MOV DS, AX ;给数据段寄存器赋值。
MOV SI, BP ;BP 传送给 SI,传送一个字。
④ 寄存器与存储单元之间的数据传送
例如:
MOV AL, [2000H] ;将 2000H 单元的内容传送给 AL,传送一个字节。
MOV AX, [SI] ;将以 SI 为首地址的连续两个内存单元的数据传送给AX。
MOV [3200H], CX ;将CL 存入3200H单元,将CH存入3201H单元。
MOV ARRY[DI], DL ;将 DL 的内容存入 ARRY+DI 的内存单元中。
MOV DL, [BX][SI] ;将 BX+SI 内存单元的数据传送给 DL。
⑤ 存储单元之间的数据传送
不能用一条 MOV 指令实现内存单元之间的数据传送。8086 汇编语法规定 MOV 令中两个操作数不能同时为存储器操作数。要实现存储单元之间的数据传送,需要两条指令。例如:
MOV [DI], [SI] ;语法错误。
应该为:
MOV AX, [SI]
MOV [DI], AX
使用 MOV 指令时注意:
① MOV 指令不影响标志寄存器的任何标志位。
② 源和目的操作数不可同时为存储器操作数。
③ 源和目的操作数必须等长,即同时为字节类型或字类型。
④ 不能用立即数给段寄存器赋值。
⑤ 不允许给 CS 赋值。
⑥ MOV 指令不能访问 IP 和 Flags
(2)PUSH,POP
-
PUSH 和 POP 是堆栈操作指令助记符。堆栈是程序在内存中开辟的一个数据区,用以
保存寄存器或存储器中暂时不用而又必须保存的数据。程序中堆栈是用段定义语句在内存中定义的一个堆栈段,堆栈段的段基址存放在 SS 寄存器,段内偏移地址存放在 SP 寄存器中,SP 也常称为堆栈指针,它总是指向栈顶。堆栈是一种线性表,只在栈顶(低地址端)进行输入输出操作。CPU 对堆栈的操作采
用先进后出(或后进先出)存取方法。CPU 把数据存入堆栈称为压入堆栈 PUSH从堆栈中取出数据称为弹出堆栈 POP。压入堆栈时堆栈增长,堆栈指针 SP 减小,向低地址方向移动;弹出堆栈时堆栈减小,堆栈指针 SP 增大,向高地址方向(底)移动。
指令格式:PUSH src ;压栈指令
POP dst ;出栈指令 -
PUSH 指令把操作数压入堆栈,执行过程为:
① src 的高 8 位存入[SP-1],src 的低 8 位存入[SP-2] ② SP-2 送 SP
-
例如:
设 SS=2000H,SP=102H,AX=623EH,执行下面指令后:PUSH AX AX 的数据 62H 存入 20101H 单元,3EH 存入 20100H 单元,SP=0100H
-
POP 指令把操作数弹出到 dst 中,执行过程为:
① 把[SP]的内容弹出到 dst 的低 8 位,把[SP+1]的内容弹出到 dst 的高 8 位
② SP+2 送 SP
例如:设 SS=2000H,SP=102H,执行下面指令后:POP AX SS=2000H,SP=104H,AX=79FFH
-
堆栈操作指令属于单操作数指令,操作数可以是寄存器,也可以是存储器。
堆栈指令的操作数必须是字类型,可以是 16 位的通用寄存器或段寄存器,也可以是两个连续的内存单元,可以采用任何寻址方式。
8086CPU 不允许立即数作为堆栈操作的操作数,CS 不能作为出栈指令的操作数.堆栈指令不影响任何标志位。
例如:PUSH DI PUSH DS PUSH CS PUSH WORD PTR[1000H] PUSH WORD PTR[SI] PUSH WORD PTR[BP+6] POP SI POP DS POP WORD PTR[1000H] POP WORD PTR[SI] POP WORD PTR[BX+DI]
-
堆栈操作常用来传递函数的参数。程序中压栈操作和出栈操作常常成对出现,以保持堆栈的平衡。
-
堆栈初始化
堆栈初始化应该加载堆栈段寄存器 SS 和堆栈指针寄存器 SP。通常把堆栈段的栈底地址装入 SS。例如,如果堆栈段位于存储单元 20000H~2FFFFH 处,则应该设置 SS=2000H, SP=0000H。这样 SP 实际指向 2FFFFH,因为操作时首先 SP 减 1,然后才把第一个字节压栈。所有的段都是自然循环的,段的顶部就是段的底部。
-
汇编语言中堆栈段的设置是由汇编(MASM)和连接程序(LINK)自动设置的,除非有特殊需要改变这些初始化值,否则不必要加载 SS 和 SP
(3) 数据交换指令
-
XCHG
-
本指令的功能是将源操作数与目标操作数相互对应交换位置,交换可以在通用寄存器与累加器之间,通用寄存器之间,通用寄存器与存储器之间.但是不能在两个存储单元之间交换,段寄存器与IP也不能左为源或者目标操作数.
-
例:
XCHG AX,[SI+0400H]
-
设当前CS=1000H,IP=0064h,DS=2000H,AX=1234H,执行该指令后(假设之前[SI+0400H]内为ABCDH),AX=ABCDH;(23400H)=34H,(23401H)=12H
(4) 字节翻译指令
- XLAT
- 字节翻译指令又称为代码转换或查表指令,它特别适合于不规则代码的转换
- 该指令通过查表方式完成代码转换功能,执行操作是:AX←[BX+AL].执行结果是将带转换的序号转换成对应的代码,具体转换步骤如下:
- 建立代码表,将该表定位到内存中某个逻辑段的一片连续地址中,并将表的首地址的偏移地址置入BX
- 将带转换的一个十进制数在表中的序号(又称索引值)送入AL寄存器中.该值实际上就是表中某一项与表格首地址之间的位移量
- 执行XLAT命令