汇编语言程序设计(第3版)
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

第3章 指令系统

本章以8086/8088指令系统为基础,从介绍指令的基本格式,操作数的寻址方式入手,讲述指令系统中各类指令的功能、使用方法和应用场合,并根据处理器的发展历程,讲述80386和Pentium处理器新增的指令。

3.1 指令格式

计算机的指令系统是指微处理器所能执行的各种指令的集合。微处理器的主要功能必须通过它的指令系统来实现。不同的微处理器有着不同的指令系统,其中每条指令与微处理器的一种基本操作相对应,这在设计微处理器时就已确定。

8086/8088指令系统包括上千条指令,这里所谓的指令可分为机器指令和汇编指令。机器指令用二进制代码表示,便于计算机识别,但不利于用户记忆和交流;汇编指令则用一些简单的符号来表示机器指令,以弥补机器指令的不足。若无特殊说明,本书此后所论及的指令均为汇编指令。

微型计算机的每一条指令通常由两部分构成。

1.操作码(OP-Code)

操作码指示计算机所要执行的操作类型。CPU执行指令时,首先将操作码从指令队列送到执行部件EU中的控制单元,经指令译码器分析识别后,产生执行本指令操作所需的时序控制信号,控制计算机完成规定的操作。

2.操作数(Operand)

操作数指出指令执行操作所需的数据或操作结果存放的位置。有两个操作数的指令中一个操作数称为源操作数,另一个称为目的操作数。源操作数和目的操作数是参加操作的两个操作数,而目的操作数又存放操作的结果,也就是说,运算后,参加运算的一个操作数将会丢失,但一般情况下不关心这个问题。如果以后的运算中还会用到这个操作数的话,则应在运算之前将其送到其他位置。

指令的基本格式如图3.1所示。

图3.1 指令的基本格式

3.2 寻址方式

计算机是靠指令加工处理信息的,信息寄存在寄存器中或存储在存储器中,执行指令时往往要从寄存器或存储器中取出信息,加工处理后又存放到寄存器或存储器中。对于一条具体的指令,如何在寄存器或存储器中找到指令执行时所需的信息,如何确定执行结果的存放位置?这就需要了解寻址方式。寻址方式说明如何寻找操作数,以及如何确定执行结果存放位置。8086/8088指令涉及四种操作数:隐含操作数,立即操作数,寄存器操作数,以及存储器操作数。由此就有对应的4类寻址方式。

3.2.1 固定寻址(Inherent Addressing)

这种寻址方式下,操作数隐含在指令中。8086/8088的某些单操作数指令规定操作数在CPU的某个固定的寄存器中,而这个寄存器又隐含在操作码中,例如:加法的十进制调整指令DAA,其操作数总是固定隐含在AL中;还有的双操作数指令,例如寄存器的入栈、出栈指令只给出一个操作数,而另一个操作数被固定隐含在栈顶。

使用这种寻址方式的指令不需要计算存储器的有效地址EA,执行速度较快。

3.2.2 立即寻址(Immediate Addressing)

这种寻址方式下,操作数以常量的形式出现在指令中。操作数随着指令一起进入指令队列,不必执行总线周期,故称为立即数。立即数可以为8位,也可以为16位。如果是16位数,则低字节存放在低地址单元中,高字节存放在高地址单元中。立即寻址方式用来表示常数,它常用于给寄存器赋初值,立即数只能用做源操作数,不能用做目的操作数。

【例3.1】MOV CX,9;立即数9作为源操作数赋给寄存器CX。

【例3.2】MOV AX,5807H

指令执行后(AX)=5807H,如图3.2所示。图中指令存放在代码段中,OP表示该指令的操作码部分,5807H则为立即数,它是指令的一部分。

图3.2 指令执行情况

3.2.3 寄存器寻址(Register Addressing)

这种寻址方式下,操作数为通用寄存器或段寄存器(CS除外)。16位寄存器操作数可以是AX、BX、CX、DX、SI、DI、SP、BP、DS、SS及ES;8位寄存器操作数可以是AL、AH、BL、BH、CL、CH、DL和DH。这种寻址方式由于操作数就是CPU的寄存器,不需要执行总线周期,执行速度较快。因此,在既可使用寄存器寻址又可使用后述的存储器寻址的场合,常常选用前者。

【例3.3】MOV AX,CX

若指令执行前(AX)=9602H,(CX)=2081H;则指令执行后,(AX)=2081H,(CX)内容保持不变。指令执行情况如图3.3所示。

图3.3 寄存器寻址方式执行情况

3.2.4 存储器寻址

存储器寻址方式下,操作数一般是代码段之外的数据段、堆栈段、附加数据段中的存储单元,指令给出的是存储单元的地址或产生存储单元地址的表达式。执行此类指令时,CPU首先根据操作数字段提供的地址信息,由执行部件EU计算出有效地址EA(EA是一个不带符号的16位数据,代表操作数地址离段首地址的距离,即该地址到段首地址的字节数),再由总线执行部件BIU根据公式PA=(16×段首地址)+EA计算出操作数的物理地址。

一般而言,一条指令的目的操作数和源操作数不能同为存储器操作数。存储器寻址方式按EA计算方法的不同可以分为5种。

1. 直接寻址(Direct Addressing)

格式:(1)[常量]

(2)变量

直接寻址是最简单的存储器寻址。这种寻址方式下,操作数的有效地址由指令直接给出,是带有方括号的常量或是变量。

需要说明的是,该方式下,操作数的段地址默认为在数据段寄存器DS中,即DS为默认段寄存器。因此,直接寻址方式下,作为操作数的存储单元的物理地址为:

PA=16×(DS)+nn

这里nn表示常量或变量的偏移地址。

【例3.4】MOV AL,[1000H];将DS段1000H单元的内容传送到AL中。

【例3.5】MOV AX,[1000H]

与上一条指令不同的是,指令执行后将DS段中偏移地址为1000H的字单元内容传送到AX,即低地址1000H对应AL,高地址1001H对应AH。若(DS)=2000H,(21000H)=3412H,则该指令执行后(AX)=3412H。指令执行情况如图3.4所示。

图3.4 指令执行情况

使用直接寻址方式时应注意:

(1)直接地址可用数值表示,包括在[ ]之中,也可以用变量表示,例如:MOV AL,VALUE;这里,VALUE为变量,变量是有属性的,它由数据段中定义数据的伪指令确定(伪指令将在第4章介绍)。

(2)若操作数在代码段、堆栈段或附加段中,则应在操作数地址之前使用前缀指出段寄存器名,这种前缀称为段超越前缀。例如:

      MOV  AL,ES:[2000H]    ;将ES段2000H单元的内容传送到AL中

段超越前缀也可以用于其他存储器寻址方式中,以使得给定的段寄存器取代默认的段寄存器。

(3)直接寻址方式适合于处理存储器的单个单元。IBM-PC机中为了避免指令字的长度过长,规定双操作数指令除立即寻址方式之外必须有一个操作数使用寄存器或段寄存器,这就是一个变量常常先要送到寄存器中去的原因。

2.寄存器间接寻址(Register Indirect Addressing)

格式:[BX、BP、SI或DI]

这种寻址方式下,操作数的有效地址EA不像直接寻址那样直接放在指令中,而是由基址寄存器BX、BP或变址寄存器SI、DI之一给出,即

如果指令中使用的是BX、SI和DI,则操作数在数据段中,且用数据段寄存器DS中的内容作为段地址,即操作数的物理地址为

【例3.6】MOV AL,[BX];设BX的内容为1000H,则指令功能是将DS段1000H单元的内容传送到AL中。

【例3.7】MOV AX,[BX]

设(DS)=2000H,(BX)=1000H,(21000H)=3412H,物理地址PA=16×2000H+1000H=20000+1000H=21000H,执行情况如图3.5所示。

图3.5 寄存器间接寻址执行情况

指令执行后,(AX)=3412H。

若指令中使用的是BP,则操作数在堆栈段中,用堆栈段寄存器SS中的内容作为段地址,即操作数的物理地址为

PA=16×(SS)+(BP)

寄存器间接寻址通常用来对一维数组进行处理。只需在执行完一条指令后改变间接寻址寄存器BX、BP、SI和DI中的内容,就可以使用同一个地址表达式来指定一维数组中的不同元素,从而对连续的存储器单元进行存/取操作。

3. 基址寻址(Based Addressing)

格式:偏移量[BX或BP]

其中偏移量可以是常量或变量,还可以表示为[BX或BP][偏移量]和[BX或BP+偏移量]。

这种寻址方式与寄存器间接寻址方式的区别仅在于,只能使用基址寄存器BX和BP,且指令中还要指定一个8位或者16位的偏移量,操作数的有效地址EA等于基址寄存器BX或BP的内容与偏移量之和,该偏移量在两个字节范围内,即

【例3.8】MOV AL,80H[BP];设BP内容为2040H,则指令功能是将堆栈段20C0H单元的内容传送到AL中。

该指令又可表达为

                  MOV  AL,[BP][80H]
                或MOV  AL,[BP+80H]

【例3.9】MOV AX,COUNT[BX]

设(DS)=2000H,(BX)=1000H,COUNT=3000H,(24000H)=1058H,其寻址示意图如图3.6所示。指令执行后,(AX)=1058H。

图3.6 基址寻址方式执行情况

基址寻址通常也用来访问一维数组中的元素,用偏移量来确定数组的起点,基址寄存器的值选择一个元素。与寄存器间接寻址一样,因数组中所有元素具有相同的长度,只要改变基址寄存器的内容,就可以使用同一个地址表达式选择数组中任意的元素。

4.变址寻址(Indexed Addressing)

格式:偏移量[SI或DI]。

其中偏移量可以是常量或变量,其他的表示形式类似于基址寻址方式,只需将基址寄存器BX、BP换成变址寄存器SI、DI即可。变址寻址中总是使用段寄存器DS的内容作段首地址,操作数的有效地址EA等于间址寄存器内容和位移量之和,即

变址寻址方式同样适合处理数组,通常SI用于源数组的变址寻址,DI则用于目的数组的变址寻址。

   例如: MOV AX,ARRAY1[SI]
          MOV ARRAY2[DI],AX

其中ARRAY1和ARRRAY2分别用来表示源数组和目的数组的起点。若用上述两条指令,配上修改SI、DI值的指令,构成循环,就可实现将源数组移动到目的数组的目的。

5.基址变址寻址(Based Indexed Addressing)

格式:偏移量[BX或BP+SI或DI]。

其中偏移量可以是常量或变量,其他的表示形式类似于基址寻址方式,只需将基址寄存器换成基址+变址寄存器即可。

这种寻址方式下,存储器操作数的有效地址EA是指令指定的基址寄存器BX、BP之一与变址寄存器SI、DI之一的内容以及偏移量三者之和,即

这里共有四种组合情况,并且根据基址是在BX还是在BP中,确定寻址操作数是在数据段还是堆栈段。对于前者,段寄存器使用DS;对于后者,段寄存器使用SS。基址变址寻址的操作数物理地址为

【例3.10】MOV AX,3000H[BX+SI]

设(DS)=1000H,(BX)=0400H,(SI)=1260H;

则:EA=0400H+1260H+3000H=4660H

PA=10000H+4660H=14660H

指令的执行情况如图3.7所示。指令执行后,(AX)=1058H。

图3.7 基址变址寻址方式执行情况

应当注意以下指令是错误的:

          MOV  AX,30H[BX][BP]
          MOV  AX,10H[SI][DI]

基址变址寻址方式同样适合数组或表格的处理,由于基址和变址寄存器中的内容都可以改变,在处理二维数组时尤为方便。

这种寻址方式中,若偏移量为0,则偏移量可默认。即指令

          MOV  AX,0[BX+SI]

可表示为

          MOV  AX,[BX+SI]

3.3 指令的执行时间

通常,一条指令的执行时间是指计算机取指令、取操作数、执行指令及传送结果各个阶段所需时间的总和。如果要详细讨论各种指令的执行时间是一个比较复杂的问题,这里只作简单介绍。

由于指令是存放在存储器中,因此运算器要执行指令就需先访问存储器,但是8086/8088 CPU的执行部件EU和总线接口部件BIU是并行工作的,BIU可以预先把指令取到指令队列缓冲器中存放,形成了取指和执行的并行,这样,在计算指令的执行时间时,就不把取指时间计算在内。

执行指令的时间,除了EU中的基本执行时间外,有些指令在执行过程中可能需要多次访问内存,包括取操作数和存放操作数结果等,要执行总线的读/写周期,这样,执行一条指令的时间就是指令的基本执行时间及存取操作数时间的总和。指令的基本执行时间因指令的不同而异,相互之间有很大的差别,而存取操作数所需的计算有效地址EA的时间又随寻址方式的不同而异。

表3.1表示执行几种不同的指令所需的时间;表3.2表示执行加法时不同的寻址方式所需的时间;表3.3表示在不同的寻址方式下计算EA所需的时间。在这些表格中,所有的时间都以时钟周期数表示(计算机是按照节拍工作的,这里所说的节拍称为时钟周期)。

表3.1 指令的基本执行时间举例

表3.2 加法指令的执行时间

表3.3 计算有效地址EA所需的时间

从表3.1至表3.3可以看出,不仅不同指令的执行时间差别很大,而且同一种指令使用不同的寻址方式时执行时间的差别也是很大的,通过以下例题可以对指令的执行时间有一个具体的了解。

【例3.11】设8086的时钟频率为5MHz(即时钟周期=0.2μs),试求两个字节相加的ADD指令在各种寻址方式下,指令的执行时间t。

(1)目的操作数和源操作数均为寄存器操作数。需要3个时钟周期,即

t=3×0.2=0.6(μs)

(2)目的操作数为寄存器操作数,源操作数为基址变址寻址的存储器操作数。需要的时钟数为

t=9+EA=9+12=21(T)

第一项的9为这种寻址方式下指令的基本运算和基本操作时间,第二项为计算EA的时间,即

t=21×0.2(μs)=4.2(μs)

(3)目的操作数为基址变址寻址的存储器操作数,源操作数为寄存器操作数。需要的时钟数为

t=16+EA=16+12=28(T)=5.6(μs)

第一项的16为这种寻址方式下指令的基本运算和基本操作时间,第二项为计算EA的时间。

对于其他的寻址方式下ADD指令的执行时间,请读者练习计算。

从上述的例子可以看出:对于同一条ADD指令而言,因寻址方式不同,执行指令的时间也不同,即执行的效率有差异,有时这种差异还很大。从表3.2还可以得知,同一类的指令使用不同的寻址方式时指令的长度也不一样,占用存储器的字节数差异也很大。当一个实际的应用程序要求运行效率较高和占用空间较小时,程序的设计者不仅要研究算法、数据结构,还要研究指令与寻址方式的选用,才能编制出高效而简洁的程序。

3.4 Intel8086/8088指令系统

8086/8088指令系统包括约百种指令助记符,它们与寻址方式结合,再加上操作数字节数的不同,可以构成上千种指令。这些指令按照功能可分为6类。

(1)数据传送指令;

(2)算术运算指令;

(3)位操作指令;

(4)串操作指令;

(5)转移指令;

(6)处理器控制指令。

本节将主要介绍前3类指令的格式和功能,后3类指令本节仅作概要介绍,详情将在后面的相关章节中讲解。为便于指令的介绍,现作以下约定。

(1)指令中的IMM表示立即数,IMM8仅表示8位立即数,IMM16仅表示16位立即数。

(2)指令中的REG表示寄存器操作数。它可代表8位寄存器AH、AL、BH、BL、CH、CL、DH和DL,以及16位寄存器AX、BX、CX、DX、SP、BP、SI和DI。REG8仅表示8位寄存器,REG16仅表示16位寄存器。指令中的SEGREG表示段寄存器操作数。它可代表CS、DS、ES及SS。

(3)指令中的MEM表示存储器操作数,它可以表示任何一种存储器寻址方式下的操作数。MEM8、MEM16及MEM32分别表示8位、16位及32位存储器操作数。

(4)在对指令功能作说明时,用圆括号来表示所括起部分的内容。例如用(AX)表示AX的内容,用(2040H)表示偏移地址为2040H的存储单元的内容。

(5)在指令的图示中,对于存储器单元一般只标出偏移地址。

3.4.1 数据传送指令

数据传送指令用于寄存器、存储器或输入/输出端口之间传送数据或地址,这类指令共14条,按其特点可分为四组,如表3.4所示。

表3.4 数据传送指令表

1.通用数据传送指令

(1)MOV目的操作数,源操作数把源操作数传送到目的操作数。

      【例3.12】MOV   AX,BX          ;AX←(BX), 即将BX的内容传送到AX中;
                MOV   CL,80H         ;CL←80H;
                MOV   AL,[2000H]     ;AL←(2000H), 即将EA为2000H(段地址取DS的值)
                                     单元的值传送到AL中。

说明:

① 源操作数不变(一般带有源操作数的8086/8088指令都不改变源操作数的内容,本书对于例外指令将予以说明)。

② 立即数、段寄存器CS不能作为目的操作数。源操作数和目的操作数不能同为存储器操作数,如图3.8所示。

图3.8 数据传送关系图

以下指令均是错误的:

MOV 64H, BL

MOV CS, AX

MOV [2000H], [BX]

③ 源操作数和目的操作数类型必须一致,即同为单字节数,或同为双字节数,当指令中只有一个操作数的类型明确时,另一个操作数被视为同一类型。可以用“BYTE PTR”或“WORD PTR”将一个存储器操作数定义为字节或字类型。当存储器操作数为字类型时,寄存器或立即数的高字节对应其高地址,低字节对应其低地址(这种对应关系亦适用于其他指令)。

④ MOV指令的执行不影响标志寄存器。

【例3.13】MOV [2000H],AX

将AX的内容传送到偏移地址为2000H的存储单元中。指令执行情况如图3.9所示。

图3.9 指令执行情况

【例3.14】MOV WORD PTR [BX+SI],80H

当(BX)=2080H,(SI)=1040H时,表示将0080H传送到偏移地址为30C0H的存储单元中。指令执行情况如图3.10所示。

图3.10 指令执行情况

(2)PUSH源操作数

将源操作数压入堆栈。先将SP的内容减2,再将双字节的源操作数传送到SP所指示的堆栈栈顶。

      【例3.15】PUSH  BX         ;指令执行情况如图3.11所示。

图3.11 指令执行情况

(3)POP目的操作数

将栈顶内容弹至目的操作数。先将SP所指单元也即堆栈栈顶的一个字传送到目的操作数,并将SP的内容加2。

【例3.16】POP AX

若该指令紧接在上述PUSH BX指令之后,则指令执行情况如图3.12所示。

图3.12 指令执行情况

堆栈是一个非常有用的存储结构,它遵循先进后出的原则,通常用于子程序的调用和返回,现场的保护和恢复等场合。PUSH和POP指令一般应配对使用。使用时需注意以下4点:

① 堆栈操作指令中,有一个操作数是隐含的,这个操作数就是(SP)指示的栈顶存储单元。

② 8086/8088堆栈操作都是字操作,不允许对字节操作。例如:PUSH AH不是正确指令。

③ 每执行一条入栈指令,(SP)自动减2,高字节和低字节先后入栈;执行出栈指令时,则相反,低字节和高字节先后出栈,(SP)自动加2。

④ CS寄存器可以入栈,但不能随意弹出一个数据到CS。

【例3.17】设在一个被调用的子程序中,要使用到AX、BX、CX和DX,子程序的运行还可能影响状态标志。为了使这些寄存器中的数据不被破坏,进入子程序时先予以入栈保护,子程序结束前再做出栈恢复。子程序中保护和恢复的程序段为

          SUB1 PROC  NEAR          ; 定义过程
                PUSHF
                PUSH  AX
                PUSH  BX
                PUSH  CX
                PUSH  DX            ; 保护现场
                ┇
                POP  DX
                POP  CX
                POP  BX
                POP  AX
                POPF                ; 恢复现场
          SUB1 ENDP                 ; 过程结束

(4)XCHG目的操作数,源操作数

把源操作数和目的操作数互换。说明:该指令与MOV指令功能上的区别有两点,其一,该指令不允许使用立即数和段寄存器作为操作数;其二,该指令改变源操作数的内容。

2.累加器专用传送指令

(1)XLAT,把(BX)与(AL)相加形成有效地址EA,将该单元中的单字节数传送到AL中。

      【例3.18】MOV BX,4C02H
                MOV  AL,1DH
                XLAT

有关存储单元情况如图3.13所示,则执行上述指令后,(AL)=55H。

这是一条专门用于AL和字节表中某一存储单元之间进行数据传送的指令。字节表的首地址在BX中,根据AL设置的偏移地址,就可以将该单元的内容传送到AL中。

(2)IN累加器,端口地址

从指定端口输入一个字节到AL或输入一个字到AX。端口地址以数值形式给出或通过DX间接给出。当端口地址大于255时,则只能由DX间接给出。

   【例3.19】IN AX,16H         ; 从端口16H输入一个字到AX中。

图3.13 执行情况

      【例3.20】MOV DX,280H
                IN  AL,DX           ; 从端口280H输入一个字节到AL。

说明:该指令及后述的OUT指令是专用于累加器和I/O端口之间进行数据传送的指令,操作数的确定方式有别于前述寻址方式。其一,端口地址不加“[ ]”;其二,使用DX作为专用间址寄存器。

(3)OUT端口地址,累加器

实现输出,即与IN指令相反方向的数据传送。

3. 地址传送指令

地址传送指令实现操作数地址的传送。

(1)LEA目的操作数,源操作数

将源操作数的有效地址EA传送给通用寄存器。

      【例3.21】MOV   BX,0408H
                MOV   SI,2000H
                LEA    BP,[BX+SI+6]   ; 将240EH送BP(而不是将240EH单元的内容送BP!)。

(2)LDS目的操作数,源操作数

将源操作数指定的存储单元中的双字(通常为段地址和有效地址)传送给DS及目的操作数,高两字节送DS,低两字节送目的操作数。

【例3.22】已知(DS)=30C0H,相关存储区情况及执行以下指令的功能如图3.14所示。

图3.14 指令执行情况

          LDS   SI,[40H]

(3)LES目的操作数,源操作数

与LDS指令的区别仅在于,传送地址时将高两字节送ES,而不是送DS。

说明:

① 地址传送指令的源操作数必须是存储器操作数,目的操作数必须是16位通用寄存器。

② LEA指令与LDS、LES指令所传送的有效地址有区别。LEA指令所传送的有效地址为源操作数的有效地址,而LDS及LES指令所传送的有效地址在源操作数所指的存储单元中。

4. 标志传送指令

这组指令专用于对标志寄存器操作。如前所述,8086/8088标志寄存器具有16位,LAHF和SAHF仅对其低8位操作,而PUSHF和POPF对整个标志寄存器操作。

(1)LAHF,将标志寄存器低8位送AH。

(2)SAHF,将(AH)送标志寄存器低8位。

(3)PUSHF,与PUSH指令功能相似,该指令的特殊之处仅在于,压入堆栈的是标志寄存器的内容。

(4)POPF,与POP指令功能相似,该指令的特殊之处仅在于,弹出的堆栈的内容是送标志寄存器。

标志传送指令中SAHF和POPF指令将直接影响标志寄存器的内容。利用这一特性,可以方便地改变标志寄存器中指定位的状态。

说明:数据传送指令中仅SAHF及POPF影响标志寄存器的内容。

3.4.2 算术运算指令

算术运算指令分为二进制数算术运算指令和BCD数算术运算调整指令。

1. 二进制数算术运算指令

参加算术运算的二进制数可以是单字节数或双字节数,也可以是无符号数或有符号数(有符号数在机器内部以补码形式表示)。由于汇编语言源程序中往往需要判断运算结果是否超出范围,是否为零,是否为负数等,所以二进制数算术运算指令除了产生与操作数位数相同的结果外,还将影响标志寄存器中的相应标志以便在必要时实现上述判断。该类指令共14条,按照四则运算分为4组,如表3.5所示。

表3.5 二进制算术运算指令表

注:×表示根据操作结果设置标志;-表示标志不确定;空白表示标志不受影响。

(1)加法指令

① ADD 目的操作数,源操作数

将源操作数加到目的操作数,同时影响状态标志。

ADD 指令执行后对标志的影响:

● OF 字节运算结果超出字节有符号数的范围(-128~+127)或字运算结果超出字有符号数范围(-32768~+32767)时,OF=1;否则OF=0。在把操作数视为有符数时,可通过该标志了解结果是否溢出。

● SF 运算结果的最高位为1时,SF=1;否则SF=0(即SF与结果的最高位一致)。

● ZF 运算结果为零时,ZF=1;否则ZF=0。

● AF 运算时,D3向D4产生进位时AF=1,否则AF=0。

● PF 运算结果的二进制位1的个数为偶数时,PF=1;否则PF=0。

● CF 运算时最高位产生进位时,即字节运算结果超出字节无符号数的范围(0~255),字运算结果超出字无符号数的范围(0~65535)时,CF=1;否则CF=0。在把操作数视为无符号数时,可通过该标志了解结果是否溢出。

【例3.23】ADD AL,BL

设(AL)=0A4H,(BL)=5CH,则指令执行后,(AL)=0,OF=0,SF=0,ZF=1,AF=1,PF=1,CF=1。

编程者往往要根据不同情况关心不同的标志位。例如:执行该指令时,机器并不能判别所得数据是有符号数还是无符号数,而编程者在使用此指令及其他相关指令时总是为了解决某一个具体问题,从而对这一点是清楚的。因此,在使用此指令后,将会根据所得数据是有符号数还是无符号数来关心不同的标志位。

当把相关数据视为有符号数,从OF=0可知相加结果未溢出,也即AL的内容“0”就是两个有符号数0A4H(-01011100B)和5CH(+01011100B)之和。

当把相关数据视为无符号数,从CF=1可知相加结果产生进位也即“100000000B”就是两个无符号数0A4H(10100100B)和5CH(01011100B)之和。

说明:源操作数和目的操作数类型必须一致,即同为字节或同为字,且两者不能同为存储器操作数(这一点适用于所有双操作数的算术运算指令)。

② ADC目的操作数,源操作数

功能与ADD指令基本相同,唯一区别是:将该指令执行前的CF值加至目的操作数中。该指令主要用于多字节加法运算。

      【例3.24】MOV  DX,  2000H
                MOV  AX,  8A04H
                ADD  AX,  9D00H
                ADC  DX,  45H

此程序段实现多字节数20008A04H与459D00H的相加。DX存放有被加数的高两字节,AX存放有被加数的低两字节。ADD指令实现两数低两字节的相加,相加后(AX)=2704H,CF=1(相加结果超过两个字节)。ADC指令实现两数高两字节的相加,且将CF(即低两字节相加产生的进位)加至DX,使DX内容为2046H。

③ INC目的操作数

功能与ADD指令基本相同,区别有两点:其一,隐含的源操作数为1;其二,不影响CF标志。

该指令常用于某些计数器的计数,或用于修改地址。

      【例3.25】MOV  SI, 2000H
                MOV  AL,  [SI]
                INC  SI
                ADD  AL,  [SI]       ;(AL)为2000H单元和2001H单元内容之和

(2)减法指令

① SUB目的操作数,源操作数

将目的操作数减去源操作数,同时产生相应标志。SUB指令执行后对标志的影响与ADD指令基本相同,只不过这里只有“借位”而无“进位”。

② SBB目的操作数,源操作数

功能与SUB指令基本相同,唯一区别是:目的操作数除减去源操作数外,还要减去该指令执行前的CF值。

该指令主要用于多字节减法运算。

③ DEC目的操作数

功能与SUB指令基本相同,区别有两点:其一,隐含的源操作数为1;其二,不影响CF标志。

④ NEG目的操作数

对目的操作数取补码,结果送目的操作数。因为对一个数取补码相当于用0减去此数,所以该指令也属于减法运算指令。

⑤ CMP目的操作数,源操作数

功能与SUB指令基本相同,唯一区别是:目的操作数不被差值取代。该指令的作用在于根据两操作数的大小关系产生状态标志值,以便其后指令根据状态标志确定程序流程。

(3)乘法指令

① MUL源操作数

实现无符号数乘法运算。将(AL)或(AX)作为被乘数,源操作数作为乘数,乘积送AX或送DX、AX。当源操作数为字节操作数时,默认(AL)为被乘数,乘积送AX;当源操作数为字操作数时,默认(AX)为被乘数,乘积高两字节送DX,低两字节送AX。指令执行情况如图3.15所示。

图3.15 乘法指令执行情况

        例如:
          MUL  CL                    ;AL、CL中的无符号数之积送AX;
          MUL  [SI]                  ; 此为错误指令。原因在于,计算机无法判别源操作数是字节操
                                       作数抑或字操作数;
          MUL  WORD  PTR  [SI]       ;AX中的无符号数与SI所指单元的字无符号数相乘,乘积送
                                      DX和AX。

MUL指令影响进位标志CF和溢出标志OF(其他标志不确定)。若乘积高半部分(字节相乘时乘积中的(AH);字相乘时乘积中的(DX))非0,则CF=OF=1;否则,CF=OF=0。也即,CF=OF=1标志着AH或DX中放有乘积的有效位。

② IMUL源操作数

功能与MUL指令基本相同。区别仅在于,该指令实现有符号数乘法运算。此指令执行后,CF=OF=1亦标志着AH或DX中放有乘积的有效位。即标志着(AH)或(DX)不是对应的低半部分的符号扩展。

      【例3.26】MOV AL,0FCH     ;-4送AL;
                MOV  CL,1FH     ;+31送CL;
                IMUL  CL        ;(AL)与(CL)之积-124送AX, 即(AL)=84H,(AH)
                                =FFH。此时CF=OF=0, 标志着(AH)是(AL)的符号扩展。

(4)除法指令

① DIV源操作数

实现无符号数除法运算。以(AX)或(DX)、(AX)中的内容为被除数,源操作数为除数。商送AL或AX,余数送AH或DX。当源操作数为字节操作数时,默认(AX)为被除数,商送AL,余数送AH;当源操作数为字操作数时,默认(DX)、(AX)内容为被除数,商送AX,余数送DX。指令执行情况如图3.16所示。

图3.16 除法指令执行情况

      【例3.27】MOV AX,1001H       ;4097送AX;
                MOV  CL,20H        ;32送CL;
                DIV   CL           ;(AX)与(CL)相除, 商128送AL, 余数1送AH。即(AL)
                                    =80H,(AH)=01H。

DIV指令执行后,各状态标志位不确定。

说明:当源操作数为字节类型时,商的范围为0~255(FFH);当源操作数为字类型时,商的范围为0~65535(FFFFH)。超出范围则产生0号中断。

② IDIV源操作数

功能与DIV指令基本相同,区别在于,该指令实现有符号数除法运算。另外,8086/8088指令系统中规定余数的符号与被除数符号相同。例如:

          MOV  CX,4
          IDIV  CX              ; 若已知DX、AX中放有4001H, 则该指令执行后,(AX)=1000H,
                               (DX)=0001H。若已知DX、AX中放有-4001H, 则该指令执行后,
                               (AX)=F000H(即-1000H),(DX)=FFFFH(即-1)。

③ CBW

将(AL)的符号位扩展到AH中(即使得AH各位与AL最高位相同),该指令常用在IDIV指令之前。CBW指令执行后,各状态标志位不确定。例如:

          MOV  AL,  76H         ;+76H送AL;
          CBW                   ;0076H送AX, 即+0076H送AX。
        又如:MOV  AL,98H       ;-68H送AL;
          CBW                   ;FF98H送AX, 即-0068H送AX。

④ CWD

功能与CBW基本相同,区别仅在于,该指令是将(AX)的符号位扩展到DX中。

2.十进制数算术运算调整指令

前面所述的算术运算都是针对二进制数,但人们最为常用的却是十进制数。在用计算机进行算术运算时,可以先将操作数做十→二进制转换,然后做二进制数算术运算,再将结果做二→十进制转换。为了便于十进制数的运算,8086/8088系统还提供了一组十进制数算术运算调整指令,该类指令将在5.2.1中详细介绍。

3.4.3 位操作指令

8086/8088提供的位操作指令包括逻辑运算指令和移位指令,这类指令可直接对寄存器或存储器操作数的位进行操作,如表3.6所示。

表3.6 位操作指令表

注:×表示根据操作结果设置标志;-表示标志不确定;空白表示标志不受影响。

1. 逻辑运算指令

(1)NOT目的操作数

将目的操作数按位取反,结果送目的操作数。例如

          MOV  AX,0
          NOT  AX         ; 使(AX)为FFFFH。

(2)AND目的操作数,源操作数

将目的操作数与源操作数按位相与,结果送目的操作数。当两个操作数的对应位都为1时,结果的对应位为1;否则为0。该指令常用于屏蔽目的操作的某些位,即使得目的操作数的某些位置0,其余保持不变。例如

          MOV  AL,49H
          AND  AL,0FH   ; 使(AL)=09H, 即屏蔽(AL)的高4位, 而低4位不变。

(3)OR目的操作数,源操作数

将目的操作数与源操作数按位相或,结果送目的操作数。当两个操作数的对应位都为0时,结果的对应位为0;否则为1。该指令常用于使目的操作数的某些位置1,其余位保持不变。

例如(4)XOR目的操作数,源操作数

          MOV  AL,49H
          OR  AL,3CH   ; 使(AL)=7DH, 即使得(AL)中间4位置1, 其余位保持不变。

将目的操作数与源操作数按位作异或操作,结果送目的操作数。当两个操作数的对应位不同时,结果的对应位为1;否则为0。该指令常用于判断两个数中哪些位不同,或用于改变指定位的状态。例如

          MOV  DH,  12H
          XOR  DH,  80H     ; 使(DH)=92H, 即改变DH最高位状态。
    又如:XOR  AX,  AX      ; 使(AX)=0。

(5)TEST目的操作数,源操作数

功能与AND指令基本相同,唯一区别是:目的操作数保持不变。该指令常用于检测某种条件是否满足,但又不希望改变目的操作数的场合。例如

          TEST  AL,01H      ; 可用此指令检测AL最低位的状态。若AL最低位为0, 则两操作数按位相
                                    与的结果为0, 从而ZF=1; 若AL最低位为1, 则结果为1, 从而
                            ZF=0。

说明:逻辑运算指令中的两个操作数不能同为存储器操作数。

2. 移位指令

移位指令包括算术移位指令、逻辑移位指令和循环移位指令。这些指令只有目的操作数而无源操作数,并在指令中给出移位的位数。而且此位数只能用1或CL表示。

(1)算术、逻辑移位指令

算术移位指令(SAL、SAR)用于有符号数,而逻辑移位指令(SHL、SHR)用于无符号数。操作数的左移意味着小数点相对右移,而操作数的右移意味着小数点相对左移。算术、逻辑移位指令的功能如图3.17所示。

图3.17

① SHL/SAL 目的操作数,计数

SHL指令和SAL指令功能完全相同。将目的操作数左移若干位,每左移一位,最低位补0,最高位送CF。该指令可以方便地实现有符号数和无符号数乘以2n的运算(n为移位计数值),不过在使用时要注意是否发生溢出。例如

          MOV  AL,16H
          SHL  AL,  1    ; 使(AL)即00010110B左移1位。移位后,(AL)=00101100B,
                    是原值的2 倍。CF 值为0。
        又如:MOV  BH,0EEH
              MOV  CL,  2
                                SAL  BH,CL  ; 使BH中的有符号数11101110B左移2
                                位。移位后(BH)=10111000B, 是
                            原值的4倍(原值为-00010010B, 即-18; 移位后的值为-01001000, 即
                            -72)。

② SHR 目的操作数,计数

将目的操作数右移若干位。每右移一位,高位均补0,最低位送CF。该指令可以方便地实现无符号数除以2n的运算。

③ SAR 目的操作数,计数

将目的操作数右移若干位。每右移一位,高位均保持不变,最低位送CF。该指令可以方便地实现有符号数除以2n的运算。例如

          MOV  AL,  0F8H
          SAR  AL,  1      ; 使AL中的有符号数11111000B右移1位。移位后(AL)=11111100B, 是
                            原值的1/2(原值为-00001000B, 即-8; 移位后的值为-00000100B, 即-4)。

(2)循环移位指令

循环移位指令的功能如图3.18所示。

图3.18

① ROL 目的操作数,计数

将目的操作数循环左移若干位。每左移一位,左移前的最高位送最低位以及CF。

② ROR 目的操作数,计数

将目的操作数循环右移若干位。每右移一位,右移前的最低位送最高位以及CF。

③ RCL 目的操作数,计数

将目的操作数连同CF循环左移若干位。每左移一位,左移前的最高位送CF,左移前的CF送最低位。

④ RCR 目的操作数,计数

将目的操作数连同CF循环右移若干位。每右移一位,右移前的CF送最高位,右移前的最低位送CF。

      【例3.28】    MOV  DX, 0FFF9H
                    XOR  AX,  AX          ; 使AX,CF清0。
                    SAR  DX,  1
                    RCR  AX,  1
                    SAR  DX,  1
                    RCR  AX,  1

该程序段实现DX、AX中的有符号数FFF90000H右移两位。移位后DX、AX内容为FFFE 4000H,是原值的1/4(原值为-00000000000001110000000000000000B,移位后的值为-00000000000000011100000000000000B)。

3.4.4 串操作指令

串是指存储器中的字节串(字节序列)或字串(字序列)。8086/8088系统对串的操作提供了5种基本的指令以及与之配合使用的重复前缀。它们常用于循环结构,本书将在7.2中对此做介绍。

3.4.5 转移指令

一般情况下指令是顺序地逐条执行的。但有时需要改变这种执行流程,8086/8088系统为此提供了转移指令。转移指令用于分支结构,本书将在6.2中对此做介绍。

3.4.6 处理器控制指令

处理器控制指令用于控制CPU的某些功能,如表3.7所示。

表3.7 处理器控制指令表

3.5 Intel80x86及Pentium指令系统

3.5.1 Intel80386新增和扩充指令

80386是80X86微处理器系列发展中的里程碑。80386指令系统包括了所有80286指令,并对80286的部分指令进行了功能扩充,还新增了一些指令,特别指出的是,80386提供了32位寻址方式可对32位数据直接操作。所有16位指令均可扩充为32位指令。80386有8个32位通用寄存器:EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI。它们分别是原来的16位通用寄存器AX,CX,DX,BX,SP,BP,SI,DI的扩展(详细的寄存器结构在第2.3节已介绍),这些32位通用寄存器的低16位也可以作为16位通用寄存器独立存取数据,也就是说80386对8086,80286是向上兼容的。对于数据段寄存器,80386在原有基础上增加了两个:FS和GS。80386的标志寄存器扩展到了32位,其中某些位没有定义。80386在实地址模式下有9个标志位可用,在保护虚地址模式下有13个标志位可用,扩展后的标志寄存器也可称为E标志寄存器(EFR)EFLAGS。

80386有实地址模式、保护虚地址模式和虚拟8086模式3种工作方式,在DOS环境中只能运行于实地址模式,可做为超高速8086芯片使用。

1. 数据传送与扩展指令

(1)MOVSX寄存器,寄存器/存储器

将源操作数传送到目的操作数中。目的操作可以是16位或32位寄存器;源操作数可以是寄存器或存储器操作数,其位数应小于或等于目的操作数的位数。当源操作数的位数少于目的操作数时,目的操作数的高位用源操作数的符号位填补。此指令适用于有符号数的传送与扩展。该指令不影响状态标志位。

(2)MOVZX寄存器,寄存器/存储器

与MOVSX功能基本相同,唯一区别在于,当源操作数的位数少于目的操作数位数时,目的操作数的高位用“0”填补。该指令适用于无符号数的传送与扩展。例如

          MOV  DL,86H
          MOVSX   AX,DL               ;86H扩展成FF86H送AX;
          MOVSX   ECX,DL              ;86H扩展成FFFF FF86H送ECX;
          MOVZX   BX,DL               ;86H扩展成0086H送BX;
        又如:
          MOV  WORD  PTR[BX],68H      ;0068H送BX所指定的内存单元;
          MOV  AX,[BX]                ;0068H送AX;
          MOVSX  ESI,WORD  PTR[BX]    ; 将 0068H扩展成00000068H送ESI;
          MOVZX  EDI,WORD  PTR[BX]    ; 将0068H扩展成00000068H送EDI。

2. 堆栈操作指令

(1)PUSH 8/16 /32位立即数

将8/16/32位立即数压入堆栈。当然该指令执行后SP的值将减2或者4。通常使用以下方法来区别操作数是8位,16位还是32位立即数。

      【例3.29】PUSH 'A'   ; 将0041H压入堆栈(8位立即数)
                PUSHW  15H   ; 将0015H压入堆栈(16位立即数)
                PUSHD  20H   ; 将00000020H压入堆栈(32位立即数)

在8086/8088指令系统中,PUSH指令允许的操作数只能是两字节的寄存器操作数或两字节的存储器操作数。该指令中如果给出的数不够16或32位,则自动扩展为16或32位后压入堆栈。

(2)PUSHA

将所有通用寄存器AX,CX,DX,BX,SP,BP,SI,DI的内容按顺序压入堆栈,入栈的SP值是执行该指令之前SP的值。在执行完本指令后,SP值减16,如图3.19所示。

(3)PUSHAD

将所有通用寄存器EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI的内容顺序压入堆栈,其中压入堆栈的ESP是该指令执行前ESP的值。执行该指令后,ESP值减32。

图3.19 执行PUSHA指令的堆栈情况

(4)POPA

将栈顶的内容顺序弹至DI,SI,BP,SP,BX,DX,CX,AX(次序与PUSHA指令相反)。SP中的值是堆栈中所有通用寄存器弹出后,堆栈指针实际指向的值(不是栈中保存的SP值),也即该指令执行后SP的值,可以通过加16来恢复,如图3.20所示。PUSHA及POPA均不影响状态标志位。

图3.20 执行POPA指令的堆栈情况

      【例3.30】一个子程序被调用时,保存所有通用寄存器,可用下述指令实现:
                PUSHA
                CALL  SUB1
                POPA

显然使用上述指令比使用PUSH指令和POP指令更方便。

(5)POPAD

将当前栈顶内容顺序弹至EDI,ESI,EBP,ESP,EBX,EDX,ECX,EAX(次序与PUSHAD指令相反),但是最终ESP的值为弹出操作对堆栈指针调整后的值(而不是堆栈中保存的ESP的值)。也即执行该指令后ESP的值,可以通过增加32来恢复。

(6)PUSHFD

将32位标志寄存器EFLAGS的内容压入堆栈。

(7)POPFD

将当前栈顶的4字节内容弹至EFLAGS寄存器。

上述堆栈操作指令中,除POPFD以外,其余均不影响状态标志位。

(8)设置堆栈空间指令

格式:ENTER 16位立即数,8位立即数。

ENTER指令使用两个操作数,16位立即数表示堆栈空间的大小,也即表示给当前过程分配多少字节的堆栈空间,8位立即数指出在高级语言内(如Pascal语言)调用自身的次数,也即嵌套层数。

说明:该指令使用BP寄存器而非SP作为栈基值。

【例3.31】ENTER 6,0

该指令为过程分配了6个字节的堆栈空间,其嵌套层数为0。指令执行情况如图3.21所示。

图3.21 ENTER指令执行情况

(9)撤销堆栈空间指令

格式:LEAVE

该指令无操作数,撤销由ENTER指令建立的堆栈空间。

【例3.32】有一个16位参数,在主程序中需交给一个子程序处理,结果再传回主程序,可使用堆栈空间指令完成上述工作。

          调用子程序的过程,参数存入到堆栈中
          ENTER  4,0                   ; 建立4字节堆栈空间
          MOV   AX,Number1             ; 保存参数1
          MOV  [BP-4],AX
          MOV   AX,Number2             ; 保存参数2
          MOV  [BP-2],AX
          CALL  COURSE                 ; 调用子程序
          MOV  AX,[BP-4]               ; 取结果1
          MOV   Number1,AX
          MOV  AX,[BP-2]               ; 取结果2
          MOV   Number2,AX
          LEAVE                        ; 撤销堆栈空间
              ┇
          ; 使用堆栈处理数据的子程序
          COURSE   PROC  NEAR
          PUSHA
          MOV  AX,[BP-4]              ; 取参数1
          MOV  DX,[BP-2]              ; 取参数2
              ┇
          ; 参数处理
              ┇
          MOV  [BP-4],AX              ; 保存结果1
          MOV  [BP-2],DX              ; 保存结果2
          POPA
          RET
          COURSE   END

3. 地址传送指令

(1)LFS寄存器,存储器

将源操作数所指存储单元的4字节或6字节内容送指定的寄存器及段寄存器FS(FS及GS为80386新增的段寄存器)。

当目的操作数为16位寄存器时,将4字节的存储器操作数中的低两字节送指定寄存器,高两字节送段寄存器FS;当目的操作数为32位寄存器时,将6字节的存储器操作数中的低4字节送指定寄存器,高两字节送段寄存器FS。该指令不影响状态标志位。

【例3.33】LFS BX, ARRAY

指令执行情况如图3.22所示。

图3.22 指令执行情况

(2)LGS寄存器,存储器

该指令与LFS指令功能基本相同,唯一区别在于,该指令所涉及的段寄存器为GS。

(3)LSS寄存器,存储器

该指令与LFS指令功能基本相同,唯一区别在于,该指令所涉及的段寄存器为SS。

4. 运算指令

在8086/8088指令系统中,乘法指令只给出一个操作数,另一个操作数隐含在AL或AX中,80386将其扩充为可以有两个或3个操作数。

(1)IMUL寄存器,寄存器/存储器/立即数

将通用寄存器中的有符号数作为被乘数,相同位数通用寄存器、存储单元中的有符号数或立即数(如立即数与目的操作数不等长,运算时机器会自动将其符号扩展成与目的操作数等长)作为乘数,乘积送目的操作数。若乘积溢出,溢出位部分将丢失,且将OF及CF置1;否则将OF及CF置0。

说明:目的操作数的位数必须与源操作数位数相同。

例如: IMUL DX, 9

(2)IMUL寄存器,寄存器/存储器,立即数

与前一指令功能基本相同,唯一区别在于,寄存器/存储器为被乘数,立即数为乘数,乘积存放在第一个操作数中。例如:

          IMUL  EAX,DWORD DTR[BX],9  ; 将BX所指定的4字节存储器操作数乘以9, 乘积送32位
                                        通用寄存器EAX。

(3)CWDE

将AX中16位有符号数的符号位扩展到EAX的高16位中,即把AX的16位有符号数扩展为32位后,送EAX。

(4)CDQ

将EAX中32位有符号数扩展到EDX:EAX寄存器对中,使之成为64位有符号数,即将EAX中的符号位扩展到EDX中。

【例3.34】若DATA1中为16位有符号数,值为-5,DATA2中为32位有符号数,值为-7。将DATA1扩展成32位有符号数,将DATA2扩展成64位有符号数。

          MOV  AX,DATA1       ;AX中为-5(FFFBH)
          CWDE                 ; 扩展后EAX中为32位的-5(FFFF FFFBH)
          MOV  EAX,DATA2      ;EAX中为-7(FFFF FFF9H)
          CDQ                  ; 扩展后EDX:EAX中为64位的-7(FFFF FFFF FFFF FFF9H)

5. 移位指令

(1)移位指令助记符 寄存器/存储器 立即数(≤31)

8086中有8条移位指令,移位计数使用CL或1表示,且规定当移位次数大于1时,必须使用CL。从80286开始,则修改了上述的限制,当移位次数为1~31时,允许使用立即数。例如:

          ROL  AX,5
          SHL  WORD  PTR[BX],18

(2)SHLD寄存器/存储器,寄存器,CL/立即数

将第一操作数(16位或32位通用寄存器或存储单元)左移若干位(左移位数由8位立即数或CL指定),空出位用第二操作数(与第一操作数长度相同的通用寄存器)高位部分填补,但第二操作数的内容不变,CF标志位中保留第一操作数最后的移出位。若仅移一位,当CF值与移位后的第一操作数的符号位不一致时,OF置1;否则OF置0。移位过程如图3.23所示。

图3.23 SHLD指令功能

      【例3.35】MOV AX,8321H
                MOV  DX,5678H
                SHLD  AX,DX,1      ;(AX)=0642H,DX=5678H,CF=1,OF=1;
                SHLD  AX,DX,2      ;(AX)=1909H,DX=5678H,CF=0,OF=0。

(3)SHRD寄存器/存储器,寄存器,CL/立即数

将第一操作数(16位或32位通用寄存器或存储器单元)右移若干位(右移位数由CL或8位立即数指定),空出位用第二操作数(与第一操作数长度相同的通用寄存器)低位部分填补,指令执行后,第二操作数内容不变,CF标志位中保留第一操作数最后的移出位。移位过程如图3.24所示。

图3.24 SHRD指令功能

      【例3.36】MOV AX,4B02H       ;AX=0100101100000010
                MOV  BX,6040H      ;BX=0110000001000000
                SHRD  AX,BX,7      ;AX右移7位,BX的低7位移入AX中, 结果为AX=10000000
                                    10010110(8096H), BX=6040H(保持不变), CF=0。

指令执行情况如图3.25所示。

图3.25 指令执行情况

6. 位操作指令

(1)位测试及设置指令

测试指令可用来对指定位进行测试,因而可根据该位的值来控制程序流的执行方向,而置位指令可对指定的位进行设置。

① BT 存器/存储器地址,寄存器/立即数

第一操作数(16位或32位通用寄存器或存储单元)指定要测试的内容,第二操作数(与第一操作数同长度的通用寄存器或8位立即数)指定要测试的位,将被测内容的指定测试位的值送CF,其他状态标志不确定。

【例3.37】设BX指向一个存储单元数,CX值为4。

          BT  [BX],CX       ; 检查由BX指向数据的第4位, 并将该位值送入CF;
          JC  Swhere        ; 若CF=1, 则转移。

若用8086指令完成,可写成:

          MOV  AX,[BX]     ; 将BX指向的数装入AX中;
          TEST  AX,10H     ; 检查第4位是否为1;
          JNZ  Swhere      ; 若CF=1, 则转移。

② BTC 存器/存储器地址,寄存器/立即数

该指令在BT指令功能的基础上,将被测位取反。

③ BTR 存器/存储器,寄存器/立即数

该指令在BT指令功能的基础上,将被测位清0。

④ BTS 存器/存储器,寄存器/立即数

该指令在BT指令功能基础上,将被测位置1。

【例3.38】MOV AX, 1234H

(2)位扫描指令

                BT  AX,2           ;(AX)=1234H,CF=1
                BTC  AX,2          ;(AX)=1230H,CF=1
                BTR  AX,2          ;(AX)=1230H,CF=0
                BTS  AX,2          ;(AX)=1234H,CF=0

位扫描指令用于找出寄存器或存储器地址中所存数据的第一个或最后一个是1的位。该指令可用于检查寄存器或存储器或存储单元是否为0。

① BSF 寄存器,寄存器/存储器

对第二操作数(16位或32位通用寄存器或存储器)从最低位到最高位进行扫描,将首先扫描到的“1”的位号送第一操作数(与第二操作数位数相同的通用寄存器),且使ZF置0。若第二操作数的各位均为0,则第一操作数的值不确定,且使ZF置1。其他状态标志位不确定。

② BSR 寄存器,寄存器/存储器

与BSF指令功能基本相同,唯一区别在于,该指令是从最高位到最低位进行扫描。

        例如:  MOV  EAX,01234567H
                BSR   ECX,EAX           ;(ECX)=18H,ZF=0
                BSF   AX,CX             ;(AX)=03H,ZF=0

7. 条件设置指令

这组80386特有的指令用于测试指定的标志位所处的状态,并根据测试结果,将指定的一个8位寄存器或内存单元置1或置0。它们类似于条件转移指令中的标志位测试,但前者根据测试结果将操作数置1或置0,而后者根据测试结果决定转移还是不转移。

指令格式为

          SET条件  寄存器/存储器

说明:条件是指令助记符的一部分,用于指定要测试的标志位。例如

          SETZ   AL                    ; 当ZF=1, 则(AL)=1, 否则(AL)=0
          SETNC  BYTE  PTR[BX]         ; 当CF=0, 则BX所指字节单元内容为1

8. 内存范围检查指令

格式:BOUND 16位寄存器,32位存储器

以32位存储器低两字节的内容为下界,高两字节的内容为上界。若16位寄存器的内容在此上、下界表示的地址范围内,程序正常执行;否则产生INT 5中断(DOS并未提供该类型中断处理程序,使用时用户需自行编写)。当出现这种中断时,返回地址指向BOUND指令,而不是BOUND后面的指令,这与返回地址指向程序中下一条指令的正常中断是有区别的。

【例3.39】BOUND指令的应用。

          DATA  SEGMENT
          BOTTOM     EQU  0
          TOP         EQU  19
          ANS         LABEL   DWORD          ; 给ANS分配32位地址;
          WANS        DW   BOTTOM,TOP        ; 对边界初始化;
          BOFF        DB   TOP+1  DUP(?)  ; 分配数组;
          DATA    ENDS
          CODE    SEGMENT
                          ┇          ; 假设SI为数组的地址指针;
                  BOUND  SI,ANS       ; 检查SI是否在数据地址范围内, 否则进入类型5中断;
                  MOV   DX,BOFF[SI]   ; 若在范围内, 继续使用。
                          ┇
          CODE    ENDS

3.5.2 Pentium新增指令

1. 字节交换指令

格式:BSWAP寄存器

将32位通用寄存器以字节为单位进行高低字节的交换,即对指定寄存器的32位操作数的位31~24与位7~0,位23~16与位15~8交换。该指令不影响状态标志位。

      【例3.40】寄存器(EDX)=12345678H
                BSWAP   EDX
      则指令执行后,(EDX)=78563412H。

80X86系列处理器按“高高低低”的原则存储多字节数据,但某些处理器按“低低高高”的原则存储数据,BSWAP指令特别适宜于这两种数据格式之间的转换。

2. 互换并相加指令

格式:XADD寄存器/存储器,寄存器

该指令将第一操作数(8位,16位或32位通用寄存器或存储单元)与第二操作数(与第一操作数位数相同的通用寄存器)内容互换,并将两者之和送第一操作数。该指令对状态标志位的影响与ADD指令相同。

      【例3.41】若BX所指单元内容为11223344H,(EAX)=00224466H,
                XADD  [BX],EAX

指令执行后,(EAX)=11223344H,而BX所指单元内容为1144 77AAH。XADD指令功能相当于XCHG和ADD这两条指令的功能。该指令允许使用LOCK前缀。

3.比较并交换指令

(1)格式:CMPXCHG寄存器/存储器,寄存器

该指令将第一操作数(8位,16位或32位通用寄存器或存储单元)内容与对应长度的累加器(AL,AX或EAX)内容作比较,若相等,则使ZF置1,且将第二操作数(与第一操作数位数相同的通用寄存器)内容送第一操作数;否则使ZF清0,且第一操作数送对应累加器。

【例3.42】CMPXCHG ESI,EBX

若(ESI)=(EAX),则ZF=1,且将(EBX)送ESI;否则ZF=0,且将(ESI)送EAX。

(2)8字节比较交换指令

格式:CMPXCHG8B存储器

将EDX:EAX中的8字节值与指定的8字节存储器操作数相比较,若相等,则使ZF置1,且将ECX:EBX中的值送指定的8字节存储单元替换原有存储器操作数;否则使ZF=0,且将指定的8字节存储操作数器送EDX:EAX。

【例3.43】设BX所指8字节存储单元内容为0011223344556677H,(EDX)=0,(EAX)=FFFF FFFFH。

                  CMPXCHG8B  [BX]

则该指令执行后,(EDX)=00112233H,(EAX)=44556677H,ZF=0。

4.Cache管理指令

(1)使整个片内Cache无效指令

格式:INVD

该指令用于将CPU内部Cache的内容无效。其具体的操作是刷新内部Cache,并分配一个专用总线周期刷新外部Cache,执行该指令不会将外部Cache中的数据写回主存,即Cache中数据自然丢失。

(2)写回并使Cache无效指令

格式:WBINVD

该指令功能与INVD相似,具体操作是刷新内部Cache。并分配一个专用总线周期将外部Cache的数据写回主存,并在此后的一个专用总线周期将外部Cache刷新。

(3)使TLB无效指令

格式:INVLPG

该指令使页式管理机构内的高速缓冲器TLB中的某一项作废。若TBL中含有一个存储器操作数映象的有效项,则该TBL项被标记为无效。

5.处理器特征识别指令

格式: CPUID

根据EAX中的参数,将处理器的说明信息送EAX,特征标志字送EDX。

6.读时间标记计数器指令

格式: RDTSC

将Pentium中的64位时间标记计数器的高32位送EDX,低32位送EAX。该计数器随每一个时钟递增,在Reset后该计数器被置0。利用该计数器可检测程序运行性能。

7.读模型专用寄存器指令

格式: RDMSR

将ECX所指定的模型专用寄存器的内容送EDX、EAX,具体来说,高32位送EDX,低32位送EAX。若所指定的模型寄存器不是64位,则EDX、EAX中的对应位无定义。

8.写模型专用寄存器指令

格式: WRMSP

将EDX、EAX的内容送到由ECX指定的模型专用寄存器。具体来说,EDX和EAX的内容分别作为高32位和低32位。若指定的模型寄存器有未定义或保留的位,则这些位的内容不变。

本小节只是简单介绍了Pentium新增指令,对这几条指令的理解和应用要求具备Pentium体系结构方面的知识,请参阅有关文献资料。

习题

3.1 分别指出下列指令中源操作数和目的操作数的寻址方式。

        1)MOV  AX,0100H             2)MOV  [SI],AL
        3)ADC  BL,[2000H]           4)AND  BYTE  PTR[2000H],1
        5)MOV  2[BX][DI],DX         6)OR  AX,80H[SI]

3.2 指出以下指令是否合法。

        1)MOV  CS,AX                2)MOV  [2000H],AL
        3)ADD  [2000H],40H          4)MUL  AX,BX
        5)AND  184CH,AX             6)MOV  DS,0
        7)INC  [BX]                  8)SBB  AX,[DX]
        9)RCL  BX,2                10)POP  AL
        11)XCHG  BX,4050H          12)CWB

3.3 试根据以下要求写出相应的汇编语言指令。

1)将CX寄存器的内容与DX寄存器的内容相加,结果存入DX寄存器中。

2)用寄存器BX和DI的基址变址寻址方式把存储器中的一个字节数据与AH寄存器的内容相加,并把结果存入AH寄存器中。

3)用寄存器BX和位移量2000H的寄存器相对寻址方式把存储器的一个字数据和(DX)相加,并把结果送回存储器中。

4)将数0C3H与(BL)寄存器相加,结果送回BL寄存器中。

5)将存储器数据段中1300H单元(采用直接寻址方式)中的一个字数据与立即数3456H相减,结果送回存储器1300H单元。

3.4 假设AX中的数据为6987H,DATE1分别为下列数值时,执行ADD AX,DATE1指令后,标志位SF、ZF、CF和OF的状态是什么?

      1)1234H              2)4801H
      3)EB30H              4)902AH

3.5 假设AX中的数据为3760H,DATE1分别为下列数值时,执行CMP AX,DATE1指令后,标志位SF、ZF、CF和OF的状态是什么?

      1)1234H              2)4801H
      3)EB30H              4)902AH

3.6 设(SP)=2040H,(AX)=12D4H,(BX)=36F4H。试回答:

1)执行PUSH AX指令后,(SP)=?

2)再执行PUSH BX及POP AX指令后,(SP)=?,(AX)=?

3.7 写出执行以下计算的指令序列,其中X,Y,Z,R和Q均为存放16位带符号数单元的地址。

        1)Q←X+(Z-Y)
        2)Q←X+(Y+9)-(Z+3)
        3)Q←(X*Y)+(Z/R)
        4)Q←5*(X-Y)+R/(Z+8)

3.8 现有(DS)=4000H,(BX)=0100H,(SI)=0002H,(40100H)=11H,(40101H)=22H,(40102H)=33H,(40103H)=44H,(41200H)=55H,(41201H)=66H,(41202H)=77H,(41203H)=88H,试说明下列各条指令执行完成后AX的内容。

        1)MOV   AX,0100H
        2)MOV   AX,[BX]
        3)MOV   AX,1200H
        4)MOV   AX,BX
        5)MOV   AX,[BX][SI]
        6)MOV   AX,1100H[BX]
        7)MOV   AX,1100H[BX][SI]
        8)MOV   AX,2[BX]

3.9 已知:(DS)=2000H,(ES) =3000H,(CS)=5000H,(SS)=4000H (AX)=5566H,(BX)=1000H,(BP)=0010H,(21000H)=1122H,(31000H)=7766H,(40010H)= 0FFFFH。

请写出下列各条指令独立执行完后,有关寄存器及存储单元的内容。

        1)ADD   AL,[BX]
        2)AND   AX,BX
        3)SUB   ES:[BX],AX
        4)XOR   [BP],AX

3.10 在数据段DATA中已为0120H单元定义的符号名为MESS,其中存放的数据为1122H。

1)试问以下两条指令有什么区别,指令执行结束后AX寄存器的内容是什么。

          MOV   AX,MESS
          LEA   AX,MESS

2)试问以下两条指令有什么区别,指令执行结束后AX寄存器的内容是什么。

          MOV   AX,MESS
          MOV   AX,OFFSET  MESS

3.11 写出各种使AL置0的指令。

3.12 用两种方法实现将(AL)乘以10值送AX的功能。

3.13 写一程序段,将附加段2000H~2003H四个字节之和送AX。

3.14 假设(DX)=0F7H,变量DATA1中内容为9EH,确定下列每条指令执行后的结果。

        1)AND  DX,DATA1
        2)XOR  DX,DATA1
        3)OR  DX,DATA1
        4)SHL  DX,1
        5)XOR  DX,0FFH
        6)AND  DX,0H
        7)TEST  DX,80H
        8)TEST  DX,01H

3.15 试写出移位指令执行后BX寄存器的内容,执行前(BX)=6CB5H,CF=0。

        1)MOV  CL,04H
          SHR  BX,CL
        2)MOV  CL,03H
          SAL  BX,CL
        3)ROR  BX,1
        4)MOV  CL,06H
          RCR  BX,CL

3.16 若(AX)=0012H,(BX)=0034H,则下列指令执行后AX为多少?

          MOV  CL,8
          ROL  AX,CL
          ADD  AX,BX

3.17 试分析下面的程序段所完成的功能。

          MOV     CL,4
          SHL      DX,CL
          MOV     BL,AH
          SHL      AX,CL
          SHR      BL,CL
          OR       DL,BL

3.18 下列程序段执行后(AL)=?(DL)=?完成的是什么功能?

          MOV  CL,4
          MOV  AL,48H
          MOV  DL,AL
          AND  AL,0FH
          OR  AL,30H
          SHR  DL,CL
          OR  DL,30H

3.19 编写程序使:

1)BX寄存器低4位置1

2)AX寄存器的低4位清0

3)AX寄存器各位取反

4)CX寄存器的低4位取反

5)用TEST指令测试AL寄存器的位1、位5和位7是否同时为1,如果是则把0FFH送CL寄存器,否则将0送CL寄存器。

6)将AH的低4位与AL的低4位拼成一个字节(AH的低4位为拼装后的高4位),结果送AH中。

3.20 说明80386的32位通用寄存器与16位通用寄存器这间的关系。

3.21 80386的寻址方式有何特点。

3.22 设AX中有一有符号数,请用两条不同的指令将AX中的值扩展到EAX中。

3.23 请用两条不同的指令使BX所指的32位存储器操作数的第20取反。

3.24 用一条指令实现把EAX中的32位数保存到寄存器对DX:AX中。

3.25 设寄存器对EDX:EAX中放有0102030405060708H,请写出指令使其中的内容成为0807060504030201H。

3.26 试给出下列指令序列执行后目的寄存器的内容。

        1)MOV   BX,-10H
          MOVSX   EBX,BX
        2)MOV   DL,-37H
          MOVSX   ECX,DL
        3)MOV   AX,37H
          MOVZX   ECX,AX
        4)MOV   CL,0A3H
          MOVZX   EAX,CL

3.27 编写程序段,将EBX、ECX和ESI寄存器的内容相加,其和存入EDI寄存器中(不考虑溢出)。