第八章 8086指令系统
我们把汇编指令称为机器语言的指令助记符,每一条汇编指令都对应一条机器指令。X86 CPU厂商AMD和INTEL提供硬编码表。编译器或者调试器就是通过查表的方式,将汇编指令翻译成机器指令,或者将机器指令反编译成汇编指令。
■指令分组
(1)数据传送指令;
(2)算术运算指令;
(3)逻辑运算指令;
(4)串操作指令——第十七章字符串处理详细讲述;
(5)程序控制指令,又称为转移指令;
(6)处理器控制指令;
(7)宏命令——自定义指令,第二十二章宏命令详细讲述。
■指令表示格式
[标号:] 指令助记符 [操作数1 [,操作数2]] [;注释]
指令是否带操作数,取决于指令本身,可以没有操作数,也可以是一个、两个或三个操作数。最多就是三个操作数了。
标号的使用取决于程序的需要。注意,这里的标号是代码段的地址标号,为了和数据段地址标号区别,代码段地址标号后面多了一个“:”。
■注释
16位汇编指令中的注释使用“;”,后面添加注释内容,注释可以增强代码的可读性。我们需要养成良好的习惯,记得在源代码中添加注释。
■其他说明
对于每一条指令,程序员需要注意以下几点:
(1)指令的功能;
(2)指令操作数的寻址方式;
(3)指令对FLAG寄存器标志位的影响;
(4)指令的长度和执行时间;
8.1 数据传送指令
本节内容:数据传送指令,不影响标志位。
■传送指令:MOV指令,包含目的操作数和源操作数,功能为获取源操作数的值并送入目的操作数。操作数数据宽度必须一致,不能同时为内存操作数和段寄存器。CS不能做目的操作数,IP不能做目的操作数或源操作数。立即数不能做目的操作数。
■交换指令:XCHG指令,交换目的操作数和源操作数的值,为MOV指令的复合指令,使用规则与MOV指令相同。
■地址传送指令:
●LEA指令的功能是获取内存操作数的地址并送入目的操作数。汇编语言中通过指令区分获取地址还是获取该地址处的值。
●LDS指令:将32位地址指针中的高16位送入DS,低16位送入目的操作数(指针寄存器)。
●LES指令:将32位地址指针中的高16位送入ES,低16位送入目的操作数(指针寄存器)。
■堆栈操作指令:
●PUSH指令:将16位操作数压入堆栈。
●POP指令:将16位操作数弹出堆栈。
■标志操作指令:
●LAHF指令:把标志寄存器的低8位(SF、ZF、AF、PF、CF)传送至AH寄存器的指定位。
●SAHF指令:和LAHF相反,把寄存器AH的指定位送至标志寄存器低8位。不影响高8位的OF,DF,IF,TF标志。
●PUSHF指令:把FLAG标志寄存器的内容压入堆栈。
●POPF指令:把当前堆栈顶的一个字传送到标志寄存器。
■标志位操作指令:
●CLC指令:CF位清零。
●STC指令:CF位置1。
●CMC指令:CF位取反。
●CLD指令:DF位清零。
●STD指令:DF位置1。
●CLI指令:IF位清零。
●STI指令:IF位置1。
8.1.1 传送指令
■MOV指令
指令格式:MOV DST(目的操作数) ,SRC(源操作数)
MOV指令把一个字节或一个字从源操作数SRC传送至目的操作数DST中。源操作数可以是通用寄存器、内存存储单元或立即数。目的操作数可以是通用寄存器或内存存储单元。
如图8-1所示,MOV 指令的操作数可以是如下组合:
MOV AX,1 ;16位立即数送入16位通用寄存器
MOV BYTE PTR [SI],1 ; 8位立即数存入8位(BYTE PTR)内存操作数
MOV AX,BX ;16位通用寄存器
MOV AL,BL ; 8位数据寄存器
MOV AX,[SI] ;16位内存操作数
MOV WORD PTR [DI],AX ;16位寄存器存入16位(WORD PTR)内存操作数
MOV DS,AX ;16位通用寄存器存入16位段寄存器
MOV AX,SS ; 16位段寄存器存入16位通用寄存器
MOV WORD PTR [SI],CS ;16位段寄存器存入16位内存操作数
MOV DS,[SI] ;16位内存操作数存入16位DS段寄存器
MOV DS,1 ;错误,立即数不可以直接存入段寄存器,可以借助通用寄存器间接存入
MOV AX,1 ;正确
MOV DS,AX ;正确
图8-1 MOV指令数据传送方式
●CPU内部寄存器之间的数据传送
mov ah,al
mov dl,bh
mov bp,sp
mov ax,cs
mov ds,bx
mov ds,1234h ;错误
mov cs,1234h ;错误
mov ax,1234h ;正确
mov bx,ip ;错误
mov ds,ax
mov es,ax
注意
1.源操作数和目的操作数不能同时为段寄存器;
2.CS不能为目的操作数;
3.IP寄存器不能为源操作数,也不能做目的操作数,这种例外永远存在;
4.源操作数和目的操作数的数据宽度必须一致;
5.MOV指令不影响标志位;
6.不能同时为存储器操作数或段寄存器,需借助其它通用寄存器;
mov bx,[si]
mov [di],bx
mov ax,cs
mov ds,ax
7.立即数不能直接传送到段寄存器,需借助其他通用寄存器;
mov ax,5000h
mov ds,ax
8.立即数永远不能做目的操作数。
●立即数送至通用寄存器或存储单元(各种存储器寻址方式)
mov bl,4 ;十进制数
mov di,-5 ;十进制数
mov varb,-1 ;varb表示变量名,代表一个存储单元
mov varw,1234h ;varw是一个字变量
mov word ptr [si],5678h ;需要指定内存操作数的数据宽度
●寄存器与存储器之间的数据传送
mov ax,varw ;varw为字变量,存储器操作为直接寻址
mov bh,[si] ;存储器操作数为寄存器间接寻址
mov di,es:[di+3] ;存储器操作数为相对变址寻址,使用段超越前缀
mov dx,[bx+si+3] ;存储器操作数为相对基址加变址寻址
mov [bp],ax ;默认使用SS段寄存器
mov varb,bl ;varb是一个字节变量
mov ds:[bp],dl ;使用DS段超越前缀
mov varw,es ;varw是字变量
mov cx,varw ;将变量varw的值送入cx
思考
在MOV指令中:
1.为什么CS不能做目的操作数?
2.为什么IP不能做源操作数?
8.1.2 交换指令
■XCHG指令
利用交换指令可方便地实现通用寄存器与通用寄存器或存储单元之间的数据交换。
指令格式:XCHG OPRD1,OPRD2。
将操作数1的内容与操作数2的内容互换。操作数同时是字节或字。
例:
xchg al,ah
xchg si,di
注意
1.OPRD1和OPRD2可以是通用寄存器和存储单元。
2.OPRD1和OPRD2操作数不可以是段寄存器。
3.OPRD1和OPRD2不能同时是存储单元。可采用各种寻址方式来指定存储单元。
xchg [si+1],al
xchg [si+di+3],bx
4.OPRD1或OPRD2不能是立即数。
5.XCHG指令不影响标志位。
8.1.3 地址传送指令
■LEA指令:传送有效地址指令
指令格式:LEA REG,OPRD
将操作数OPRD的有效地址传送到操作数REG。操作数OPRD必须是一个存储器操作数,操作数REG必须是一个16位通用寄存器。
lea ax,buff ; buff为变量名,将buff地址标号送入ax寄存器
lea dx,[bx+1] ;将地址标号bx+1送入dx寄存器
lea si,[bx+di+4] ;将地址标号bx+di+4送入si寄存器
注意
1.LEA指令和MOV指令有本质的区别:
假设buffer变量的偏移是1234h,该字变量的值为5678h。
LEA指令取地址:lea ax,buffer ;执行后,ax=1234h。
MOV指令取值:mov ax,buffer ;执行后,ax=5678h。
在16位汇编语言中:
如果取内存操作数的值,则使用MOV指令;
如果取内存操作数的地址,则使用LEA指令;
2.LEA指令不影响标志位。
3.LEA指令还有一种用法,LEA AX,[1+2+3];AX=6,编译器会计算方括号内常量表达式的值,相当于做了一个简单的加法运算。
■LDS指令:段值和段内偏移构成32位的地址指针。该指令传送32位地址指针。
指令格式:LDS REG,OPRD。
LDS指令将操作数OPRD中所含的一个32位地址指针的16位段值部分送到数据段DS,16位偏移部分送入REG寄存器。操作数OPRD必须是一个32位的存储器操作数,REG可以是一个16位的通用寄存器,实际往往是变址寄存器或基址寄存器。
lds di,[bx] ; [bx]是一个32位内存操作数,高16位为段值,低16位为偏移。
lds si,farpointer ;farpointer是一个32位双字变量。
■指令LES
LES指令也是传送32位地址指针。
指令格式:LES REG,OPRD。
LES指令将操作数OPRD中所含的一个32位地址指针的段值部分送到附加段ES,偏移部分送入REG寄存器。其他与LDS指令相同。
如图8-2所示,原数据段的逻辑地址为076A:1234H,对应的物理地址为088D4H。执行LDS SI,DWORD PTR [1234H]后,数据段DS的值为1234H,SI寄存器的值为5678H,逻辑地址变为1234: 5678 H,对应的物理地址为179B8H。
图8-2 LDS指令执行示意图
8.1.4 堆栈操作指令
堆栈是一段RAM内存空间,栈底一端地址较大,栈顶一端地址较小,入栈时,由高地址到低地址,出栈时,方向相反。段值为SS寄存器,SP始终指向栈顶。堆栈内存空间16位对齐。
堆栈内数据存取原则为后进先出的原则,参见图6-11 堆栈存储数据规则演示。
■堆栈用途
(1)现场和返回地址保护;
(2)寄存器内容的保护;
(3)传递参数;
(4)存储局部变量
■进栈指令PUSH:把16位数据压入堆栈
指令格式:PUSH SRC
PUSH指令把源操作数SRC压入堆栈。先把栈顶指针SP减2,然后把源操作数SRC送入SP指向的栈顶。
源操作数可以是通用寄存器或段寄存器,也可以是字存储单元,源操作数不可以是立即数。如果将8位寄存器或内存操作数入栈,必须先将其扩展为16位操作数,然后再入栈。
PUSH SI
PUSH DS
PUSH VARW ;字变量
PUSH [SI]
如图8-3所示:
假设AX=1234H
PUSH AX
■出栈指令POP
POP DST
POP AX
图8-3 进栈出栈操作示意图
注意
1.8086 CPU不可以PUSH立即数,如 push 1;错误。80386 PUSH指令支持立即数操作数。
2.堆栈指令不影响标志位。
8.1.5 标志操作指令
■标志传送指令
●LAHF指令:LAHF
如图8-4所示,该指令把标志寄存器的低8位(SF、ZF、AF、PF、CF)传送至AH寄存器的指定位。
●SAHF指令:SAHF
该条指令和LAHF相反,把寄存器AH的指定位送至标志寄存器低8位。不影响高8位的OF,DF,IF,TF标志。
8-4 LAHF指令示意图
●PUSHF指令:PUSHF
该条指令把标志寄存器的内容压入堆栈,即先把堆栈指针寄存器SP的值减2,然后把标志寄存器的内容送入由SP所指的栈顶。
●POPF指令:POPF
该条指令把当前堆栈顶的一个字传送到标志寄存器,同时相应地修改堆栈指针,即把SP加2。
PUSHF指令和POPF指令一起使用,起到保护和恢复标志寄存器的作用。
思考
如何利用PUSHF和POPF指令改变TF追踪标志的状态?
■标志位操作指令
●清进位标志CF指令CLC
格式:CLC;功能:CF置0。
●STC指令
格式:STC;功能:CF置1。
●CMC指令
格式:CMC;功能:进位标志CF取反。
●清方向指令CLD
格式:CLD;功能:DF置0。
●STD指令
格式:STD;功能:DF置1。
●CLI指令
格式:CLI;功能:IF置0。
●STI指令
格式:STI;功能:IF置1。
练习
1、8086的指令集可分为哪些子集?
2、通常情况下源操作数和目的操作数不能同时为存储器操作数。请给出存储器操作数甲送到存储器操作数乙的三种方法。
3、如何实现代码段与数据段相同?请编写一个将数据段和代码段合并成一个段的源程序,并验证其正确性。
4、请使用一条指令将(BX +123)的和送入AX寄存器。
5、请比较如下指令片段并添加注释:
LDS SI,[BX] MOV SI,[BX] MOV DS,[BX+2]
MOV DS,[BX+2] MOV BX,[BX]