8086中数据传送类指令详解

目录

通用数据传送类指令:不影响FLAGS寄存器中的标志位

数据传送指令:MOV

压栈(PUSH)出栈(POP)指令

交换指令XCHG

查表指令:XLAT

针对有符号数的字长扩展指令

地址传从指令:不影响FLAGS寄存器中的标志位

取存储单元偏移地址指针指令:LEA(Load Effective Address)

源地址指针指令:LDS(Load pointer using DS)和LES(Load pointer using ES)

标志位存储指令:不影响FLAGS寄存器中的标志位

将标志位存入AH/从AH中取出

将标志位存入堆栈/从堆栈取出

输入输出指令:不影响FLAGS寄存器中的标志位

端口与引脚的区别

如何判断端口的位数?

算术运算类指令:影响FLAGS中的标志位

加法指令

减法指令

乘法运算

除法运算

逻辑运算指令

移位操作指令


通用数据传送类指令:不影响FLAGS寄存器中的标志位

数据传送指令:MOV

指令格式:MOV des src

含义:将src的内容存入des中

注意:

1. 两个操作数字长必须相等,并且至少知道一个操作数的字长;

2. 由于代码段、数据段、附加段的存储单元(不含堆栈段)之间不可以直接进行相互操作,因此双操作数指令的操作数不可以同时为存储器操作数。因为存储单元可以直接与寄存器/堆栈段内存单元之间进行数据转移,因此存储单元的内容相互交换必须以寄存器/堆栈段内存单元为媒介,以下是两种交换内存单元内容的方式:

3. 无论在那个操作指令当中,因为段寄存器不可以被赋值,因此段寄存器不可以作为目标操作数。但是FLAGS可以被赋值,就相当于刷新FLAGS寄存器中的内容。

4. 指令指针寄存器IP虽然可以被当作目标寄存器来使用,但是不建议这样做,因为IP寄存器中存放的是执行代码的地址,如果改变IP寄存器中的值,代码运行顺序将出现紊乱。

5. MOV指令不可以用于堆栈段内存单元中数据的存取,数据出入堆栈段只能通过出栈POP和压栈PUSH指令实现。

注意:只有基址变址寻址,没有基址基址寻址(mov AX [BX][BP])或者编制变址寻址(mov AX [SI][DI])。

压栈(PUSH)出栈(POP)指令

要想将寄存器/存储器(除堆栈外)的数据送入堆栈,那么必须且只能使用PUSH和POP指令。PUSH和POP均为单操作数指令,格式如下所示:

PUSH 寄存器/存储器单元

POP 寄存器/存储器单元

堆栈遵循先入后出(FILO:first-in last-out)的读写规律,与堆栈段有关的寄存器有三个:栈顶指针寄存器SP、栈顶指针寄存器/堆栈段段基寄存器SS、基址指针寄存器BP。其中SS寄存器和SP寄存器决定了堆栈有两个状态(满栈和空栈),BP寄存器常用于存放堆栈段内存单元相对于堆栈段段首的偏移地址,PUSH/POP时用不到:

但是与PUSH/POP指令相关的只有栈顶指针寄存器SP,压栈/出栈会影响栈顶指针寄存器SP在堆栈段中指向的位置:

操作指令注意事项:

1. 内存单元的绝对地址必须可以被16整除(因为8086位16位的CPU)并且8086CPU只能处理8位/16位字长的数据,这就要求存储在存储器中的数据长度必须是2byte(word)

2. PUSH/POP这样的单操作数格式的指令,操作数只能是内存单元或者寄存器;

3. 我们看到PUSH指令会使得SS:SP指针向栈顶位置偏移,即PUSH指令操作的方向是从高地址到低地址(自下而上);而POP指令会使得SS:SP指针向栈底位置偏移,即POP指令操作的方向是从低地址到高地址(自上而下);

4. POP指令的操作数不可以是段寄存器,因为上面也提到过“段寄存器不可以被赋值“,段寄存器中的段基地址由操作系统进行决定,操作系统采用”见缝插针“的形式分配内存。

5. PUSH和POP一般成对出现,否则会造成内存泄漏。

6. 像PUSH和POP这样的单操作数指令,其后的操作数如果为存储器操作数,那么存储器操作数必须被声明字长,又由于堆栈内的数据均为16位字长,故:PUSH WORD PTR([1024H])。

堆栈段和其他逻辑段中数据存放的形式相同

交换指令XCHG

指令格式:XCHG 操作数1 操作数2

这个指令因为是用于交换不同位置的数据的,因此每个操作数既是目标操作数又是源操作数。操作指令注意事项:

1. XCHG不可以交换两个存储器存储单元中的数据,因为存储器存储单元(除堆栈段外)无直接联系的通道,相互赋值都不行更何况是交换数据。

2. 操作数不可以是段寄存器,对于FLAGS、IP这样的专用寄存器,我们尽量不要对其进行读写操作。

查表指令:XLAT

指令格式:XLAT

这个指令的操作数采用了隐含寻址的寻址方式。其目标操作数是BX(代表表格首地址),源操作数是AL(代表表格元素相较于表头的偏移地址),该指令的作用就是“将存储器中地址为BX+AL的存储单元的8位数据内容拷贝至AL寄存器中“。

XLAT 是取“某指定内存区”的内容,将其存放与 AL中。而该“指定内存区”由 BX和 AL来确定,具体规则是:

BX寄存器中存放的是某内存的地址,指向某块内存区(通常该块内存存放的是一个数组),AL来提供从该BX指向的内存开始的偏移量。假设 BX指向的内存区中依次存放了 0x11, 0x22, 0x33, 0x44, AL为2; 在执行王 XLAT后, 取0x33内存内容存放到 AL中。

针对有符号数的字长扩展指令

1. 将有符号数从8位(Byte)扩展到16位(Word):CBW

符号位

高8位

1

FFH

0

00H

2. 将有符号数从16位(Word)扩展至32位(Double Word):CWD

符号位

高16位

1

FFFFH

0

0000H

地址传从指令:不影响FLAGS寄存器中的标志位

取存储单元偏移地址指针指令:LEA(Load Effective Address)

指令格式:LEA 间址寄存器 存储器操作数

该操作指令目的在于“将存储器存储单元的偏移地址而非内容拷贝至间址寄存器中“,操作指令注意事项:

1. 目标操作数必须是间址寄存器(基址寄存器BX、基址指针寄存器BP、目标变址寄存器DI、源变址寄存器SI),源操作数必须是存储器操作数(可以用寄存器寻址和直接寻址表示);

2. 要和MOV指令区别开来

假设数据段[1024H]存放数据为5462H:

指令

BX中的值

含义

MOV BX DS:[1024H]

5462H

存储单元的数据

LEA BX DS:[1024H]

1024H

存储单元的偏移地址

LEA的使用案例:

源地址指针指令:LDS(Load pointer using DS)和LES(Load pointer using ES)

指令格式:“LDS 通用寄存器 存储器操作数“和”LES 通用寄存器 存储器操作数“

标志位存储指令:不影响FLAGS寄存器中的标志位

我们在debug中只能看到8个标志位(用于单步调试的TF陷阱标志位是看不到的),我们能看到的8个标志位分别为:

标志

1

0

备注

溢出标志位OF

有符号数溢出

有符号位未溢出

16位/8位

方向标志位DF

按减地址方向进行串操作

按增地址方向进行串操作

串操作

中断标志位IF

中断允许

中断不允许

零标志位ZF

计算结果为0

计算结果非0

16位/8位

符号标志位SF

符号位为1(正数)

符号位为1(负数)

16位/8位

进位标志位CF

最高位进位/借位

最高位未进位/借位

16位/8位

辅助标志位AF

Bit3向bit4进位/借位

Bit3向bit4未进位/借位

8位

奇校验位PF

数据中有奇数个1

数据中有偶数个1

16位/8位

将标志位存入AH/从AH中取出

将标志位存入堆栈/从堆栈取出

在执行函数嵌套时会将一些数据存入堆栈中以便跳出函数/循环后再使用。

输入输出指令:不影响FLAGS寄存器中的标志位

对于输入输出指令,我们还需回顾一下有关输入输出指令的寄存器:

累加器AX

输入/输出数据的存放处

数据寄存器DX

IO端口的偏移地址

我这里要分别说明一下8088和8086的输入输出指令的不同之处——输入输出数据位数:

对于8088而言,对外输入输出输入的数据字长为8位,因此用于输出输入数据存放的寄存器只需8位即可。指令用于从I/O端口读数据到累加器AL(AX的低8位寄存器)中,而输出指令OUT用于把累加器AL的内容写到I/O端口中,即从CPU方面看,只有累加器AL才能与I/O端口进行数据传送,所以这两条指令也被称为累加器专用传送指令。还记得AD0-AD7低8位地址传输总线吗?这8位地址总线和对外数据交互总线分时复用。

对于8086而言,对外输入输出输入的数据字长为16位,因此用于输出输入数据存放的寄存器需要为16位的。指令用于从I/O端口读数据到累加器AX中,而输出指令OUT用于把累加器AX的内容写到I/O端口中,即从CPU方面看,只有累加器AX才能与I/O端口进行数据传送,所以这两条指令也被称为累加器专用传送指令。16位地址总线和数据总线分时复用。

输入输出指令的寻址方式:

1. 直接寻址:

IN AL PORT

将“地址为PORT“的端口的byte长度的数据存入AL寄存器中

OUT PORT AL

将AL中的byte长度的数据输出至“地址为PORT“的端口

注意:由于双操作数格式的指令要求“如果操作数中有立即数,那么立即数的字长由另一个操作数的字长确定“,因此这里的立即数PORT也必须是8位的。但是这里请切记“直接寻址中PORT最多为8位字长”,因此直接寻址只能用AL不可以用AX

对于8086 CPU来讲,8086 CPU对外数据总线为16位宽,因此8086的直接寻址如下所示:

IN AX PORT

将“地址为PORT“的端口的double byte长度的数据存入AX寄存器中

OUT PORT AX

将AX中的double byte长度的数据输出至“地址为PORT“的端口

2. 间接寻址(寄存器寻址):

其实不管是存储器寻址也好I/O端口寻址也罢,都涉及了两种寻址方式:间接寻址(寄存器寻址)和直接寻址(立即数寻址)。但是I/O端口的间接寻址(寄存器寻址)并没有存储器间接寻址(寄存器间接寻址[BX]、寄存器相对寻址[BX+DATA]、寄存器基址变址寻址[BX][SI]、寄存器基址变址相对寻址[BX+SI+DATA])那么多样,I/O端口仅有寄存器间接寻址,并且这里使用的间址寄存器不再是BX、BP、SI、DI了(准确的来讲BX/BP/SI/DI是存储器间址寄存器),I/O端口的间址寄存器只有DX(准确的来讲DX是I/O端口间址寄存器)

IN AL DX

DX中存放着I/O端口的偏移地址,此操作是将指定地址端口的一个byte数据存入AL寄存器中

OUT DX AL

DX中存放着I/O端口的偏移地址,此操作是将AL中的一个byte数据输出至指定地址的端口

注意:这里的DX和AL字长并不相等,这是因为AL用于存放端口寄存器的8位数据,而DX中的数据表示端口的地址,系统并未对两者进行运算,故OPEN1和OPEN2可以不同。

IN AX DX

DX中存放着I/O端口的偏移地址,此操作是将指定地址端口的双byte数据存入AX寄存器中

OUT DX AX

DX中存放着I/O端口的偏移地址,此操作是将AX中的双byte数据输出至指定地址的端口

端口与引脚的区别

端口是CPU与外部引脚沟通的媒介,即端口就是寄存器组,为何被称之为寄存器组呢?因为端口中需要有状态寄存器控制引脚的输入输出状态、数据寄存器存储引脚电平的高低以及复用寄存器支持引脚第二功能。

就如同我们常常提及的PA端口中,有PA.0、PA.1……PA.7这8个引脚。既然端口地址总线是16位的,那我们到底可以寻址多少个端口?又可以寻址到多少个引脚呢?

每个端口有地址,每个引脚也有地址,16位宽的地址总线可以寻址到64K个地址单元,端口地址和引脚地址的关系如下图所示:

我们可以看到端口地址是个范围并且这个范围取决于端口的位数,而引脚地址是个位地址并且是个准确的地址,因此8086/8088 CPU可以管理64K个引脚,以及64K/8个端口。由于8088 CPU数据总线位宽为8位,因此端口基地址均可以被8整除。

如何判断端口的位数?

我们一般常说8位/16位端口,这里的8位/16位到底说的是端口地址呢?还是端口中数据寄存器的位数呢?其实,这里的8位/16位端口指的是“端口中数据寄存器是8位/16位的“,即CPU可以通过数据总线一下子读取到8/16个引脚的高低电平状态。

算术运算类指令:影响FLAGS中的标志位

我们再回顾一下9个标志位(在Debug中可以看见的只有8个,不包括TF标志位):

其中最需要注意的是进位标志位CF:

当进行加法运算时,若最高位向前有进位,则CF=1,同理,当进行减法运算时,若最高位向前有借位,则CF=1。也可以这样说:

加法指令

1. 不带进位的加法指令:ADD OPEN1 OPEN2

指令的含义是:执行“目标操作数+源操作数“,并将结果放入目标操作数中。

格式

目标操作数类型

源操作数类型

ADD [BX] 1024H

存储器操作数

立即数操作数

ADD [BX] [AX]

存储器操作数

存储器操作数

ADD AX BX

寄存器操作数

寄存器操作数

ADD AX [BX]

寄存器操作数

存储器操作数

ADD [BX] AX

存储器操作数

寄存器操作数

注意:目标操作数不可以是立即数!

2. 带进位的加法指令:ADC OPEN1 OPEN2

指令的含义是:执行“目标操作数+源操作数+CF标志位“,并将结果放入目标操作数中。

格式

目标操作数类型

源操作数类型

ADC [BX] 1024H

存储器操作数

立即数操作数

ADC [BX] [AX]

存储器操作数

存储器操作数

ADC AX BX

寄存器操作数

寄存器操作数

ADC AX [BX]

寄存器操作数

存储器操作数

ADC [BX] AX

存储器操作数

寄存器操作数

我们发现,ADC和ADD指令应用场景及要求相同。那ADC和ADD指令又有何不同呢?

mov ax,2  ; CF=0
mov bx,1  ; CF=0
sub bx,ax ; BX=0001B-0010B=1111B且CF=1
adc ax,1 ; AX=10B+1+1=5

看完上述代码我们会明白ADC与ADD的不同,但是你知道ADC的用处吗?

ADC指令用于带进位的高位运算,代码格式如下所示:

add al,bl  
adc ah,bh 

我们举个例子:001EF000H+00201000H

mov ax, 001EH  
mov bx,0F000H  
add bx,1000H  
adc ax,0020H  

注意:无论是ADC还是ADD指令,都是双操作数指令,并且这两个操作数的字长必须相等,ADC主要用于多字节运算(多于16/8位字长的操作数运算),对于多字节运算,只能先加低16/8位,再加高16/8位并且考虑地位向上的进位。

我们再举个例子,看看ADD和ADC运算是如何改变FLAGS寄存器中各个标志位的,在系统运算18H+1FH=00011000H+00011111H=00110111H

OF

DF

IF

SF

ZF

AF

PF

CF

0

0

0

0

0

1

1

0

AF=1:低8位中第3位向低4位进位/借位(最低位为第0位);

OF=0:最高位进位/借位与次高位进位/借位的异或为0(判断有符号位是否溢出)

CF=0:最高位进位/借位(判断无符号位是否溢出)

SF=0:最高位/符号位为0

ZF=0:结果非0

PF=1:8位结果中1的个数为奇数个

3. 递加指令:INC OPEN

单操作数指令要求操作数不可以是立即数操作数/段寄存器,只能是存储器操作数或者寄存器操作数。并且INC与ADD、ADC同属加法运算,但INC唯独不会影响CF标志位。

INC WORD PTR([BX])

操作数为存储器操作数时,必须指定字长

INC BX

对BX寄存器中的数据进行+1操作

注意:当单操作数指令的操作数为存储器操作数时,必须指定操作数的字长!

减法指令

1. 不带进位的减法运算:SUB OPEN1 OPEN2

该指令的含义:执行“OPEN1-OPEN2“并将结果存入目标操作数中

假设:01H-02H

  1. mov ax,01H  
  2. mov bx,02H  
  3. sbb ax,bx ; ax=-1=11H,CF=1  

SUB不考虑借位,并且对标志位的影响与ADD/ADC一致。

2. 带进位的减法运算:SBB OPEN1 OPEN2

该指令的含义:执行“OPEN1-OPEN2-CF“并将结果存入目标操作数中

假设:01H-02H

  1. mov ax,01H  
  2. mov bx,02H  
  3. mov cx,03H  
  4. sub ax,bx ; ax=-1=FFH,CF=1  
  5. sbb cx,ax ; cx=03H-FFH-1,CF=1 

SBB考虑借位,并且对标志位的影响与ADD/ADC一致。

3. 递减指令:DEC OPEN

单操作数指令要求操作数不可以是立即数操作数/段寄存器,只能是存储器操作数或者寄存器操作数。DEC与SUB、SBB同属减法运算,但是DEC不影响CF标志位。

DEC WORD PTR([BX])

操作数为存储器操作数时,必须指定字长

DEC BX

对BX寄存器中的数据进行-1操作

注意:当单操作数指令的操作数为存储器操作数时,必须指定操作数的字长!

4. 求补指令:NEG OPEN

该指令的含义:执行“0-OPEN“操作,并将结果送回该操作数所在地址。

单操作数指令要求操作数不可以是立即数操作数/段寄存器,只能是存储器操作数或者寄存器操作数。

NEG WORD PTR([BX])

操作数为存储器操作数时,必须指定字长

NEG BX

对BX寄存器中的数据进行取补操作

例如:设AL=FFH,执行指令“NEG AL“之后,AL=01H,相当于对-1取绝对值的操作。

当使用NEG时,要提别注意该指令对标志位的影响:

1)NEG一般会引起CF=1,因为一般“0-负数“以及”0-正数“都会导致正负号的变化,进而导致溢出从而CF=1,但是当”0-0“时,正负号不会发生更迭,因此CF不会发生变化;

2)我们发现-128和+128,以及-32767和+32767的补码一样,这样的话,使用NEG指令,CF仍不会发生改变。这就是我们8位有符号数的范围为何是[127,-128]的原因,在有符号数中,-0=10000000B并且+0=00000000B,这两者在有符号数中是等价的。有符号数转无符号数会发生的变化:

C语言关于有符号和无符号变量相互赋值的探讨_BJ_Wind的博客-CSDN博客_有符号数赋值给无符号数https://blog.csdn.net/BJ_Wind/article/details/108675724

5. 比较指令:CMP OPEN1 OPEN2

该指令的含义:执行“OPEN1-OPEN2 “操作,只会影响CF标志位并不会保存结果

1)对于无符号数:

“OPEN1-OPEN2“不会导致向前借位,即OPEN1>OPEN2,这使得CF不变,接下来的跳转指令将被执行,同理,“OPEN1-OPEN2“会导致向前借位,从而导致CF=1,这表征OPEN1<OPEN2,不会执行接下来的跳转指令。

2)对于有符号数:

OF的触发标志着有符号操作数运算溢出,即异号的两个操作数运算造成溢出;SF标志位则判断最后的结果的正负。

运算形式

OF

SF

OF异或SF

结果

正数1-正数2

0

0

0

正数1>正数2

正数1-正数2

0

1

1

正数1<正数2

负数1-负数2

0

0

0

负数1>负数2

负数1-负数2

0

1

1

负数1<负数2

正数1-负数2

0

0

0

正数1>负数2

正数1-负数2

1

1

0

正数1>负数 2

负数1-正数2

0

1

1

负数1<正数2

负数1-正数2

1

0

1

负数1<正数2

溢出标志OF用于反映有符号数加减运算所得结果是否溢出。如果运算结果超过当前运算位数所能表示的范围,则称为溢出,OF的值被置为1,否则,OF的值被清为0;SF=1时,数据为负数,SF=0时数据为正数;减法的形式是“被减数-减数”。在这里最难理解的是OF=1的情况:

正数1-负数2

0

0

0

正数1>负数2

正数1-负数2

1

1

0

正数1>负数 2

负数1-正数2

0

1

1

负数1<正数2

负数1-正数2

1

0

1

负数1<正数2

我们举例进行说明:

OF=1说明计算结果溢出,即计算结果不准确,SF=1表示计算结果为负数,但是由于计算结果不准确,因此准确的计算结果应为“正数”。

OF=0说明计算结果并未溢出,即计算结果准确,SF=1表示计算结果正数,因此准确的计算结果应为“正数”。

OF=1说明计算结果溢出,即计算结果不准确,SF=0表示计算结果为正数,但是由于计算结果不准确,因此准确的计算结果应为“负数”。

OF=0说明计算结果并未溢出,即计算结果准确,SF=1表示计算结果负数,因此准确的计算结果应为“负数”。

通过以上的案例,我们可以得出如下结论:

① 对于有符号数:

判断结果

OF异或SF

ZF

被减数>=减数

0

1

被减数>减数

0

0

被减数<=减数

1

1

被减数<减数

1

0

被减数==减数

0

1

② 对于无符号数:

判断结果

CF

ZF

被减数>=减数

0

1

被减数>减数

0

0

被减数<=减数

1

1

被减数<减数

1

0

被减数==减数

0

1

3)无论对于有符号数还是无符号数,当ZF=1时,表征着运算结果=0,即OPEN1=OPEN2。

我们举个例子,来说明CMP指令的应用:

在内存数据段从DATA开始的单元中存放着两个8位无符号数,将数值最大的数据送入数据段的MAX存储单元中:

与CMP相关的跳转指令如下所示:

指令

标志位

含义

JL, JNGE

有符号

“SF异或OF”=1

被减数<减数

JNL, JGE

“SF异或OF”=0或者ZF=1

被减数>=减数

JLE, JNG

“SF异或OF”=1或者ZF=1

被减数<=减数

JNLE, JG

“SF异或OF”=0

被减数>减数

JB, JNAE

无符号

CF=1

被减数<减数

JNB, JAE

CF=0

被减数>减数

JBE, JNA

CF=1或者ZF=1

被减数<=减数

JNBE, JA

CF=0或者ZF=1

被减数>=减数

乘法运算

乘法运算中操作数的寻址方式是隐含寻址,乘法运算涉及的寄存器:

累加寄存器AX

用于存放16位的计算结果或者32位计算结果的低16位

用于存放16位的乘数

累加寄存器的低8位AL

用于存放8位的乘数

数据寄存器DX

用于存放32位计算结果的高16位

1. 无符号数的乘法运算:MUL OPEN

指令含义:如果OPEN为8位操作数,则将AL与OPEN的乘积放入AX寄存器中;如果OPEN为16为操作数,则将AX与OPEN的乘积的高16位放入DX中,低16位放入AX中。

存储器操作数

MUL WORD PTR([BX])

MUL BYTE PTR([BX])

寄存器操作数

MUL BX

注意:我们前面提到过,单操作数指令的操作数一定不可以是立即数!

2. 有符号数的乘法运算:IMUL OPEN

存储器操作数

IMUL WORD PTR([BX])

IMUL BYTE PTR([BX])

寄存器操作数

IMUL BX

有符号运算的一般流程:

因此,IMUL相较于MUL的不同就在于“两次取补码的过程”。

乘法相较于除法的不同就在于:

乘法

被乘数为8位

乘数为8位

结果为16位

被乘数为16位

乘数为16位

结果为32位

除法

被除数为16位

除数为8位

商为8位

余数为8位

被除数为32位

除数为16位

商为16位

余数为16位

除法运算

除法运算中操作数的寻址方式是隐含寻址,除法运算涉及的寄存器:

累加寄存器AX

用于存放16位的商

用于存放32位被除数的低16位

累加寄存器的低8位AL

用于存放8位的商

数据寄存器DX

用于存放32位被除数的高16位

注意:除法的运算法则:被除数/除数=商…余数

1. 无符号的除法运算:DEV OPEN

指令含义:如果OPEN为8位数据,则执行“AX/OPEN”,将商放入AL中,余数放入DL中;如果OPEN为16位数据,则执行“(DX<<16+AX)/OPEN”,将商放入AX中,余数放入DX中。

存储器操作数

DEV WORD PTR([BX])

DEV BYTE PTR([BX])

寄存器操作数

DEV BX

注意:对于所有单操作数来说,操作数不可以是立即数,并且当该操作数充当目标操作数时,操作数也不可以是段寄存器和IP指令指针寄存器。

2. 有符号的除法运算:IDEV OPEN

有符号运算的一般流程:

因此,IDEV相较于DEV的不同就在于“两次取补码的过程”。

存储器操作数

IDEV WORD PTR([BX])

IDEV BYTE PTR([BX])

寄存器操作数

IDEV BX

逻辑运算指令

除“NOT非指令”外,AND、OR、TEST指令均会影响状态标志位,其中OF/CF在运算后总会被置位0。逻辑指令操作数的限制如下所示:

逻辑指令目标操作数源操作数
TEST、XOR、OR、AND存储器操作数存储起操作数
寄存器操作数寄存器操作数
立即数操作数
NOT存储器操作数
寄存器操作数

我们看到“逻辑运算指令和MOV这种数据传送指令的最大不同就在于逻辑运算指令的操作数可以均为存储器操作数”。而且要格外注意:所有逻辑运算指令都应注意“均会使得OF/CF清零”并且不会影响AF这样的进位标志位,其他SF、ZF这样的标志位正常影响。

1. AND与运算:AND OPEN1 OPEN2

指令含义:对OPEN1和OPEN2的对应位进行与运算,并将结果送给OPEN1

AND与运算指令的使用案例:

1)确定某一位的状态

千万要注意:不要将CF和OF标志位用于判断,因为除非运算外,逻辑运算都会使得CF/OF=0!

2)对某位进行清零操作

以上操作将bit7-bit4清零,取出bit3-bit0这4个位的信息。

3)在不改变操作数的情况下,对CF/OF进行清零操作

除了非运算外,其他的逻辑运算(OR/AND/TEST)均会将CF/OF置位零。

AND指令使用示例:

2. OR或运算指令:OR OPEN1 OPEN2

指令含义:将OPEN1与OPEN2的对应位进行或运算,再将结果放入OPEN1中

OR或运算指令的使用案例:

1)对某位进行置一操作

2)在不改变操作数的情况下,对CF/OF进行清零操作

除了非运算外,其他的逻辑运算(OR/AND/TEST)均会将CF/OF置位零。

3. NOT非运算指令:NOT OPEN1

指令含义:将OPEN1按位取反,并且将结果送回OPEN1当中

切记:由于OPEN1既是源操作数又是目标操作数,因此OPEN1不可以是立即数,并且又由于其是单操作数格式,单操作数的共性就在于“当操作数为存储器操作数时,一定要使用PTR去限定其字长(OPEN1可以是16位/8位)”。

4. XOR异或指令:XOR OPEN1 OPEN2

指令含义:将OPEN1与OPEN2按位异或,并且将结果送入OPEN1中

XOR异或的用处:

1)对寄存器进行清零操作

我们回顾一下对寄存器进行清零操作都有哪些:

指令

目标操作数位置

源操作数位置

响应速度

SUB AX AX

寄存器

寄存器

AND AX 0H

寄存器

代码段

XOR AX AX

寄存器

寄存器

如果只看操作数所在位置来指令的响应速度的话:

寄存器操作数>立即数操作数>存储器操作数。为何这样说呢?

我们回顾一下8086CPU的内部结构,内部结构分为BIU和EU两部分,并且EU中ALU执行指令时不仅要读出指令预取队列中的指令还要获取操作数,如果我们的操作数就在BIU的寄存器中,那么取操作数的速度会非常快。然后就是从代码段取数据,因为立即数操作数存放在代码段中与指令存放在一起,因此在指令预取时就会将立即数操作数和指令一道被预取至BIU的指令预取队列中,由于数据从代码段取相较于从CPU内部寄存器取稍慢一些,因此立即数操作数在速度上稍逊于寄存器操作数。

在我们访问内存的数据段/附加段去获取存储器操作数时,需要进行以下几步:

向地址总线输出地址->存储器接收到读指令->数据被放置在数据总线上

这个速度就慢了很多,因此存储器操作数的访问速度最慢,这也导致指令响应速度最慢。

2)在不改变操作数的情况下,对CF/OF进行清零操作

除了非运算外,其他的逻辑运算(OR/AND/TEST)均会将CF/OF置位零。

5. TEST测试指令:TEST OPEN1 OPEN2

指令的含义:OPEN1和OPEN2按位相与,只影响标志位,不影响OPEN1中的数据

一般向TEST这样,不影响目标操作数中的数据,仅仅只影响标志位的,其后一般都跟着跳转指令(JZ、JNZ等),而且所有逻辑运算指令都应注意“均会使得OF/CF清零”并且不会影响AF这样的进位标志位,其他SF、ZF这样的标志位正常影响。

TEST指令的使用:测试某位的状态

TEST指令的实质还是AND,只不过将结果舍弃,只保留对标志位的影响而已,TEST相当于一个可以做条件判断的增强版AND

移位操作指令

移位操作指令大体上分为两类“循环移位指令”和“非循环移位指令”,从名字上就可以看出“循环移位指令”经过有限次的调用可以恢复到原来的位置,但是“非循环移位指令则做不到”。移位指令从理论上来说应该有三个数据——“被移动的对象”、“移动的位数”和“移动的方向”。但是不同的移动的方向可以调用不同的指令助记符,那么两个操作数则一个表示“被移动对象”另一个表示“移动的位数”,这样的话,移位操作指令本质上为单操作数格式

除此之外,表征移动位数的源操作数可以立即寻址也可以寄存器寻址(只可以是CL),但注意当移动位数为1时可以采用这两种寻址过程中的任意一种;当移动位数不为1时,需采用寄存器寻址。既然移位操作指令本质上为单操作数指令,那么当目标操作数为存储器操作数时,需要使用PTR指定字长,并且目标操作数不可以是立即数操作数。

1. 非循环移位指令

算数移位操作和逻辑位移操作的区别就在于“算数位移操作针对的是有符号数,逻辑位移操作针对的是无符号数”。移位操作指令影响标志位OF、CF、PF、SF、ZF,对于有符号数来说,重点看OF,如果OF=1,说明移位之后结果溢出;对于无符号数来说,重点看CF,如果CF=1,说明移位之后结果溢出。其中OF标志位的判断是CF异或SF来得出的。

1)算数左移指令:SAL OPEN1 OPEN2

指令的含义:将OPEN1左移OPEN2位,并将结果存入OPEN1中

目标操作数要求

源操作数要求

存储器操作数

PTR限制字长

立即数操作数

1

寄存器操作数

寄存器操作数CL

>=1

算术左移的实质和逻辑左移的实质一样对标志位的影响也一样,这是因为无论是有符号数还是无符号数乘法运算都是向左移位同时低位补0,但是当OPEN1为有符号数建议使用SAL, OPEN1为无符号数建议使用SHL,这样便于区分有/无符号数的操作:

2)逻辑左移指令:SHL OPEN1 OPEN2

指令的含义:将OPEN1左移OPEN2位,并将结果存入OPEN1中

目标操作数要求

源操作数要求

存储器操作数

PTR限制字长

立即数操作数

1

寄存器操作数

寄存器操作数CL

>=1

算术左移的实质和逻辑左移的实质一样对标志位的影响也一样,这是因为无论是有符号数还是无符号数乘法运算都是向左移位同时低位补0,但是当OPEN1为有符号数建议使用SAL, OPEN1为无符号数建议使用SHL,这样便于区分有/无符号数的操作:

3)算术右移指令:SHR OPEN1 OPEN2

指令的含义:将OPEN1右移OPEN2位,并将结果存入OPEN1中

目标操作数要求

源操作数要求

存储器操作数

PTR限制字长

立即数操作数

1

寄存器操作数

寄存器操作数CL

>=1

算术右移的实质和逻辑右移的实质稍有不同,因为非循环移位中算术移动涉及的都是有符号数的乘除法,而且对于除法来讲,算数位移不可以改变SF符号标志位,即算术右移不可以改变正负,因此算术右移会一直在高位补符号位。

4)逻辑右移指令:SHR OPEN1 OPEN2

指令的含义:将OPEN1右移OPEN2位,并将结果存入OPEN1中

目标操作数要求

源操作数要求

存储器操作数

PTR限制字长

立即数操作数

1

寄存器操作数

寄存器操作数CL

>=1

算术右移的实质和逻辑右移的实质稍有不同,因为非循环移位中逻辑位移涉及的都是无符号位,因此,我们不关注符号位,故我们在高位不断补0即可。

循环移位指令使用示例:

左移

实现乘法

右移

实现除法

2. 循环位移指令

循环位移指令不再区分算数移位还是逻辑移位了,而是分为“带CF循环左移”和“不带CF循环左移”,两种形式如下所示:

同样,对于“带CF循环右移”和“不带CF循环右移”,两种形式如下所示:

循环移位操作指令的用途:

1)检查某一位的状态:

我们前面提及过一些检查某位状态的操作:

AND OPEN1 OPEN2

AND AX 0001H

破坏性操作

TEST OPEN1 OPEN2

TEST AX 0001H

非破坏性操作

SHL OPEN1 OPEN2

MOV CL 3 ;SHL AX CL

非破坏性操作

注意:非循环/循环移位指令移动若干次之后,都会在CF中取得我们想要位的状态。

2)进行32位操作数的移位运算(乘除法)

3)高低位交换

不管是RCR循环右移指令还是ROL循环左移指令,循环4次之后,都可以将一个字节的高四位和低四位相交换,或者循环8次之后,都可以将一个字的高字节和低字节相交换。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

肥肥胖胖是太阳

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值