目录
第一遍学指令系统只关注指令的表层作用,没关注到指令深层作用——设置标志位。
4.1 指令系统概述
4.1.1 指令的基本概念
指令:是指示计算机进行某种操作的命令。
指令是用户使用与控制计算机运行的最小功能单位。
指令与计算机的硬件结构直接相关,不同的CPU能够执行的指令种类、数量都不同。
程序:是能够完成一个完整任务的一系列有序指令的集合。
指令集(指令系统):一台计算机所能执行的全部指令的集合。
系列计算机:每种计算机都有自己固有的指令系统,基本指令系统相同、基本体系结构相同的一系列计算机,称为系列计算机。
例如x86、ARM、MIPS系列计算机等。同一系列的计算机的指令系统是向上兼容的。
机器指令(指令字、二进制指令):计算机能够直接理解和执行的指令,称为机器指令,也称为指令字。是用二进制编码来表示的,所以也叫二进制指令。
汇编指令:由于二进制编码不易理解,也不便于记忆和书写,因此,人们就用助记符来代替二进制指令,这就形成了汇编指令。
助记符:汇编指令中的助记符通常用英文单词的缩写来表示。
例如:加法用ADD、减法用SUB、传送用MOV等。
Intel 8086 CPU中的SUB指令的各种形式如表4.1所示。
在表4.1所示的汇编指令中,DEC是指令助记符,BX是操作数。
汇编指令就是这样用助记符和操作数来表示的指令直观、方便,又好理解。这些符号化的指令使得书写程序和修改程序变得简单方便了,但计算机不能直接识别和执行。
汇编程序:在把汇编指令交付给计算机执行之前,必须通过汇编程序翻译成计算机能够识别的机器指令。
汇编指令与机器指令是一一对应的。
4.1.2 指令格式
机器指令的组成部分:操作码字段、操作数字段。
如图4.1所示。
操作码部分:规定指令所执行的操作;
操作数部分:也称为地址码部分,这部分可能直接给出参与运算的操作数,也可能是描述操作数地址的信息。
-----------------------------------------------------------------------------------------------------------------------------
指令字长度:一个指令字中包含二进制代码的位数,称为指令字长度。
机器字长:是指计算机能直接处理的二进制数据的位数,它决定了计算机的运算精度。
(机器字长==系统总线中数据总线的线数)
单字长指令:指令字长度 == 机器字长的指令。
半字长指令:指令字长度 == 半个机器字长度的指令。
双字长指令:指令字长度 == 两个机器字长度的指令。
------------------------------------------------------------------------------------------------------------------------------
等长指令字结构:在一个指令系统中,各种指令字长度是相等的。
变字长指令字结构:各种指令字长度随指令功能而异,有的指令是单字长指令,有的指令是双字长指令。
Intel 8086的指令采用变字长格式,指令由1~6字节组成。
第1字节至少包含操作码,大多数指令的第2字节表示寻址方式;
第3~6字节表示一个或两个操作数。
4.1.3 8086汇编语言格式
Intel 8086 CPU指令系统有133指令。
每条指令最多由四部分组成:标号、操作码、操作数、注释。
8086指令的一般格式为:
标号: 操作码 操作数; 注释
//其中,标号和注释都是可选项。
标号:表示该指令在代码段中的偏移地址;同时也给到该指令一个名称。
注释:对该指令进行说明,不参加指令的执行。
根据指令中所含操作数的个数,8086指令可划分为:双操作数指令、单操作数指令、无操作数指令。
(1)双操作数指令
大多数指令需要两个操作数:源操作数、目标操作数。
指令运算结果存入目标操作数的地址中,操作数中原有数据被取代。
格式为:
操作码 DST, SRC
//其中,DST为目标操作数;SRC为源操作数。
需要特别提出的是,8086系统规定:
对于双操作数指令,必须有一个操作数存放于寄存器中,不能两个操作数同为存储器操作数。
示例:MOV [BX],[SI]××××非法指令××××
这个规定适合于串操作指令外的所有双操作数指令。
(2)单操作数指令
指令中只给出一个操作数。有以下两种形式:
① 有些指令确实只需要一个操作数。(如自增指令 INC)
② 有些指令中隐含了一个操作数。(如乘法指令MUL)
单操作数指令给出的单操作数:
① 有的是作为目标操作数;
② 有的是作为源操作数——>其运算结果存放于隐含的指定目标操作数中。
格式为:
操作码 DST/SRC
(3)无操作数指令
指令中不给出操作数的地址。有以下两种形式:
① 有些指令确实不需要操作数。(如停机指令 HLT)
② 有些指令隐含了操作数。(如标志寄存器传送指令LAHF和 SAHF 、换码指令XLAT等)
格式为:
操作码
例如:
HLT ;停机指令,是无操作数指令
XLTA
4.2 8086寻址方式
程序执行过程中,程序中的指令和数据都存储在内存储器中。
程序执行时,处理器首先根据指令地址访问相应的内存单元,取出指令代码,CPU再根据指令代码中的操作数字段,取出操作数,去执行响应的操作。
寻址方式:形成指令地址或操作数地址的方式。
8086CPU的寻址分为两类:指令寻址方式、数据寻址方式。
4.2.1 数据寻址方式
数据寻址方式:形成操作数地址的方法。
计算机中操作数的位置有以下几种情况:
① 操作数包含在指令中。这样的操作数称为立即数,相应的寻址方式称为立即寻址方式。
② 操作数在CPU内部寄存器中。相应的寻址方式称为寄存器寻址方式。
③ 操作数在内存储器中。需要先计算操作数在内存中的物理地址才能对它进行存取操作。相应的寻址方式称为存储器寻址方式。
④ 操作数在I/O端口寄存器中。相应的寻址方式称为I/O端口寻址方式。
(I/O端口寄存器中的数据,可能是外设输出的,给到CPU读入;可能是CPU输出的,给到外设读入)
(1)立即寻址
指令的操作数(地址码)字段直接给出操作数本身,这种寻址方式称为立即寻址方式。
这个操作数称为立即数。
其汇编语言格式为:
操作码 DST,数字表达式
这个数字表达式的值可以是一个8位整数,也可以是一个16位整数。例如:
MOV AX, 251 ;将十进制数251送入寄存器AX,251是立即数——存储十进制数的二进制码
MOV AL,'5 ' ;将'5'的 ASCII 码送入寄存器 AL,'5'是立即数——存储字符的ASCII码
MOV AL, OE8H ;将8位二进制数0E8H送入寄存器AL,0E8H是立即数——存储十六进制数的二进制码
MOV AX,2346H ;将16位二进制数2346H送入寄存器AX, 2346H是立即数——存储十六进制数的二进制码
立即寻址方式的主要应用场景:给寄存器、存储单元赋值。
因此这种寻址方式不能用于单操作数指令;
在双操作数指令中,立即数也只能用于源操作数,不能用于目标操作数。
立即寻址方式的指令寻址过程如图4.2所示。
由于立即寻址方式中操作数直接从指令中取得,因此指令执行速度快。
在立即寻址方式中,操作数是指令的一部分,不能修改,因此,立即寻址方式只能适用于操作数固定的情况。
而很多情况下,指令所处理的数据都是在不断变化的,这就需要引入其他寻址方式。
(2)寄存器寻址
操作数在CPU内部的通用寄存器中,指令中给出寄存器名称,这种寻址方式称为寄存器寻址。
(机器指令中为寄存器的二进制编号)
其汇编语言格式为:
操作码 DST, SRC ;DST与SRC其中一个或两个同时为寄存器名
其中源和目的操作数可以使用以下寄存器,使用哪个寄存器取决于正在执行的指令。
① 8个16位通用寄存器:AX、BX、CX、DX、SI、DI、BX、BP。
② 8个8位通用寄存器:AH、AL、BH、BL、CH、CL、DH、DL。
③ 段寄存器:CS、DS、SS、ES。
④ FLAGS标志寄存器。
复习:8086的14个4类16位寄存器:8+1+1+4
通用寄存器:数据寄存器、指针变址寄存器
专用寄存器:控制寄存器(IP(也算指针寄存器)、FLAGS)、段寄存器
所以8086其实有3个指针寄存器:BP、SP、IP。
可以看到,只有IP寄存器不能出现在指令中——CS可以作为源操作数,备份到目的操作数。
而CS和IP的赋值改变,都是通过隐含操作数指令——转移指令JMP、……,实现的。
例如:
MOV AX,CX ;将16位寄存器CX中的内容送入寄存器AX
MOV DL,BL ;将8位寄存器BL中的内容送入寄存器 DL
其中,AX、CX、DL、BL就是寄存器寻址方式。
寄存器寻址方式的指令寻址过程如图4.3所示。
这种寻址方式的优点是:寄存器数量一般在几个到几十个,因此所需地址码较短,从而缩短了指令
长度,节省了程序存储空间;另一方面,从寄存器里取数比从存储器里取数的速度快得多,从而提高了指令执行速度。
伯是,CPU内部寄存器数量有限,因此,在程序中所需变量较多的情况下,需要将操作数存放在内存储器中。
(3)存储器寻址
当操作数存放在内存储器中时,才会涉及到“地址”这个概念,而且8086是20位地址。
操作数的物理地址可由逻辑地址计算出来,即:
物理地址 = 段地址 × 10H + 段内偏移地址
段(基)地址:由段寄存器(CS、SS、DS、ES)给出。
不同指令有默认段寄存器,也可以显式给。
偏移地址:需要从指令地址码部分计算求得。
有效地址(Effective Address,EA):这个计算得到的偏移地址称为有效地址。
形式地址(位移量,DISP):指令地址码字段给出的地址。
存储器寻址方式——就是通过形式地址计算出操作数有效地址的过程。
形式地址(偏移量)——>有效地址(偏移地址)——>物理地址(段地址:偏移地址)
① 直接寻址
操作数的有效地址由指令地址码字段直接给出,这种方式称为直接寻址方式。
给出形式:标号、[标号]、[数字]
方括号表示存储器寻址。
即:
EA = DISP
其汇编语言格式可以表示为以下几种。
地址表达式
[地址表达式]
[数字表达式]
假设在数据段定义了一字节数组 TABLE,首地址在段内偏移地址1000H,则以下3条指令是等效的。
MOV AL, TABLE
MOV AL,[TABLE]
MOV AL,[1000H]
以下3条指令也是等效的。
MOV AL,TABLE+2
MOV AL, [TABLE+2]
MOV AL, [1000H+2]
直接寻址方式默认的段寄存器是DS(数据段)。
当CPU执行含有直接寻址方式的指令时,取出指令地址码字段的值,作为操作数的偏移地址,取出DS寄存器的值作为操作数的段地址,从而计算出操作数的20位物理地址,继而访问存储器得到操作数,如图4.4所示。
假设DS=3000H,(31000H)=12H,(31001H)=34H
(注:带圆括号的31000H表示该地址存储的内容为12H)
则指令
MOV AX,[1000H] ;物理地址 = DS × 10H + 1000H = 31000H
寻址过程如图4.5所示。
指令执行结束后,AX=3412H。
如果要访问其他段中的数据,则必须在指令中用段跨越前缀指出段寄存器名。
其汇编格式可以为以下4种形式之一:
段寄存器名:地址表达式
段寄存器名:[地址表达式]
段寄存器名:数字表达式
段寄存器名:[数字表达式]
即指定了“段寄存器名:”,数字表达式也可以不用方括号。
例如,假设TABLE是在附加数据段定义的一字节数组的首地址标号,其偏移地址为1000H。
则以下4条指令是等效的:
MOV AL, ES: TABLE
MOV AL, ES:[TABLE]
MOV AL, ES: 1000H
MOV AL, ES:[1000H]
表示将该字节数组中的第1个数组元素送入AL寄存器中。
以下4条指令也是等效的:
MOV AL, ES:TABLE+2
MOV AL, ES:[TABLE+2]
MOV AL, ES: 1000H+2
MOV AL, ES:[1000H+2]
表示将该字节数组的第3个数组元素送入AL寄存器中。
直接寻址方式简单、直观,指令执行速度快,适用于处理单个变量
② 寄存器间接寻址
操作数的有效地址被放在一个寄存器中,该寄存器由指令地址码字段指定。
这里就必须使用[ ]表示存储器寻址(寄存器间接寻址),除非指定段前缀。
可以使用的寄存器有——4个指针变址寄存器:
基址寄存器BX、
基址指针寄存器BP、
源变址寄存器SI、
目的变址寄存器DI。
其汇编语言格式为:
[BX]
[BP]
[SI]
[DI]
执行这种寻址方式的指令时,依据地址码字段的值访问指定寄存器,将其中的值作为操作数的偏移地址。根据段地址和偏移量计算操作数的20位物理地址,继而访问得到操作数。具体过程如图4.6所示:
重点1——默认段寄存器
[BX]、[SI]、[DI]——默认段寄存器DS,操作数默认在数据段中。
[BP]——默认段寄存器SS,操作数默认在堆栈段中。
重点1——指定段寄存器
此外,还可以通过指定段跨越前缀来取得其他段中的数据。
例如:
MOV AX,[BX] ;物理地址=DS × 10H + BX
MOV AL,[BP] ;物理地址=SS × 10H + BP
MOV AX, ES:[DI] ;物理地址=ES × 10H + DI
假设DS=3000H,BX=1010H,(31010H)=12H,(31011H)=24H,则指令
MOV AX,[BX] ;物理地址 = DS × 10H + BX = 31010H
寻址过程如图4.7所示:
指令执行结束后,AX=2412H。
寄存器间接寻址方式一般用于:访问表格(顺序表、链表)或字符串。
执行完一条指令后,通过修改SI、DI、BX 或BP的内容就可以访问到表格的下一数据项的存储单元。
③ 寄存器相对寻址
操作数的有效地址为基址或变址寄存器与一个位移量之和(或差)。
其中的寄存器和位移量均在指令地址码字段指定。
其汇编指令格式可表示为以下两种形式之一:
DISP [BX]
[BX ± DISP]
其中[BX]可以是[BX]、[BP]、[SI]、[DI]中的一个。
执行寄存器相对寻址方式的指令时,依据地址码字段的编号访问指定寄存器的值,将其与位移量相加(或相减),和(或差)作为操作数的偏移地址,计算得到操作数的20位物理地址,继而访问到操作数。
具体过程如图4.8所示:
重点1——默认段寄存器
[BX]、[SI]、[DI]——默认段寄存器DS,操作数默认在数据段中。
[BP]——默认段寄存器SS,操作数默认在堆栈段中。
重点1——指定段寄存器
此外,还可以通过指定段跨越前缀来取得其他段中的数据。
例如:
MOV AX,20H[SI] ;物理地址= DS×10H + SI + 20H(8位位移量)
MOV CL,[BP+2000H] ;物理地址= SS×10H + BP + 2000H(16位位移量)
MOV AX,STR[BX]
假设 TABLE是数据段中定义的一个字节数组的首地址标号(或变量名),它在数据段中的偏移地址为0100H。
若DS=2000H,SI=00A0H,(201A0H)=12H,(201A1H)=34H,则指令:
MOV AX,TABLE[SI] ;物理地址=DS×10H+SI = 20000H + 0100H + 00AOH = 201AOH
寻址过程如图4.9所示。
指令执行结束后,AX=3412H。
当然,也可用段跨越前缀重新指定段寄存器,例如:
MOV AL, ES:TABLE[SI] ;物理地址=ES×10H + TABLE + SI
这种寻址方式可用于:表格或数组数据的访问操作。使用时将表格或数组首址作为位移量,用寄存器记录下标,通过修改SI、DI、BX或BP的内容,就可以访问不同的数组元素。
④ 基址加变址寻址
操作数的有效地址是两个指定寄存器的值之和,
即 EA=[BX]/[BP]+[SI]/[DI]。
其汇编语言格式可表示为以下两种形式之一:
[基址寄存器名][变址寄存器名]
[基址寄存器名+变址寄存器名]
例如:
MOV AX,[BX][SI] ;物理地址=DS×10H + BX+SI
MOV AX,[BX+SI] ;物理地址=DS×10H + BX+SI
当机器执行这种寻址方式的指令时,依据地址码字段的值访问指定基址寄存器和变址寄存器的值,将其相加之和作为操作数的有效地址。具体过程如图4.10所示:
假设DS=2000H,BX=0500H,SI=0010H,(20510H)=12H,(20511H)=34H,则指令:
MOV AX,[ BX + SI] ;物理地址= DS×10H+ BX+SI=20000H + 0500H + 0010H = 20510H
寻址过程如图4.11所示:
指令执行结束后,AX=3412H。
重点1——默认段寄存器
[BX]——默认段寄存器DS,操作数默认在数据段中。
[BP]——默认段寄存器SS,操作数默认在堆栈段中。
重点1——指定段寄存器
此外,还可以通过指定段跨越前缀来取得其他段中的数据。
例如:
MOV AL,ES:[BX][SI] ;物理地址=ES × 10H + BX + SI
MOV AL,ES:[BX+SI] ;物理地址=ES × 10H + BX + SI
这种寻址方式也可以用于:表格或数组的访问。将表格或数组首地址存入基址寄存器,通过修改变址寄存器内容可访问到表格或数组的任一数据项的存储单元。
由于这种寻址方式两个寄存器内容都可修改,因此它比寄存器相对寻址更灵活。
⑤ 相对基址加变址寻址
存储单元的有效地址为一个基址寄存器、一个变址寄存器的内容及指令中指定的8位或16位位移量的和。
即EA=[BX]/[BP]+[SI]/[DI]+DISP。
其汇编语言形式可以为以下两种形式之一:
位移量[基址寄存器][变址寄存器]
[基址寄存器+变址寄存器+位移量]
以下3条指令是等价的:
MOV AL,TABLE[BX][SI]
MOV AL,TABLE[BX + SI]
MOV AL,[TABLE + BX + SI]
例如:
MOV AX, [BX+DI+20H] ;物理地址=DS×10H + BX + DI + 20H
MOV AX, ES:1000H[BP][SI] ;物理地址=ES×10H + BP + SI + 1000H
(4)I/O端口寻址
一个端口地址:地址本身为16位,地址指向的空间大小为8位(1字节),至多存放0FFH。
在8086指令系统中,访问I/O端口有专用的输入/输出指令——IN、OUT。
这两个指令对I/O端口的数据寻址可采用直接或间接方式。
① I/O直接端口寻址
端口地址以8位立即数方式在指令中直接给出。
I/O直接寻址方式所寻址的端口号范围:0~255——00H~0FFH。
例如,指令:
IN AL, n ;将n端口内容读入AL
IN AX, n ;将n端口内容读入AX(连续读取两个字节)
OUT n, AL ;将AL内容输出到n端口
OUT n, AX ;将AX内容输出到n端口(连续输出两个字节)
② 间接端口寻址
将8位或16位的I/О端口地址存在DX寄存器中,其中16位的I/O端口地址必须存放在DX寄存器中,
即通过DX寄存器间接寻址,可寻址的端口号范围为:0~65535——0000H~0FFFFH。
寄存器寻址:操作数是寄存器存储的数据。
寄存器间接寻址:操作数是寄存器内容地址对应的空间的数据。
例如,指令:
IN AL, DX ;将DX端口内容读入AL
IN AX, DX ;将DX端口内容读入AX(连续读取两个字节)
OUT DX, AL ;将AL内容输出到DX端口
OUT DX, AX ;将AX内容输出到DX端口(连续输出两个字节)
------------------------------------------------------------------------------------------------------------------------------
在8086 CPU中,I/O端口地址的位数取决于以下因素:
(1)独立编址方式
8086 CPU采用独立编址方式,I/O端口地址空间与内存地址空间是分开的。在这种方式下,I/O端口地址使用低16位地址线(A0~A15)进行寻址。因此,8086 CPU的I/O端口地址是16位的,可以寻址64K个I/O端口。
(2)指令集设计
8086 CPU的I/O操作通过`IN`和`OUT`指令实现。这些指令支持两种寻址方式:
• 直接寻址:使用8位立即数表示端口地址,但这种方式只能寻址0000H~00FFH范围内的端口。
• 寄存器间接寻址:将完整的16位端口地址存入DX寄存器,然后通过DX寄存器间接寻址。
(3)硬件设计限制
尽管8086 CPU的地址总线是20位的,但I/O端口地址仅使用低16位地址线(A0~A15),因此其I/O端口地址空间为16位。这种设计使得I/O端口地址空间独立于内存地址空间,避免了地址冲突。
------------------------------------------------------------------------------------------------------------------------------
4.2.2 指令寻址方式
指令寻址方式:是指确定下一条将要执行指令地址的方法。有顺序寻址方式、跳转寻址方式两种。
顺序寻址方式:由于指令地址在内存中顺序安排,当执行一段程序时,通常是一条接一条地顺序进行,这种顺序执行的过程,称为指令的顺序寻址方式。
8086中程序指令在代码段中,由CS寄存器指定段地址,IP寄存器指定偏移地址。
逻辑地址CS:IP所形成的物理地址就是指令的地址,CPU每取一个指令,IP自动+1,实现指令的顺序寻址。
跳转寻址方式:当程序执行的顺序发生转移时,指令的地址就采取跳转寻址方式。
跳转:是指将要执行的下一条指令的地址不是由当前IP+1顺序给出,而是由当前正在执行的指令给出。
程序跳转后,从新的指令地址开始顺序执行。因此,CS、IP寄存器的内容也相应改变,跟踪新的指令地址。
跳转寻址方式由程序控制类指令实现,如转移指令、子程序调用指令等。
程序控制类指令使程序转移到目标地址,从目标指令地址开始执行程序。目标指令地址既可以和程序控制指令在同一个逻辑段内,也可以在不同的逻辑段内。
--------------------------------------------------------------------------------------------------------------------------------
段内转移:在同一个逻辑段内的转移。
段间转移:在不同逻辑段的转移。
无论是段内转移还是段间转移,都有两种寻址方式:直接寻址、间接寻址。
(80x86有20根地址线,1M内存,逻辑段≤64KB,至少16段,只有4种逻辑段,每种不只1段)
(1)段内直接寻址
段内直接寻址方式:程序控制指令中直接指明了目标指令地址,且当前指令与目标指令地址在同一个代码段内,即只改变IP寄存器的值而不改变CS寄存器的值。
其汇编格式有如下3种形式:
指令名 SHORT 目标地址标号 ;加SHORT修饰,段内短转移(8位位移量)
指令名 NEAR PTR 目标地址标号 ;加……修饰,段内近转移(16位位移量)
指令名 目标地址标号 ;不加修饰默认,段内近转移(16位位移量)
最常用的就是不加修饰:指令名 目标地址标号——段内近转移(16位位移量)
汇编段内直接指令寻址方式时,汇编程序计算转移目标地址标号与IP寄存器当前值(本条指令的下一条指令的地址)的差值,将其补码作为位移量,写入指令的地址码字段。
位移量可以是一个带符号的8位数或16位数——当位移量为负时,表示向低地址转移;当位移量为正时,表示向高地址转移。
短转移:在汇编格式中,如果符号地址前加SHORT,则表示位移量被强制为8位,跳转范围为-128~+127,称为短转移;
近转移:如果符号地址前加 NEAR PTR,则表示位移量被强制为16位,跳转范围为-32768~+32767,即允许转移到当前代码段内的任何位置,称为近转移;
若什么都没加,默认为16位。
当执行这种寻址方式的转移指令时,机器取出位移量,与当前(IP)相加,将其和送入IP寄存器中,CS寄存器内容保持不变,从而实现指令的转移,如图4.12所示:
在这种寻址方式中,位移量不会随程序加载到不同的内存区域而改变,不管程序加载到哪段内存区域,都能够正确转移到目标地址。因此,使用这种转移指令可实现程序重定位。
需要注意的是:条件转移指令的位移量只能是8位,而无条件转移指令的位移量可以是8位,也可以是16位。
(2)段内间接寻址
在段内间接寻址方式中,指令中给出的不是转移目标地址本身,而是存放目标地址的单元。
由于是段内转移,指令与目标地址在同一个代码段中,存放目标地址的单元需要16位,用来存放目标地址的段内偏移地址。
既可以是一个16位的寄存器,也可以是内存单元中的一个字,其寻址方式可以是除立即数外的任何一种形式。
其汇编格式为:
指令名 16位寄存器名
指令名 WORD PTR 存储器寻址方式(当是字节地址)
指令名 存储器寻址方式(当是字地址)
汇编段内间接指令寻址方式时,汇编程序按格式中规定的寻址方式填写地址码字段。
当执行这种寻址方式的转移指令时,机器按照指令中规定的寻址方式寻址到一个字,然后把它送入IP寄存器中,CS寄存器的内容不变,从而实现转移,如图所示:
(3)段间直接寻址
在段间直接寻址方式中,要转向的目标地址与程序控制指令处于不同的代码段,指令中直接给出目标地址,此转移地址用地址标号或数值地址表示。
其汇编格式有以下两种形式:
指令名 FAR PTR 目标地址标号
指令名 段地址:段偏移地址
段间直接指令不仅改变IP寄存器的值,而且改变CS寄存器的值。
汇编这种指令寻址方式时,汇编程序将转移目标地址标号所在段的段地址及段内偏移地址值写入指令的地址码字段。
当执行段间直接寻址方式的转移指令时,机器取指令操作码(OP)之后的第1个字送入IP寄存器中,取操作码之后的第2个字送入CS寄存器中,从而实现转移,如图所示:
CS:IP——CS高位高地址
中断向量表那里,也是一样,CS:IP——CS高位高地址
(4)段间间接寻址
在段间间接寻址方式中,指令与转向的目标地址在不同的代码段,转移的目标地址放在存储器中,可以使用任何一种存储器寻址方式。
在段间间接转移中,不仅改变IP寄存器的值,而且改变CS寄存器的值,因此存放转向地址的单元必须是一个双字类型的变量,用来分别存放转向地址的偏移地址和段地址。
其汇编格式为:
指令名 DWORD PTR 存储器寻址方式
汇编这种指令寻址方式时,汇编程序按格式中规定的寻址方式填写地址码字段。当执行这种寻址方式的转移指令时,机器按照指令中规定的寻址方式寻址到存储器中相继的两个字,把第1个字送入IP寄存器中,把第2个字送入CS寄存器中,从而实现转移,如图4.15所示。
在微机指令系统中,转移类和子程序调用类指令通常使用上述寻址方式,使程序从当前执行的位置跳转到指令寻址方式指定的位置继续运行,或者是从指定的位置调用一个子程序。
4.3 8086指令系统
Intel 8086 CPU提供133条基本指令,按功能又可分为7类:数据传送指令、算术运算指令、逻辑运算指令、移位指令、串操作指令、程序控制指令、处理器控制指令。
4.3.1 数据传送指令
数据传送指令:功能是将数据、地址、立即数传送到寄存器或存储单元中。
8086的数据传送指令共7类,如表4.2所示:
(1)通用数据传送指令 MOV
通用数据传送指令MOV是形式最简单、用得最多的指令。
功能:
①实现CPU内部寄存器之间的数据传送。
②实现寄存器和内存之间的数据传送。
③把一个立即数传送给CPU的内部寄存器或内存单元。
格式:
MOV DST,SRC ; DST ← SRC
DST:寄存器、存储器;
SRC:寄存器、存储器、立即数。
说明:
①指令中两个操作数不能同为存储器操作数。(借助通用寄存器)
(显然也不能同为立即数,故而必有一个寄存器操作数)
②CS不能作为目标操作数。(CS、IP只能在程控指令中修改,非程控指令执行完IP自动加1)
③段寄存器之间不能互相传送。(借助通用寄存器)
④立即数不能直接送入段寄存器。(借助通用寄存器)
⑤ MOV指令不影响标志位。
下面举例说明MOV指令的应用:
(1)立即数传送给通用寄存器或存储器。
MOV AL,12H ;8位数据传送,将12H传送到寄存器AL中
MOV AX,3456H ;16位数据传送,将3456H传送到寄存器AX中
(2)通用寄存器之间相互传送。
MOV AX, BX ;16位数据传送,将 BX中的数据传送到寄存器 AX中
MOV CL, BH ;8位数据传送,将BH中的数据传送到寄存器CL中
(3)通用寄存器和存储器之间相互传送。
MOV AX,[BX] ;16位数据传送,将BX指定的连续2字节中的数据传送到AX中
MOV[SI],DH ;8位数据传送,将DH中的数据传送到由SI指定的内存单元中
(4)段寄存器与通用寄存器、存储器之间的相互传送。
MOV DS, AX
MOV BX, ES
MOV ES,[SI]
MOV[DI], SS
虽然 MOV指令不能直接实现两个存储单元之间的数据传送,但可以借助CPU内部的通用寄存器,通过两条指令来完成两个存储单元之间的数据传送。
规律:至少一个是通用段寄存器;当两个都是通用寄存器,注意位对齐。
(2)堆栈操作指令
① 堆栈
堆栈:是由若干个连续存储单元组成的一段存储区域,按照“后进先出”或“先进后出”的原则存取信息。在微型计算机中,可以把内存储器的一个区域作为堆栈,该内存区域按照“后进先出”的原则,入栈、出栈操作只能在栈顶进行,由堆栈指针始终指向堆栈的顶部。当有数据压入或弹出时,修改堆栈指针,以保证它始终指向当前的栈顶位置。在信息的存取过程中,栈顶是不断移动的,而堆栈区的另一端则是固定不变的,称其为栈底。
--------------------------------------------------------------------------------------------------------------------------
我的理解
① 只有两种操作——数据入栈、数据出栈。
② 只能操作栈顶——出栈,栈顶自增2向高地址;入栈,栈顶自减2向低地址。
③ 栈是从高地址向低地址生长的。
④ 栈底:堆栈段的最高地址处,固定不变,没有指针指向。
⑤ 栈顶:堆栈段内有数据存储的最低地址,是变化的,有栈顶指针SP
⑥ 堆栈指针SP(Stack Pointer):指向栈顶。自动更新,涉及堆栈的指令时自动更新。
⑥ 基址指针BP(Base Pointer):指向当前函数栈帧的基地址,为函数内部的变量访问提供一个基地址。手动更新,MOV BP, SP。
⑦ 栈帧:函数调用过程中在堆栈上分配的一块区域,用于存储函数的局部变量、参数等信息。
--------------------------------------------------------------------------------------------------------------------------
8086中规定堆栈设置在堆栈段SS内,SP作为堆栈指针。
堆栈必须按字操作——两个字节。
堆栈初始化后,SP指向堆栈中栈底+1单元的偏移地址。
(SS:SP指向栈顶元素的地址)
例如,把内存中某段的偏移地址为0000~00FFH 的一个存储区作为堆栈,那么堆栈指针SP的初始值为0100H,如图4.16(a)所示。
此时,若向堆栈中存入信息,则首先将SP内容减2(按字操作——两个字节),即 SP=00FEH,然后,将16位信息送入SP所指单元。例如,将一个16位信息0102H送入堆栈后,堆栈示意图如图4.16(b)所示。
(高位高地址,高位先入栈)
继续向堆栈中送入0304H、0506H、0708H后,SP将指向00F8H,如图4.16(c)所示。现在,若需从堆栈中取出信息,则首先取出的是SP所指向的00F8H单元中的信息,即0708H,并且SP内容加2(按字操作——两个字节),使 SP指向00FAH,如图4.16(d)所示。
入栈:先调整栈顶指针,然后以栈顶指针为基准,将数据存入之后的2字节空间。
出栈:先以栈顶指针为基准,将数据从之后的2字节空间取出,然后再调整栈顶指针。
可见,通过堆栈指针来管理堆栈的读、写地址,保证了它的信息存取按“后进先出”的规则进行。
在程序设计中,堆栈是一种十分有用的结构。
在子程序调用和中断处理过程中,需要用堆栈保存返回地址(断点地址),即当前CS和IP的值;
在进入子程序和中断处理后,还需要保存通用寄存器的值;
子程序和中断处理程序将要返回时,则要恢复通用寄存器的值;
子程序和中断处理程序返回时,要恢复返回地址(断点地址)。
这些功能都要通过堆栈指令来实现。
② 堆栈操作指令
8086指令系统提供了专用的堆栈操作指令:
PUSH:进栈操作指令。(压栈、入栈)
POP:出栈操作指令。
在程序中使用堆栈操作指令时,应预置堆栈段寄存器SS、堆栈指针SP的值,使SP的内容为当前堆栈的栈顶。
PUSH指令的执行:先SP-2→SP,然后把源操作数送至SP所指堆栈顶部的一个字单元(两个连续字节单元)中;
POP指令的执行:把当前SP指向的堆栈顶部的一个字单元(两个连续字节单元)送至指定的目标操作数,然后SP+2→SP,使堆栈指针指向新的栈顶。
格式:
PUSH SRC ;进栈指令,SP ← SP-2, (SP+1:SP) ← SRC
POP DST ;出栈指令,DST←(SP+1: SP), SP ← SP+2
SRC:通用寄存器、段寄存器、存储器操作数;
DST:通用寄存器、除CS之外的段寄存器、存储器操作数。
单操作数指令,不能使用立即寻址方式,操作数不能是立即数。
例如:
MOV AX,1234H
PUSH AX ;单操作数指令,不能使用立即数寻址方式
设执行前SS=2000H,SP=00FEH,指令执行过程如图4.17·所示。执行后SS=2000H,(SP)=00FCH。
说明:
① PUSH、POP指令不能使用立即寻址方式,POP指令不能使用CS寄存器——不能用CS作为pop出数据的容器。
② 堆栈中数据的压入、弹出必须以字为单位进行——每次PUSH操作栈顶向低地址移动两字节,而每次POP操作栈顶向高地址移动两字节。
③ 这两条堆栈指令不影响标志位。
在程序设计时,PUSH、POP必须配对使用,以保证SP指针不被破坏。这样才能保证在程序执行期间,堆栈不会发生溢出。
(3)地址传送指令
地址传送指令:是一类专用于传送地址码的指令——将内存操作数的逻辑地址(段地址或偏移地址)传送至指定寄存器中。
(内存操作数才涉及“地址”概念,有各种地址)
共包括3条指令:LEA、LDS和 LES。
(EA:有效地址,只在内存中才有——计算得到的偏移地址)
① 取有效地址指令LEA
(Load Effective Adrress)
取有效地址指令LEA 把源操作数的有效地址(即16位的偏移地址)送入指定的寄存器中。
格式:
LEA REG,存储器地址 ;REG ← 存储单元的偏移地址
规定,LEA指令的目的操作数(DST)是一个16位的通用寄存器(REG)。
源操作数是存储在内存储器中的数据的地址。
这条指令不影响标志位。
例如:
1 MOV byte ptr[2345],12
2 LEA BX,[2345] ;将2345单元的偏移地址送人BX,指令执行后,BX中为2345
3 MOV AX,[2345] ;将2345单元的内容送入AX,指令执行后,AX中为12
4 LEA AX,[BP+SI] ;指令执行后,AX中为BP+SI的和
LEA指令常用在初始化程序段中建立操作数的地址指针,或者用来建立串操作指令所需要的寄存器指针,使某个通用寄存器成为地址指针。
注意区分第2行的[2345]是用作地址,第3行的[2345]是取地址内的数据
② 取逻辑地址指令LDS/LES
(Load Address to DS:SI,Load Address to ES:DI)
一个内存单元的逻辑地址由4字节组成:16位段地址和、16位偏移地址。
LDS/LES 指令:根据源操作数指定的内存单元,将连续4字节中的数据分别送入DS(或ES)和指定的寄存器中。
格式:
(M,MEM,MEMERY:存储器)
LDS REG,M寻址 ;REG ← M寻址的第一个字单元, DS ← M寻址的第二个字单元
LES REG,M寻址 ;REG ← M寻址的第一个字单元, ES ← M寻址的第二个字单元
同样,DS:SI、ES:DI——低地址入低位(指定寄存器),高地址入高位(DS/ES)
其中,目标操作数REG是一个16位的通用寄存器。
LDS指令将源操作数存储单元指定的4个连续字节中的内容(存的是逻辑地址),送入指定的寄存器中。其中,前两字节作为偏移地址传送到REG中;后两字节作为段地址传送到DS中。
LES 与LDS相似,但将段地址送到ES中。
例如:
LDS BX,[1230H]
将地址为1230H和1231H 的内存单元中的16位数据作为偏移地址,送入BX寄存器;
将地址为1232H和1233H的内存单元中的16位数据作为段值,送入DS寄存器。
LEA BX,[1234]——BX直接存储1234这个偏移地址。
LED/LDS BX,[1234]——BX间接存储[1234]里存储的2字节逻辑地址——段地址:偏移地址。
说明:
① LDS和LES指令中的REG不允许是段寄存器。
② LDS和 LES指令均不影响标志位。
(4)标志寄存器传送指令
LAHF、SAHF、PUSHF、POPF指令均是无操作数指令。
LAHF、SAHF隐含的操作数为AH。
① 读取标志指令LAHF
(Load AH from Flag)
读取标志指令LAHF:将标志寄存器FLAGS中的低8位传送至8位通用寄存器AH中。
FLAGS 的低8位包括标志位SF、ZF、AF、PF和CF,如图4.18所示。
除了OF之外的状态标志位。
② 设置标志寄存器指令SAHF
(Store AH into Flag)
设置标志寄存器指令SAHF:把8位通用寄存器AH中的数据传送至标志寄存器FLAGS的低8位。
SAHF指令可能会改变SF 、ZF、AF、PF和CF标志位,但不影响位于高字节的OF、DF、IF和 TF标志位。
③ 标志寄存器的进栈/出栈指令PUSHF/POPF
(PUSH Flag、POP Flag)
栈操作都是字操作:2字节、16位。
标志寄存器:包括9个标志位,总16位,1字,2字节。
(Ⅰ)标志寄存器进栈指令PUSHF:把标志寄存器推入当前堆栈的顶部,同时修改堆栈指针,即 SP-2→SP。这条指令不影响标志位。
(Ⅱ)标志寄存器出栈指令POPF:把当前堆栈指针所指的字传送给标志寄存器,同时修改堆栈指针,即SP+2→SP。这条指令执行后,标志位的状态就取决于原来栈顶的内容。
LAHF、PUSHF不影响标志位。
SAHF、POPF影响标志位。
(5)数据交换指令XCHG
(前缀X,全称Exchange)
数据交换指令XCHG:可以把源操作数和目标操作数交换。
允许在寄存器之间、寄存器和存储单元之间完成一个字或字节的交换,位对齐就好。
格式:
XCHG DST, SRC ;DST↔SRC
其中,DST、SRC可以是:通用寄存器、存储器。
说明:
(Ⅰ)DST,SRC不允许是段寄存器、立即数和IP寄存器。
(Ⅱ)DST和SRC 中,必须有一个是寄存器寻址方式,即两个存储单元之间不能直接互换数据。
(Ⅲ)XCHG指令不影响标志位。
例如:
XCHG BX,[BP+SI]
假设该指令执行前,BX=1234H,BP=0100H,SI=0020H,SS=1F00H,(1F120H)=0000H,源操作数物理地址=lF00H:0120H=1F120H,交换前源操作数为0000H,目标操作数为1234H,则指令执行后,BX=0000H,(1F120H)=1234H。
(6)换码指令XLAT
(前缀X,全称Translate,翻译)
换码指令XLAT:又称为字节翻译指令或查表指令。
该指令通过查表的方式,用表格中的一个值(称为换码字节)来置换AL中的内容。
该指令是无操作数指令,隐含了两个默认的寄存器AL和BX。
计算得有效地址EA=BX+AL。
BX作基址寄存器、AL作偏移量。
执行的操作是AL<-[BX+AL]。
使用换码指令之前,应先建立代码转换表,并设置寄存器BX、AL的值。
具体步骤如下:
(Ⅰ)在数据段建立代码转换表,并将该表首地址的偏移地址存入BX寄存器。
(Ⅱ)将待转换的数据在表中对应的位移量存入AL寄存器。
(Ⅲ)执行XLAT指令,将[BX+AL]内存单元中的一个字节送入AL中。
说明:
(Ⅰ)(AL)是一个8位无符号数,所以表格中最多只能存放256个代码。
(Ⅱ)XLAT指令的执行结果不影响标志位。
例如,若已将十六进制数0~9、A~F的ASCII码依次放入以1000H为起始地址(在数据段DS内建立)的代码转换表中,执行以下程序段:
MOV BX, 1000H ;将代码转换表首地址1000H送入BX
MOV AL, 4 ;待转换的十六进制数为4
XLAT ;AL<-[1004H]
XLAT指令执行后,表中取得“4”对应的ASCII码34H,替代原来AL中的4。
(7)输入/输出指令IN/OUT
作为I/О端口地址和存储器分离的地址空间系统,80x86有专门的I/O指令用于与端口进行通信。
8086的I/O指令是IN指令和OUT指令。
这两条指令既可以传送字节也可以传送字,并且都有直接端口寻址和间接端口寻址两种方式。
直接端口寻址:直接给出8位端口号。
间接端口寻址:由DX给出16位端口号。
指令格式为:
IN DST, SRC ;SRC为I/O端口寻址
OUT DST, SRC ;DST为I/O端口寻址
例如:
IN AL,PORT ;AL<-PORT ;直接端口寻址方式
IN AX,PORT ;AX<-PORT+1, PORT
IN AL,DX ;AL<-(DX) ;间接端口寻址方式
IN AX, DX ;AX<-(DX+1, DX)
OUT PORT,AL ;PORT<-AL ;直接端口寻址方式
OUT PORT,AX ;PORT+1,PORT<-AX
OUT DX,AL ;(DX)<-AL ;间接端口寻址方式
OUT DX, AX ;(DX+1,DX)<—AX
两组指令前两种方式是直接端口寻址方式,端口地址PORT是一个8位的立即数,其范围为0~255。
两组指令后两种格式是间接端口寻址方式,端口地址在DX中,其范围为0~65535,这种方式通过对DX寄存器的增量可以处理几个连续端口地址的输入/输出。
需要注意的是,指令中使用的数据寄存器必须是AL或AX;间接寻址的寄存器必须是DX。
(内存储器-内存地址、I/O接口电路-I/O端口地址-8位或16位)
4.3.2 算术运算指令
8086指令系统中,具有完备的加、减、乘、除运算指令——对二进制数、BCD码进行运算。
参加运算的数可以是字节或字,也可以是带符号数、无符号数。
(算术运算:整数
逻辑运算:逻辑代数
浮点运算:浮点数)
(1)加法与减法指令
加法与减法指令的汇编格式及功能说明如表4.3所示。
说明:
① 目的操作数:通用寄存器、存储器操作数。(结果要存入目的操作数,故而得是一个容器)
② 源操作数:通用寄存器、存储器操作数、立即数。
① 不带进位加法指令ADD
不带进位加法指令ADD:完成两个操作数求和运算,并把结果送到目标操作数中。
需要注意的是,ADD指令的操作数都是带符号数。
由于AX、AL是累加寄存器,所以通常ADD、ADC的目的操作数都是AX、AL。
格式:
ADD DST,SRC ;DST<-SRC+DST
例如:
ADD AL, BL ;AL+BL->AL
ADD AX,[BX+SI] ;DS段中有效地址为BX+SI单元中的字,与AX寄存器中的数相加,结果存入AX中
ADD指令影响OF、SF、ZF、AF、PF、CF标志位。
(即6个状态标志位,按高位→低位的顺序)
例如:
MOV AL, 46H ;AL=46H
ADD AL, 0C5H ;AL+0C5H->AL
指令执行过程如下:
指令执行后,对标志位的影响为:
OF=0、SF=0,ZF=0,AF=0,PF=0,CF=1(按高位→低位)
对于SF、ZF、AF、PF、CF的设置比较容易判别。
OF是根据源操作数的符号及运算结果符号位的变化来设置的,如果参加运算的两个数符号相同,而结果符号相反,则OF置1,否则OF置0;
也可以根据1.1.3节介绍的补码运算判断溢出方法,使用双符号法或单符号法进行判断:
单符号法:将两个符号位相异或。
双符号法:将最高位进位值与次高位进位值相异或。
结果就是OF的值
② 带进位加法指令ADC
(ADD with Carry)
带进位加法指令ADC与ADD指令类似,只是在两个操作数相加时,还要把进位标志CF加上去。
ADC指令的操作数都是带符号数。
格式:
ADC DST, SRC ;DST<-SRC+DST+CF
例如:
ADC AX, BX ;AX=AX+BX+CF
带进位加法指令主要用来实现多字节、多精度加法,因为它能加上低位来的进位。
例如,有两个4字节数,分别存放在自0100H和0200H开始的存储单元中,其低位字位于低地址处。求这两个数之和,结果存放在自0300H开始的单元中。
相应的程序段为:
MOV AX, [0100H]
ADD AX, [0200H] ;低16位相加
MOV[0300H],AX ;低16位之和存入0300H,0301H单元
MOV AX,[0100H]+2 ;装入高16位
ADC AX,[0200H]+2 ;高16位求和,而且是考虑低16位的进位的求和
MOV[0300H]+2,AX ;将高16位之和存入0302H、0303H单元
4字节加法步骤:
① 先实现低位2字节ADD加法,结果存入结果空间的低2字节;
② 再实现高位2字节ADC加法,结果存入结果空间的高2字节。
ADC指令对标志位的影响与加法指令ADD 相同——6个状态标志位。
③ 自增指令INC
(Increase)
自增指令INC将操作数DST加1,结果再送回DST。
格式:
INC DST ;DST<-DST+1
INC指令影响OF、SF、ZF、AF、PF标志位。(按高位→低位)
不影响进位标志CF。(一般用于两个无符号数加减运算的进位、借位情况)
例如:
INC SI ;将SI内容加1,结果送回SI
④ 不带借位减法指令SUB
(Subtract)
SUB指令:求目标操作数减去源操作数的差,结果送回到目标操作数中。
SUB指令的操作数都是带符号数。
格式:
SUB DST, SRC ;DST<-DST-SRC
例如:
SUB AX, CX ;AX-CX->AX
SUB[BX+10H],1234H ;立即数与内存单元中的字相减,内容存入内存单元中
SUB指令影响OF、SF、ZF、AF、PF、CF标志位。
减法的本质是加法,故而对标志位的影响一样。
例如:
MOV DL, 41H
SUB DL, 5AH
减法指令执行过程如下:
指令执行后DL=0E7H,OF=0,SF=1,ZF=0,AF=1,PF=1、CF=1,。
减法运算对AF、CF的设置方法是:将参加减法运算的数看作4位、8位无符号数,如果被减数小于减数,则AF/CF=1,否则AF/CF=0;
或者对实际进行的补码加法运算过程中产生的最高位进位取反。
对OF的设置方法是:将参加运算的数看作带符号数,那么只有当相减的两个数符号相反时,才可能产生溢出。所以如果两个数符号相反,而结果与减数符号相同,则OF=1,否则OF=0;
也可以使用双符号法或单符号法进行判断,将实际进行的补码加法运算过程中的最高位进位值与次高位进位值相异或,或者将两个符号位相异或,结果就是OF的值。
⑤ 带借位减法指令SBB
(Subtract with Borrow)
带借位减法指令SBB与SUB指令类似,只是在两个操作数相减时,还要再减去借位标志CF,结果送到目标操作数中。
SBB指令的操作数都是带符号数。
格式:
SBB DST,SRC ; DST<-DST -SRC- CF
SBB指令对状态标志位的影响与SUB指令相同。
它的用法与ADC指令相似,主要用来做多字节、多精度减法,因为它能够减去低位产生的借位。
⑥ 自减指令 DEC
(Decrease)
自减指令 DEC将目标操作数DST减1,并将结果再送回DST。格式:
DEC DST ;DST<-DST - 1
例如:
DEC[ BX + SI] ;DS段有效地址为BX+SI的存储单元内容减1
DEC指令影响OF、SF、ZF、AF、PF标志位,但不影响进位标志CF。
⑦ 取负指令 NEG
(Negtive)
取负指令NEG对指令中给出的操作数DST求相反数,再将结果送回 DST。
因为对一个数取相反数相当于用0减去这个数,所以 NEG 指令执行的也是减法操作。
格式:
NEG DST ;DST<-0-DST
由于计算机中的数均是以二进制补码的形式存储的,若操作数的原值为正数,那么执行NEG指令后,其值变为该数所对应的负数的补码;而若操作数的原值为负数(补码表示),那么执行该指令后,其值变为该数所对应的正数的补码(正数的补码是它本身)。
也就是说,无论操作数DST是正数还是负数,执行完该指令后,都相当于对 DST 按位取反(包括符号位),末位加1。因此,这条指令也称为求补指令。
例如:
NEG AL
若(AL)=03H,则 CPU执行完该指令后,(AL)=0FDH。
NEG指令影响OF、SF、ZF、AF、PF、CF标志位。
⑧ 比较指令CMP
比较指令CMP与SUB指令相似,也执行减法操作。
与减法指令不同的是,相减的结果并不送回目标操作数,因而指令执行后,仅仅改变了标志寄存器的内容,两操作数的值保持不变。
CMP指令的操作数可以是带符号数,也可以是无符号数。
格式:
CMP DST, SRC ;DST - SRC并设置标志位
在比较指令执行之后,根据OF、SF、ZF、CF这4个标志位判断两数的大小。
例如,执行指令
CMP AX, BX
(Ⅰ)两数相等
ZF=1是判断两数相等的充要条件。
(Ⅱ)AX 和 BX不等,且均为无符号数
两数为无符号数,其运算对SF标志无影响。
若CF=0,表明两数相减未产生借位,则AX>BX;
若CF=1,表明相减之后有借位,则AX<BX。
CF标志用来判断两无符号数的大小。
(Ⅲ)AX 和 BX不等,且为同号的带符号数
两数符号相同,即同为正数或同为负数,则两数相减不会产生溢出,即OF=0。
若AX>BX,结果为正数,则SF=0;
若AX<BX,结果为负数,则SF=1。
于是可得,当OF=0时:
若SF=0,则AX>BX;
若SF=1,则AX<BX。
SF标志用来判断两同号带符号数的大小。
(Ⅳ)AX 和 BX不等,且为异号的带符号数
当两数符号不同时,其中一个为正数,另一个为负数。
若两数相减没有溢出,即OF=0,则仍可根据SF标志来判断两数的大小,结论与(Ⅲ)相同。
若两数相减产生溢出,则两数相减会影响符号位。
当OF=1时:
若 SF=1,则AX>BX;(向负溢出)例126-(-3)=-127,
若 SF=0,则AX<BX。(向正溢出)例-126-3=127,
综合(Ⅲ)、(Ⅳ)可得出如下结论:
两个带符号数相减,当OF、SF相同时,AX>BX;OF、SF相反时,AX<BX。
即:
若 OF⊙SF=1,AX>BX;
若 OF⊙SF=0,AX<BX。
换句话说:
若 OF⊕SF=0,AX>BX;
当 OF⊕SF=1,AX<BX。
-----------------------------------------------------------------------------------------------------------------------------
我的话:
带符号数的比较——
① 若 OF、SF同号,则AX>BX。
例:
OF == SF == 0,无溢出,相减为正
OF == SF == 1,向负溢出,正 - 负
② 若 OF、SF异号,则AX<BX。
例:
OF == 0,SF == 1,无溢出,相减为负
OF == 1,SF == 0,向正溢出,负 - 正
-----------------------------------------------------------------------------------------------------------------------------
在分支程序设计中,常用CMP指令来产生条件,其后往往跟着一条条件转移指令,由CMP指令为条件转移指令提供控制转移的依据。
(2)乘法指令与除法指令
8086提供的乘法与除法指令及其功能如表4.4所示。
① 乘法运算指令
(Integer Multiply,整数乘法—带符号乘法)
乘法指令IMUL/MUL可以完成带符号/不带符号的乘法运算。
格式:
MUL/IMUL SRC
说明:
(Ⅰ)单操作数指令。
MUL指令和IMUL指令只提供一个源操作数,可以是除立即数外的任何寻址方式。
另一隐含操作数为累加器AL(8位乘法)或AX(16位乘法)。
两个8位数相乘结果(16位)存于AX中;
两个16位数相乘结果(32位)一起存于DX、AX中,DX存放高16位,AX存放低16位。
如图4.19所示。
(Ⅱ)无符号乘法指令 MUL、带符号乘法指令 IMU
例如(11111111B)×(11111111B)若看作无符号数,则使用MUL指令,执行的结果为255×255=65 025;若看作带符号数,则使用IMUL指令,执行的结果为(-1)×(-1)=1。
(Ⅲ)对除CF和OF以外的标志位无定义(即指令执行后,标志位的状态不确定)
对于MUL指令:
若乘积的结果高半部分为0(字节相乘后AH=0,或者字相乘后DX=0),则OF=CF=0,否则OF=CF=1。
对于IMUL指令,若乘积的结果高半部分有有效数字(不是符号扩展),则CF=OF=1,否则CF=OF=0。
例如,设AL=0FC,CL=10H。
执行“MUL CL”命令后,AX=0FC0H,CF=OF=1;
执行“IMUL CL”命令后,(AX)=0FFC0H,CF=OF=0。
② 除法运算指令
(Integer Division,整数乘法—带符号乘法)
(Ⅰ)无符号数除法指令DIV
字节除法、字除法。
格式:
DIV SRC
其中,源操作数可以是寄存器或存储器操作数。
DIV指令对所有标志位均无定义。
(Ⅱ)带符号数除法指令IDIV
IDIV指令的使用格式、指令功能及对标志位的影响情况与DIV指令都一样。
唯一区别是,IDIV指令只用于带符号数的除法,并规定余数符号与被除数符号相同。
8位和16位除法过程如图4.20所示。
8位除法:AX ÷ SRC,商→AL,余数→AH。
16位除法:DX AX ÷ SRC,商→AX,余数→AX。
除法指令执行后,若商超出了表示范围,即字节除法超出了AL范围,字除法超出了AX范围,就会引起0类型中断,在0类型中断处理程序中,对溢出进行处理。
(3)符号扩展命令
在算术运算中,有时会遇到两个长度不等的数进行加、减运算。
此时,应将长度短的数的位数扩展,使两数的长度一致,只有这样,才能保证参加运算的两个操作数的类型是一致的。
无符号数位扩展:只要将其高位补“0”就可以;
带符号数位扩展:正数高位补“0”,负数高位补“1”。
8086 CPU提供两条符号扩展指令:CBW、CWD。
(Convert Byte to Word,Convert Word to Double_word)
它们都是无操作数指令,默认操作AX、DX。
CBW、CWD都不影响标志位。
对于无符号数:符号扩展指令直接将AH或DX置零——扩展位置0。
对于带符号数:经过扩展以后,数的大小不变,仅将符号位扩展——符号位扩展。
① 字节扩展指令CBW
将AL中的符号位扩展到AH中,将一个字节扩展成一个字。
如果 AL的最高位是0,则扩展以后AH=0;
如果 AL的最高位是1,则扩展以后AH=0FFH。
② 字扩展指令CWD
将AX中的符号位扩展到DX中,将一个字扩展成双字。
如果AX的最高位是0,则扩展以后DX=0;
如果AX的最高位是1,则扩展以后DX=0FFFFH。
(4)十进制算术运算指令
在实际应用中,人们习惯使用十进制数,需要计算机处理的原始数据大部分是十进制数据。人们希望计算机能够接收这些十进制数,并将处理结果以十进制形式输出。在1.1.4节中,大家已经知道,在计算机中,可以通过 BCD码表示十进制数。8086 CPU实现十进制运算的方法是仍然将这些十进制数(BCD码)看作二进制数,使用二进制加、减、乘、除指令进行运算,不过在运算之后(或之前)用调整指令进行调整,从而得到十进制(BCD码)的结果。
因此,8086提供了6条BCD码调整指令:DAA、DAS、AAA、AAS、AAM、AAD。
十进制数:以ASCII码的形式存储。
非压缩BCD码:高4位为0011,低4位为BCD码(十进制数字0~9的ASCII码30H~39H)。
压缩BCD码:只有2个4位BCD码。
如表4.5所示,它们都是无操作数指令。
压缩BCD码调整指令:加、减。
非压缩BCD码调整指令:加、减、乘、除。
下面以加法运算为例,介绍 BCD码调整指令的原理。
例如,十进制运算9+4=13,当操作数“9”和“4”用非压缩的BCD码表示时,若按二进制加法规则进行运算,则结果为:
结果不符合十进制要求,原因就在于BCD码将4位二进制码看作一个整体,表示一位十进制,按十进制运算规则应是逢10进1,运算结果>9就应该产生向更高位的进位;而这4位二进制数在计算过程中仍然遵循二进制的运算规则,它们向更高位进位的规则是逢16进1,所以,虽然结果>9,却没有产生进位,产生了非法的BCD码。
因此需要对运算结果进行调整,方法就是给运算结果再加06H,迫使它产生进位。结果为:
显然,当运算结果≤9,则不用加06H进行调整。
同理,对于压缩BCD码,应分别对高位部分和低位部分进行调整。
例如,51+72=123,高位相加>9,按二进制运算后,应加60H调整。
① 压缩 BCD码调整指令
(Ⅰ)加法调整指令DAA
(Decimal Adjust for Addition)
DAA指令要求执行前,必须执行ADD或ADC加法指令,然后DAA指令将两个压缩 BCD 码相加的结果调整后存入AL寄存器中。
无操作数指令,是一种调整指令,隐含操作数AL。
调整方法:
若AL低4位 > 9 或 AF=1,则AL寄存器内容加06H,且将AF置1;
若AL高4位 > 9 或 CF=1,则AL寄存器内容加60H,且将CF置1。
影响的标志位:SF、ZF、AF、PF、CF,对OF无定义。
例如:
MOV AL,28H ;AL-28
ADD AL,38H ;AL<-AL+38,AL=60H,CF=0,AF=1
DAA ;因AF=1,做加6调整,AL=66,AF = 1,CE = 0
(Ⅱ)减法调整指令 DAS
(Decimal Adjust for Substraction)
DAS指令要求执行前,必须执行SUB或SBB指令,并将两个压缩 BCD码相减的结果存入AL寄存器中。
调整方法:
若AL低4位 > 9 或 AF=1,则AL寄存器内容减06H,且将AF置1;
若AL高4位 ≥ 9 或 CF=1,则AL寄存器内容减60H,且将CF置1。
影响的标志位:SF、ZF、AF、PF、CF,对OF无定义。
② 非压缩 BCD码调整指令
(Ⅰ) 加法调整指令 AAA
(ASCII Adjust for Addition)
AAA指令要求执行前,必须执行ADD或ADC 加法指令,将两个非压缩 BCD码相加,并将结果存入AL寄存器中。
调整方法:
若AL低4位 ≤ 9,且 AF=0,则清除AL高4位(非压缩BCD码高4位无定义),CF<-AF;
若AL低4位 ≥ 9,或AF=1,则将AL寄存器内容加6,AH寄存器内容加1,AF置1,清除AL高4位,CF<-AF。
影响的标志位:AF、CF,对其他状态标志无定义。
例如,若指令执行前AX=0535H,BL=39H。
ADD AL,BL ;AX=056EH, AF=1
AAA ;AX=0604H, CF=AF=1
(Ⅱ)减法调整指令AAS
(ASCII Adjust for Substraction)
AAS指令要求执行前,必须执行SUB或SBB指令。减法指令将两个非压缩 BCD码相减,并将结果存入AL寄存器中。
调整方法:
若AL低4位 ≤ 9,且AF=0,则清除AL高4位,CF<-AF;
若AL低4位 > 9,或AF=1,则将AL寄存器内容减6,AH寄存器内容减1,AF置1,清除AL高4位,CF<-AF。
影响的标志位:AF、CF,对其他状态标志无定义。
(Ⅲ)乘法调整指令AAM
(ASCII Adjust for Multiplication)
AAM指令要求执行前,必须执行MUL乘法指令,MUL指令将两个非压缩BCD码(此时要求非压缩BCD码高4位为0)相乘,并把结果存入AL寄存器中。
调整方法:
把AL中的积调整为非压缩BCD码形式送入AX寄存器中。
具体为:(AL/0AH)的商→AH寄存器;(AL/0AH)的余数→AL寄存器。
影响的标志位:SF、ZF、PF,对OF、AF、CF无定义。
(Ⅳ)除法调整指令AAD
(ASCII Adjust for Devision)
与其他调整指令不同,AAD指令的调整工作在除法指令执行之前进行。
它针对的情况是:如果被除数是存放在AX寄存器中的非压缩BCD码表示的两位十进制数,AH中存放十位,AL中存放个位,且它们的高4位都为0,除数是非压缩BCD码表示的一位十进制数,且高4位为0。
则AAD 指令将AX寄存器中的被除数调整为二进制数,并存放在AL寄存器中,以便能用DIV指令实现两个非压缩的十进制数的除法运算。
方法:AL<-AH×10+AL,AH<-0
影响的标志位:SF、ZF、PF,对AF、CF、OF无定义。
例如:
MOV BL,5
MOV AX,0308H
AAD ;AL-3×10+8= 26H,AH--0
DIV BL ;26H/05H,商=07H→AL,余数=03H→AH
4.3.3 逻辑运算指令
逻辑运算指令同前面介绍的算术运算指令的主要差别在于:
按位运算,且不考虑位之间的进位和借位,以及运算结果的溢出。
逻辑运算指令的格式及功能说明如表4.6所示。
规定:
① 目标操作数DST:通用寄存器、存储器操作数。
② 源操作数SRC:立即数、通用寄存器、存储器操作数。
逻辑与、逻辑或、逻辑异或、逻辑非:逻辑运算的结果存储在DST。
逻辑测试:置标志位。
它们都可以进行字节或字的按位逻辑运算。
NOT指令不影响标志位。
其余4条指令都使CF和OF为0,对AF无定义,按运算结果设置SF、ZF、PF。在某些场合,经常要用到逻辑运算指令的位操作能力,以实现特定的功能。
AND 指令可以使某些位清0,某些位不变——低电平敏感。
例如:
AND AL,0FH ;使AL的高4位清0,低4位保持不变
若指令执行前AL=37H,则上述指令执行后AL=07H。
OR指令可以使某些位置1,某些位不变——高电平敏感。
例如:
OR AL, 80H ;使AL的最高位置1,其余位保持不变
若指令执行前AL=56H,则上述指令执行后AL=0D6H。
XOR指令可以使某些位不变,某些位求反;还能对寄存器清零。
——和0异或:得自己;
——和1异或:取相反;
——和自己异或:得0。
例如:
XOR AL, 0F0H ;使AL的高4位求反,而低4位不变
XOR AX, AX ;使AX=0, CF=OF=0
若指令执行前AL=78H,则上述指令执行后AL=88H。
TEST 指令可以用来判断某位是否为0或为1。
TEST 指令执行AND运算,但不将结果送回目的,而是仅仅置各标志位。
例如:
AND AL,0FH
OR BX,[SI + 20H]
XOR[BX],AL
NOT CX
TEST AL,0FFH
例如,要测试AL寄存器内容的最低位是否为0,只需执行指令:
TEST AL,01H
若执行后,ZF=1,则表明最低位为0,否则不为0。
如果要测试某位是否为1,只需将操作数用NOT指令取反,再执行以上操作就可以了。
4.3.4 移位指令
移位指令包括:移位指令、循环移位指令。
可以实现字节或字移位。
如表4.7所示。
SAL(SAR):Shift Arithmetic Left
SHL(SHR):SHift logical Left
ROL(ROH):ROtate Left
RCR(RCH):ROtate through Carry Left
目的操作数DST:通用寄存器、存储器操作数。(存储移位的结果)
源操作数COUNT:
① 移位次数,如果移位次数是一次,则可以直接出现在指令中;
② 如果移位次数大于一次,则由CL寄存器间接给出。
说明:
(1)移位指令影响标志位OF、SF、ZF、PF、CF。(除AF)
OF的设置方法是:如果移位后最高位发生了变化,则OF=1,否则OF=0。
其余标志位根据移位后的值来确定,对AF无定义。
(2)循环移位指令只影响标志位CF和OF,不影响其他标志位。
CF根据移位后的值来设置。
OF的设置方法是:如果移位前后最高位的值发生了变化,则OF=1,否则OF=0。
(3)SAL指令和SHL指令是一条机器指令的两种汇编指令表示。
(4)SAL/SHL指令:
① 若目标操作数为无符号数,且移位后值小于255(字节移位)或小于65535(字移位),则左移一位,相当于数值乘以2;
② 若目标操作数是带符号数,移位后不溢出,则执行一次SAL指令相当于带符号数乘以2。
(5)SAR指令:可用于用补码表示的带符号数的除2运算。
(6)SHR指令:可用于无符号数的除2运算。
例如:
;算术左移
MOV AH,3FH ;AH<-3FH
SAL AH,1 ;AH=7EH,CF=0,OF=0
;算术右移
MOV AL,88H ;AL<-88H 1000,1000
SAR AL,1 ;AL= 0C4H 1100,0100
;逻辑右移
MOV CL,2
MOV AL, 08H
SHR AL, CL ;AL=02H
小循环左移
MOV AL, 46H
MOV CL, 2
ROL AL, CL ;AL= 19H, CE = 1, OF = 0
4.3.5 串操作指令
串(String):是指含有字母、数字的一系列字节或字数据。
串定义:在数据段定义串string1,数据可以逐字节/逐字给出,也可以用 '……' 给出。
数据串元素:组成数据串的字节或字。
对于逐字节/逐字给出的,给什么就是什么。
对于'……'给出的,则是字符的ASCII码。
串操作:是指对数据串中所有的元素进行相同功能的操作、处理,包括统计、搜索、插入、删除、替换和传送等。
为了提高对串的处理效率,8086指令系统中专门提供了一组串操作指令+3个重复前缀,极大地方便了程序设计。
串操作指令既可以按字节操作,也可以按字操作。
串操作指令都是无操作数指令,源串和目的串均为隐含寻址方式。
所有串操作指令隐含地使用了相同的寄存器、标志位和符号,
串规定:
(1)源串地址DS:SI,存放在数据段;目的串地址ES:DI,存放在附加段。
(2)串操作指令不加重复前缀,串操作只执行一次。如果要重复执行串操作指令,就要在串操作指令前加重复前缀,并用CX存放重复的次数,每重复执行一次,CX内容减1;当CX内容为0时,串操作停止。
(3)每条串操作指令后,SI或DI的内容会自动修改,其修改方向受标志位DF控制。当DF=1时,SI或DI以递减方式修正;当DF=0时,SI或DI以递增方式修正。
由于串操作指令对源、目的串的寻址并不在指令中指出,并且指令中要根据方向标志寄存器DF的值来修改SI、DI的值,对于带重复前缀的串操作指令,还要根据CX的值决定串操作的重复次数,因此在执行串指令时,应做以下的准备工作或预置,
串准备操作:
(1)把源串的首地址/末地址送入SI寄存器。
(2)把附加段中目的串的首地址/末地址送入DI寄存器。
(3)将重复次数(串长度)送入CX寄存器。
(4)设置方向标志 DF。无操作数指令CLD可使DF=0,STD可使DF=1。
(5)对于 STOS类和 SCAS类指令,还应给AX/AL预置初值。
(1)基本串操作指令
8086 提供了5类串操作指令,每类又分为按字节操作、按字操作,共10条指令。
如表4.8所示。
① 串传送指令MOVSB/MOVSW
格式:
MOVSB
MOVSW
串传送指令将 DS:SI所指的一个字节或字传送给ES:DI指向的一个存储单元中,同时修改指针SI和 DI。
指令执行后对标志位无影响。
② 串比较指令 CMPSB/CMPSW
格式:
CMPSB
CMPSW
串比较指令的功能是比较源串与目的串中的一个字节或字,根据比较结果设置标志位。
比较方法是将源串中的字节或字减去目的串的字节或字,但不保留相减的结果,只是根据减法运算结果影响状态标志位来反映源串与目的串的大小,同时修改指向源串和目的串的堆址指针SI和 DI。
串比较指令影响的标志位有OF、SF、ZF、AF、PF、CF。
③ 串搜索指令SCASB/SCASW
格式:
SCASB
SCASW
串搜索指令将累加器AX/AL的内容与目的串比较,并根据比较结果设置标志位。
不涉及源串,不改变累加器、目的串的值,同时修改指针DI。
其影响的标志位有OF、SF、ZF、AF、PF、CF。
④ 取串指令LODSB/LODSW
格式:
LODSB
LODSW
取串指令从 DS:SI 指示的源串中取一个字节或字送到累加器AL/AX中,同时修改地址指针SI。
不涉及目的串。
取串指令执行后不影响标志位。
⑤ 存串指令STOSB/STOSW
格式:
STOSB
STOSW
存串指令将累加器AX/AL的内容送至ES:DI所指的字或字节,同时修改地址指针DI。
不涉及源串。
指令执行后不影响标志位。
(2)重复前缀指令
串操作指令与普通指令相比,只是多了一个自动修改地址指针的功能。
加入重复前缀才能使串操作指令重复执行,重复次数由CX的值决定。
重复前缀有3种形式,它们的助记符格式及功能说明如表4.9所示。
① 无条件重复前缀指令REP
(Repeat)
针对MOVS、STOS、LODS三类串操作指令,,只要CX≠0,无条件重复(伪)。
当CX≠0时,重复执行REP后的串操作指令,并使得CX<-CX-1,直到CX=0时退出。
格式:
REP
与MOVS结合使用:可完成一串数据的传送;
与STOS结合使用:可在内存ES:DI中建立一个内容相同(AX/AL)的串;
与LODS结合使用:可重复从存储器中取出数据到累加器中,累加器中保存最后一次取出的数据。
例如,将DS段中长度为10H的字符串STR1传送到ES段首址为STR2的缓冲区中,可用如下的程序段实现:
LEA SI,STR1
LEA DI,STR2
MOV CX,10H ;重复16次
REP MOVSB ;指令前缀REP,不能单独使用;
;MOVSB默认操作DS:SI——>ES:DI,SI++,DI++
② 相等重复前缀指令REPE/REPZ
(Repeat while Equal、Repeat while Zero)
当CX≠0且ZF=1时,重复执行该指令前缀后的串操作指令。
意思是一直相等ZF=1,就一直比,直到CX=0时退出;——此时ZF=1
若中间不等ZF=0,就直接退出。——此时ZF=0,SI/DI都已经加到了下一个数据位。
用于 CMPS 和 SCAS 类指令前。
格式:
REPE/REPZ
一般与CMPS结合:可以用来判断两个串是否相同。
例如,比较两个长度为20H的字符串ARY1,ARY2是否相同,如果相同,则给BL送0,否则送0FFH。可用如下的程序段实现:
LEA SI,ARY1
LEA DI,ARY2
MOV CX,20H
CLD ;方向标志DF置0,SI、DI递增,向高地址延伸(数据段、附加段)
MOV BL, 0FFH
REPE CMPSB
JNZ NEQU ;Not Equal
MOV BL,0
NEQU: HLT ;停机指令
③ 不等重复前缀REPNE/REPNZ
(Repeat while Not Equal、Repeat while Not Zero)
当CX≠0且ZF=0时,重复执行该指令前缀后的串操作指令。
意思是一直不等ZF=0,就一直比,直到CX=0时退出;——此时ZF=0
若中间相等ZF=1,就直接退出。——此时ZF=1,SI/DI都已经加到了下一个数据位。
用于 CMPS 和 SCAS类指令前。
格式:
REPNE/REPNZ 基本串指令
一般SCAS结合使用:可以用来在串中查找关键字。
例如,查找长度为10H的STRIN字符串中是否有字符'A ',如果有,则将第一次出现'A '的位置信息送给DX寄存器,否则DX置入0FFFH。可用如下的程序段实现:
LEA DI,STRIN
MOV AL,'A'
MOV CX,10H
MOV DX,0FFFFH
CLD
REPNE SCASB ;字节搜索,从ES:DI开始与AL比较+设置标志位+DI自增
JNZ NFOUND
DEC DI ;减回去,加到了下一个将要搜索比较的位置
MOV DX,DI ;'A'字符出现的位置
NFOUND:HLT
4.3.6 程序控制指令
程序控制指令可以改变程序的执行顺序,引起程序的转移,执行结果不影响标志位。
这类指令包括转移指令、子程序调用指令、中断指令。
(1)转移指令
转移指令包括无条件转移指令、条件转移指令、循环控制指令。
① 无条件转移指令JMP
JMP指令:使程序无条件地转移(真)到目标地址,从目标地址开始执行程序。
目标地址既可以和程序控制指令在同一个代码段内,也可以在不同的代码段内。
JMP指令支持:段内直接转移、段内间接转移、段间直接转移、段间间接转移。
直接:直接给出地址。
间接:地址在容器内。
如表4.10所示。
OPR为转移的目标地址——它可以是语句的标号或语句标号加常量表达式。
例如,可设置如下段间远转移指令:
CODE1 SEGMENT
……
JMP FAR PTR NEXTC
……
CODE1 ENDS
CODE2 SEGMENT
……
NEXTC: ADD AL, BL
……
CODE2 ENDS
转移过程如图4.21所示。
② 条件转移指令
条件转移指令:能够对一个或几个状态标志位进行测试——如果条件满足,就转移到指令指出的目的地去执行指令,否则顺序执行程序。
通常是根据上一条指令执行时所设置的状态标志位来形成转移与否的条件。
条件转移指令的格式及转移条件与标志位的关系如表4.11所示。
无符号数更大用Above:侧重于数值的绝对大小更大,类似于比较高度。
带符号数更大用Greater:比较关系复杂,需要考虑正负。
条件转移指令都是具有SHORT属性的段内相对转移,转移地址的形成是在当前至IP的基础上再加上一个8位的位移量,转移范围为-128~+127。
通常在指令中直接给出目标地址所在的标号。
根据控制转移判断的条件,条件转移指令大致可分为以下3类。
(Ⅰ)简单条件转移指令:根据单个标志位的状态判定转移条件。
共有5类10条:OF、SF、ZF、PF、CF。
(Ⅱ)无符号数条件转移指令:根据无符号数比较运算的结果对标志位的影响情况进行判断并实现转移的指令。
(Ⅲ)带符号数条件转移指令:8086 CPU提供了4条针对带符号数比较运算结果对标志位的影响情况进行判断并实现转移的指令。
③ 循环控制指令
循环控制指令位于循环程序的首部或尾部,控制程序的走向,实现循环过程。
循环控制指令都是具有SHORT属性的段内相对转移。
8086 CPU 提供4条循环控制指令,如表4.12所示。
其中,OPR是地址标号。
需要说明的是,循环控制指令中对CX寄存器的修改不影响状态标志位ZF。
LOOPE和LOOPZ、LOOPNE和LOOPNZ是同一条机器指令的两种不同助记符。
它们的功能:
① 使程序根据CX循环执行;(LOOP的唯一功能)
② 还提供了提前结束循环的手段。
例如,若要在一串字符中查找第一个非空格字符。
可以使用LOOPE/LOOPZ指令——相等就循环,不等就跳循环。
它控制程序段重复查找,如果字符等于空格(ZF=1),就继续查找,直到CX=0。此时ZF=1。
在查找过程中,一旦找到第一个非空格字符使ZF=0,就立即结束循环。
程序段如下:
MOV CX, L ;CX<-字符串长度
MOV AL,20H ;AL一空格字符的ASCII码
MOV SI,-1 ;设地址指针
AGAIN: INC SI ;地址指针自增1
CMP AL,STR[SI] ;与字符串比较
LOOPE AGAIN
JNE YES ;找到非空格字符则转移
……
STR[SI]表示从内存地址[STR+SI]取出一个字节的数据,寄存器相对寻址,变量名STR代表变量STR的地址。
(2)子程序调用和返回
子程序:为便于模块化设计和程序共享,在程序设计时,通常将具有独立功能的部分程序段编写成子程序(或称为过程),供其他程序在需要时进行调用。
主程序:调用子程序的程序称为主程序,也称父程序、调用程序。
主程序在调用子程序时,将控制转移到子程序去执行,子程序执行完后,再回到主程序调用语句的下一条语句接着执行。
8086 CPU为子程序的调用、返回提供了两条指令:CALL指令、RET指令。
如表4.13所示:
① 调用指令CALL
CALL指令实现对子程序的调用。
CALL指令使CPU 暂停执行主程序,转去执行子程序。被调用的子程序执行完后,再返回到主程序中继续执行。
断点:在主程序执行调用语句时,当前的CS、IP指示的指令地址称为断点,即子程序执行结束后主程序继续执行的位置。
段内调用、段间调用:被调用的子程序可以和主程序在同一个代码段中,也可以在不同的代码段中,因此有段内调用和段间调用两种方式。
近过程、远过程:8086指令系统中把处于当前代码段的过程称为近过程,用NEAR表示,而把其他代码段的过程称为远过程,用 FAR 表示。
调用子程序时,将断点压入堆栈加以保存,然后将CS,IP重新赋值,指向子程序(保存断点)
待子程序执行完后,再从堆栈中弹出断点信息,返回主程序(恢复断点)
CALL指令:自带断点保护功能。
RET指令:自带断点恢复功能。
段内调用不改变代码段寄存器 CS 的值,只改变指令指针IP,所以断点只包括IP的值。
段间调用因为在不同的代码段,既要改变代码段寄存器CS,又要改变指令指针IP,所以断点包括CS及IP4字节的信息。
无论是段内调用还是段间调用,都有直接和间接两种寻址方式。
(Ⅰ)段内直接调用
段内:只改变IP
↓(如何改变IP?)
直接:直接CALL新的IP地址(以子例程名字的形式给出)
格式:
CALL NEAR PTR OPR
其中,OPR为子程序名,它指出子程序的入口地址;NEAR PTR可以省略。
当执行这条指令时,将当前IP的值加上16位的位移量,形成新的IP。
其中16位位移量为当前IP与子程序入口地址之间的差值,在汇编时由汇编程序计算得到。
例如:
CALL, PROCB
其中,PROCB为子程序的名称。
(Ⅱ)段内间接调用
段内:只改变IP
↓(如何改变IP?)
减接:间接CALL新的IP地址(以寄存器、存储器的形式给出)
格式1:
CALL OPR ;OPR为16位寄存器名
格式2:
CALL WORD PTR OPR ;OPR为存储器寻址方式
其中,OPR可以是一个16位的寄存器,也可以是内存单元中的一个字地址,其中存放了子程序入口地址。
当执行这条指令时,首先根据指令中规定的寻址方式找到子程序入口地址,然后将它作为偏移地址送入IP。
表4.13中的EA为这个子程序入口地址的有效地址,即EA对应单元中存放的是子程序入口处的16位偏移地址。
例如:
CALL BX
CALL 20H[BX][SI]
(Ⅲ)段间直接调用
段间:改变IP、CS
↓(如何改变IP、CS?)
直接:直接CALL新的CS:IP地址(以子例程名字的形式给出)
格式:
CALL FAR PTR OPR
其中,OPR为过程名,代表子程序的入口地址,其寻址方式为直接寻址,即指令中直接给出子程序入口地址的段地址和偏移地址。
执行这条指令时,将子程序的入口地址分别送给CS、IP。
例如:
CALL, FAR PTR PROCA
(Ⅳ)段间间接调用
段间:改变IP、CS
↓(如何改变IP、CS?)
间接:间接CALL新的CS:IP地址(以双字存储器空间的形式给出)
格式:
CALL DWORD PTR OPR
其中,OPR为存储器寻址方式,它指出存放子程序入口地址的首地址。
这条指令执行时,首先根据指令中规定的寻址方式找到子程序入口地址的首地址,读取连续4字节单元的内容,并将前2字节内容送给IP,后2字节内容送给CS。
表4.13中的EA即为这个程序入口地址的首地址。
压栈:CS先入栈,IP后入栈。CS:IP——高位高地址,先入栈
赋值:存储器寻址前两字节先给IP,再把后两字节给CS。CS:IP——IP先赋值
出栈:IP先出栈,CS后出栈。CS:IP——低位低地址,先出栈,IP先赋值
② 返回指令
(Return段内返回、Return Far段间返回)
有如下两种格式的返回指令:
RET/RETF ;段内/段间子程序返回指令
RET n/RETF n ;带偏移量n的段内/段间子程序返回指令
返回指令从堆栈中弹出断点,恢复原来的CS和IP,返回到主程序继续往下执行。
返回指令通常是子程序执行的最后一条指令。
针对段内调用和段间调用,每种格式的返回指令都相应地有段内返回和段间返回。
段内返回指令:从栈顶弹出两字节送给IP,并相应地修改SP的值。
段间返回指令:从栈顶弹出4字节分别送给IP、CS,并相应地修改SP的值。
它们完成不同的功能,汇编形成不同的机器代码,但汇编指令使用的格式都是一样的。
带偏移量的返回指令中,参数n是一个常数或表达式。
若为表达式,则计算后形成不带符号的16位位移量。
带偏移量的返回指令能在断点出栈后,再将SP加上这个位移量。
如果主程序在调用子程序时,需要给子程序传递一些参数,则可在转到子程序前将这些参数压入堆栈,供子程序使用。子程序返回后,这些参数不再有用,这时使用带偏移量的返回指令,可使SP指向参数入栈前的单元。
调用指令和返回指令不影响状态标志位。
③ 中断和中断返回指令
中断指令:INT软中断指令、溢出中断指令INTO、中断返回指令IRET。
有关这些指令的使用格式及指令功能等,将在后面章节中进行介绍,在此不再赘述。
4.3.7 处理器控制指令
处理器控制指令:用于控制CPU的动作——设置或清除某些标志位、实现对CPU的管理等。
(1)标志位处理指令
标志位处理指令:设置或清除某个指定的标志位,而不影响其他标志位。
它们都是无操作数指令。
指令汇编格式及功能如表4.14所示。
CLD、STD在串操作已经讲过。
(2)其他处理器控制指令
其他处理器控制指令如表4.15所示,这些指令的共同特点是执行结果不影响标志位。
① 停机指令 HLT
(Halt 暂停,停止)
HLT指令使CPU进入暂停状态。
只有当下面3种情况之一发生时,CPU才退出暂停状态:
(Ⅰ)CPU的复位输入端RESET线上有复位信号。
(Ⅱ)非屏蔽中断请求输入端NMI线上出现请求信号。
(Ⅲ)可屏蔽中断输入端INTR线上出现请求信号且标志寄存器的中断标志IF=l。
② 等待指令 WAIT
执行WAIT指令时,测试CPU的 TEST 引脚电平,决定是否进入等待状态。
若 TEST线为高电平时,CPU就进入等待状态,且每隔3个时钟周期对TEST引脚进行一次测试,直到 TEST引脚变为低电平,CPU 才结束等待状态。
③ 总线封锁前缀指令LOCK
LOCK是一条前缀指令,可放在任何指令的前面,使相应指令执行时,总线被锁定,不允许其他主设备使用总线。直至此指令执行完毕后,CPU才响应其他设备的总线请求。
④ 空操作指令 NOP
CPU执行NOP指令时不完成任何具体功能也不影响标志位,只占用机器的3个时钟周期。空操作指令通常用于延时程序。
⑤ 交权指令 ESC
(Escape 转交)
ESC指令主要用于CPU与外部处理器(如协处理器8087)配合工作,指示协处理器完成外部操作码指定的功能。
格式:
ESC DATA, SRC
其中,DATA是一个外部操作码(6位),控制协处理器完成某种指定的操作。
4.4 例题解析
……
4.5 本章实验项目
【实验】 DOS常用命令练习
提前在某盘下建立自定义名字的文件夹,并存入若干文件。然后切换到“命令提示符窗口”,如图所示。在默认盘符下分别练习cd、dir、rename、copy、del等常用DOS命令。
提示:
cd进入已有路径(即对应Windows的文件夹);
dir查看当前路径下目录;
rename给已有文件重新命名;
copy复制文件;
del删除文件;
4.5.1 DOS基本概念
DOS(Disk Operating System):磁盘操作系统,是一个基于磁盘管理的操作系统,在微软公司的 WIndows 2000发布之前,DOS系统基本统治着个人操作系统世界。
随着Windows 操作系统的风行,DOS系统已逐渐成为历史,失去了往日的风采。但是仍有很多用Windows操作系统难以解决的问题,这是用DOS命名来解决这些问题,往往事半功倍。
4.5.2 DOS命令分类
(1)内部命令
内部命令是DOS命令中常驻内存的一部分,它是在当系统作冷、热启动时由磁盘上的系统文件装入内存的。用户使用时仅仅是调用内存中系统区的某一程序段来执行。例如,DIR、TYPE、COPY 等命令都属于内部命令。
(2)外部命令
外部命令是以可执行的程序文件形式(通常扩展名为.exe或.com)存在于磁盘上。这就意味着该命令文件必须记录在磁盘或已插入驱动器的软盘上,否则 DOS是找不到该命令的。例如,FORMAT.COM、DISKCOPY.COM等命令都属于外部命令。
(3)批处理命令
在使用磁盘命令过程中,有时需要连续使用几条DOS命令,有时则要多次重复使用若干条DOS命令,还有的时候需要有选择地使用某些DOS命令。为了满足这些要求,DOS提供了一些特殊文件——后缀为BAT的文件。该文件允许用户组织键盘命令语言程序,一次建立,多次执行。这个 BAT文件可用字处理软件来建立。最典型的例子是在 DOS系统盘上一个名为AUTOEXEC. bat的批命令文件,当系统作冷、热启动时,机器会自动执行该文件上的DOS命令。
4.5.3 常用DOS命令
(1)MD——建立子目录
功能:创建新的子目录。
类型:内部命令。
格式:MD[盘符:][路径名]<子目录名>。
使用说明:
① “盘符”:指定要建立子目录的磁盘驱动器字母,若省略,则为当前驱动器。
② “路径名”:指定要建立的子目录的上级目录名,若缺省则建在当前目录下。
例如:a.在C盘的根目录下创建名为FOX的子目录;b.在 FOX子目录下再创建USER子目录。
C:\> MD FOX (在当前驱动器C盘下创建子目录 FOX)
C:\> MD FOX \USER (在 FOX子目录下再创建 USER子目录)
(2)CD—改变当前目录
功能:显示当前目录。
类型:内部命令。
格式:CD[盘符:][路径名][子目录名]。
使用说明:
① 如果省略路径和子目录名,则显示当前目录。
② 如果采用“CD\”格式,则退回到根目录。
③ 如果采用“CD ..”格式,则退回到上一级目录。
例如:a.进入USER子目录;b.从USER子目录退回子目录;c.返回根目录。
C:\> CD FOX \ USER (进入FOX子目录下的USER子目录)
C:\FOX\USER> CD .. (退回上一级根目录)
C:\FOX> CD\ (返回根目录)
C:\>
(3)RD——删除子目录命令
功能:从指定的磁盘删除目录。
类型:内部命令。
格式:RD[盘符:][路径名][子目录名]。
使用说明:
① 子目录在删除前必须是空的,也就是说需要先进入该子目录,使用 DEL(删除文件的命令)将其子目录下的文件删空,然后再退回上一级目录,用RD命令删除该子目录本身。
② 不能删除根目录和当前目录。
例如,要求把C盘FOX子目录下的USER子目录删除,操作如下:
第一步,先将USER子目录下的文件删空:
C:\> DEL C:\FOX\USER\*.*
第二步,删除USER子目录:
C:\> RD C:\FOX\USER
(4)DIR——显示磁盘目录命令
功能:显示磁盘目录的内容。
类型:内部命令。
格式:DIR[盘符][路径][/P][/W]。
使用说明如下。
① /P的使用:当要查看的目录太多,无法在一屏完整显示完,屏幕会一直往上卷,不容易看清。加上/P参数后,屏幕上会分面一次显示23行的文件信息,然后暂停,并提示;Press any key to continue。
② /W的使用:加上/W只显示文件名,文件大小及建立的日期和时间则都省略。加上参数后,每行可以显示五个文件名。
(5)PATH——路径设置命令
功能:设备可执行文件的搜索路径,只对文件有效。
类型:内部命令。
格式:PATH[盘符1]目录[路径名1]{[;盘符2:],<目录路径名2>…}。
使用说明:
① 当运行一个可执行文件时,DOS 会先在当前目录中搜索该文件,若找到则运行之;若找不到,则根据PATH命令所设置的路径,顺序逐条地到目录中搜索该文件。
② PATH命令中的路径,若有两条以上,各路径之间以一个分号“;”隔开。
③ PATH命令有三种使用方法:
PATH[盘符1:][路径1][盘符2:][路径2]…(设定可执行文件的搜索路径)
PATH:(取消所有路径)
PATH:(显示目前所设的路径)
(6)TREE——显示磁盘目录结构命令
功能:显示指定驱动器上所有目录路径和这些目录下的所有文件名。
类型:外部命令。
格式:TREE[盘符:][/F][/PRN]。
使用说明:
① 使用/F参数时显示所有目录及目录下的所有文件,省略时,只显示目录,不显示目录下的文件。
② 选用/PRN参数时,则把所列目录及目录中的文件名打印输出。
(7)DELTREE——删除整个目录命令
功能:将整个目录及其下属子目录和文件删除。
类型:外部命令。
格式:DELTREE[盘符:]<路径名>。
使用说明:该命令可以一步就将目录及其下的所有文件、子目录、更下层的子目录一并删除,而且不管文件的属性为隐藏、系统或只读。使用时务必小心。
(8)CHKDSK——检查磁盘当前状态命令
功能:显示磁盘状态、内存状态和指定路径下指定文件的不连续数目。
类型:外部命令。
格式:CHKDSK[盘符:][路径][文件名][/F][/V]。
使用说明:
① 选用[文件名]参数,则显示该文件占用磁盘的情况。
② 选用[/F]参数,纠正在指定磁盘上发现的逻辑错误。
③ 选用[/V]参数,显示磁盘上的所有文件和路径。
(9)DISKCOPY——整盘复制命令
功能:复制格式和内容完全相同的软盘。
类型:外部命令。
格式:DISKCOPY[盘符1:][盘符2:]。
使用说明:
① 如果目标软盘没有格式化,则复制时系统自动进行格式化。
② 如果目标软盘上原有文件,则复制后将全部丢失。
③ 如果是单驱动器复制,系统会提示适时更换源盘和目标盘,请操作时注意分清源盘和目标盘。
(10)SCANDISK——检测、修复磁盘命令
功能:检测磁盘的FAT表、目录结构、文件系统等是否有问题,并可将检查出的问题加以修复。
类型:外部命令。
格式:SCANDISK[盘符1:]{[盘符2:]…}[/ALL]。
使用说明:
① SCANDISK适用于硬盘和软盘,可以一次性指定多个磁盘或选用[/ALL]参数指定所有的磁盘。
② 可自动检测出磁盘中所发生的交叉连接、丢失簇和命令结构等逻辑上的错误,并加以修复。
(11)COPY——文件复制命令
功能:复制一个或多个文件到指定盘上。
类型:内部命令。
格式:COPY [源盘][路径]<源文件名>[目标盘][路径][目标文件名]。
使用说明:
① COPY是文件对文件的方式复制数据,复制前目标盘必须已经格式化。
② 复制过程中,目标盘上相同文件名称的旧文件会被源文件取代。
③ 复制文件时,必须先确定目标盘有足够的空间,否则会出现“insufficient”的错误信息,提示磁盘空间不够。
④ 文件名中允许使用通配符“*”“?”,可同时复制多个文件。
⑤ COPY命令中源文件名必须指出,不可以省略。
⑥ 复制时,目标文件名可以与源文件名相同,称作“同名拷贝”,此时目标文件名可以省略。
⑦ 复制时,目标文件名也可以与源文件名不相同,称作“异名拷贝”,此时,目标文件名不能省略。
⑧ 复制时,还可以将几个文件合并为一个文件,称为“合并拷贝”,格式如下:
COPY;[源盘][路径]源文件名1><源文件名2>…[目标盘][路径]<目标文件名>
⑨ 利用COPY命令,还可以从键盘上输入数据建立文件,格式如下:
COPY CON[盘符:][路径]文件名;
⑩ 注意:COPY命令的使用格式要求源文件名与目标文件名之间必须有空格。
(12)TYPE—显示文件内容命令
功能:显示ASCII码文件的内容。
类型:内部命令。
格式:TYPE[盘符:][路径]<文件名>。
使用说明:
① 显示由ASCII码组成的文本文件,对以.exe,.com 等为扩展名的文件,其显示的内容是无法阅读的,没有实际意义。
② 该命令一次只可以显示一个文件的内容,不能使用通配符。
③ 如果文件有扩展名,则必须将扩展名写上。
④ 当文件较长,一屏显示不下时,可以按以下格式显示:
TYPE[盘符:][路径]<文件名> |MORE
MORE为分屏显示命令,使用此参数后当满屏时会暂停,按任意键会继续显示。
⑤ 若需将文件内容打印出来,可用以下格式:
TYPE[盘符:][路径]<文件名>
(13)REN——文件改名命令
功能:更改文件名称。
类型:内部命令。
格式:REN[盘符:][路径]<旧文件名><新文件名>。
使用说明:
① 新文件名前不可以加上盘符和路径,因为该命令只能对同一盘上的文件更换文件名。
② 允许使用通配符更改一组文件名或扩展名。
(14)DEL——删除文件命令
功能:删除指定的文件。
类型:内部命令。
格式:DEL[盘符:][路径]<文件名>[/P]。
使用说明:
① 选用/P参数,系统在删除前询问是否真要删除该文件,若不使用这个参数,则自动删除。
② 该命令不能删除属性为隐含或只读的文件。
③ 在文件名称中可以使用通配符。
④ 若要删除磁盘上的所有文件(DEL *·*或DEL·),则会提示:(Are you sure?)(你确定吗?)若回答Y,则进行删除,回答N,则取消此次删除任务。
(15)CLS——清屏幕命令
功能:清除屏幕上的所有显示,光标置于屏幕左上角。
类型:内部命令。
格式:CLS。