微机原理与接口技术: 3 - 8088/8086 指令系统 - 六大类指令 数据传送,算术运算, 逻辑和移位运算,串操作, 控制转移指令 和 处理器控制指令 —— 万字长文!干货满满!

8088/8086 指令系统

以8088/8086 CPU 指令系统为基础, 介绍指令的一般概念和执行过程, CISC 和 RISC 指令的概念, 寻址方式 以及 不同类型指令的功能.

Goal:

  1. 了解 指令的的一般概念, 指令的基本格式以及指令的执行过程.
  2. 熟悉 指令对操作数的各种寻址方式
  3. 深入理解 8086指令系统全部六大指令的功能, 包括指令操作码的含义, 指令对操作数的要求和指令的执行结果.

3.1 概述

控制计算机完成指定操作并能够被计算机所识别的命令称为指令 Instruction. 一台计算机能够识别的所有指令的集合称为该机的指令系统 Instruction System. 指令系统 定义了计算机硬件所能完成的基本操作.

Intel 8088/8086 CPU 指令也是 Intel 80x86系列 CPU的基本指令系统. -> 后面统称 8086指令系统.

8086指令系统共有92种基本指令. 按照功能可将其分为 六大类: 数据传送类, 算术运算类, 逻辑运算和移位, 串操作, 控制转移类, 处理器控制.

六大类指令的常用助记符:

请添加图片描述

3.1.1 指令的基本构成

1 - 指令的一般格式

一条指令通常由两个部分组成, 如图所示为双操作数指令:

请添加图片描述

指令 = 操作码 ( 指令码 ) 指令操作的对象 ( 操作数 ) = 操作码 ( 指令码 ) 源操作数 , 目标操作数 \begin{aligned} 指令 & = 操作码(指令码) 指令操作的对象(操作数) \\ & = 操作码(指令码) 源操作数, 目标操作数 \end{aligned} 指令=操作码(指令码)指令操作的对象(操作数)=操作码(指令码)源操作数,目标操作数

8086指令的长度在1~7个字节之间.

  • 操作码 - OPC / Operation Code : 占用1~2个字节, 指出指令要进行的操作.

  • 操作数 - Operand: 操作数的个数 -> 主要决定指令的长度. 一条指令的操作数为 0~2个.

相应的, 指令由三种格式:

  1. 零操作数指令 : 操作数是隐含存在的. 这类指令操作的对象通常为处理器本身.
  2. 单操作数指令.
  3. 双操作数指令
2 - 指令中的操作数类型

操作数主要有三种 : 立即数操作数, 寄存器操作数 和 存储器操作数

立即数操作数 - Immediate Operands : 常数

立即数的字长范围是1~2字节, 可为 无符号数 或 有符号数.

立即数没有表示地址的含义 -> 指令中只能用作源操作数.

寄存器操作数 - Register Operands

8086 CPU的 8个通用寄存器和4个段寄存器 可以作为指令中的 寄存器操作数.

可作为 源操作数 和 目标操作数.

通常来说, 通用寄存器 存放 参加运算的数据 或 数据所在存储器单元的 偏移地址. 段寄存器 存放 当前操作数的 段基地址.

存储器操作数 - Memory Operands

含义为 : 参加运算的数据是存放在内存中的.

存储器操作数也通常称为字节或字, 极个别的指令中由双字长的操作数.

可作为 源操作数 和 目标操作数.

第二章已经学习, 能够唯一标识一个存储器单元的是它的物理地址(段基址:偏移地址) => 要寻找一个存储器操作数 -> 必须确定操作数所在的逻辑段. 若指令中没有明确指出 操作数所在段, 采用默认的段寄存器来确定操作数的段基地址.

偏移地址EA(Efficient Address) 由用户的指令给出.

3.1.2 指令的执行时间

一条指令的执行时间 包括 : 取指令, 取操作数, 执行指令 及 传送结果

立即数操作数, 寄存器操作数 和 存储器操作数 中, 寄存器操作数 执行速度最快, 立即数操作数 次之, 存储器操作数 最慢.

因为 =>

  • 寄存器位于CPU内部, 执行寄存器操作数指令时, EU可以直接从CPU内部寄存器中取得操作数, 不需要 访问内存

  • 立即数作为指令的一部分, 随指令码被BIU取出后一同存放进入IQ 并被 EU获取.

  • 存储器操作数 存放在内存中, 要由BIU 计算出 其20位物理地址 在执行存储器的读写操作.

3.2 寻址方式

寻址方式 - Addressing modes : 指获得操作数所在的地址的方法, 在8088/8086系统中, 分为两种 :

  1. 寻找操作数的地址.
  2. 寻找要执行的下一条指令的地址, 即程序的地址.

3.2.1 立即寻址

立即寻址 - Immediate Addressing : 只针对源操作数. 此时, 源操作数是一个立即数, 作为指令的一部分, 源操作数紧跟在指令的操作码之后, 存放于内存的代码段中. CPU 取指令时一并取出并直接参与运算.

这里的立即数可以是 1 / 2 字节的整数, 若为16位 2字节数, 存放时:

低八位 在低地址单元存放, 高八位 在高地址单元存放.

E.g. 执行: M O V A X , 3102 H MOV AX, 3102H MOVAX,3102H

=> 将16位立即数3102H送入累加器AX.

=> 结果 : A H = 31 H , A L = 02 H AH = 31H, AL = 02H AH=31H,AL=02H

请添加图片描述

3.2.2 直接寻址

直接寻址 - Direct Addressing : 表示参加运算的数据存放在内存中 -> 指令中的操作数是存储器操作数.

规定偏移地址必须使用"[]", "[]"内用16位常数表示存放数据的偏移地址, 段基址默认为数据段DS, 允许段重设.

E.g. 执行指令: M O V A X , [ 3102 H ] MOV \qquad AX, [3102H] MOVAX,[3102H]

=> 将数据段中 偏移地址 O f f s e t A d d r = 3102 H , 3103 H 偏移地址Offset Addr = 3102H, 3103H 偏移地址OffsetAddr=3102H,3103H 两个单元的内容送入累加器AX中.

假设 DS = 2000H, 则所寻找的操作数的物理地址为:

=> 20000 H + 3102 H = 23102 H 20000H + 3102H = 23102H 20000H+3102H=23102H

请添加图片描述

若操作数不是存在DS段, 则在指令中要用段重设符号声明:

E.g. 执行指令 M O V B L , E S : [ 1200 H ] MOV \qquad BL, ES:[1200H] MOVBL,ES:[1200H]

=> 将附加段中偏移地址为1200H单元的内容送到BL寄存器中.

3.2.3 寄存器寻址

寄存器寻址 - Register Addressing : 指令的操作数为CPU的内部寄存器 -> 寄存器操作数

可以是 数据寄存器(8bits/16bits), 地址指针, 变址寄存器 或 段寄存器.

E.g. 执行指令 M O V S I , A X MOV \qquad SI, AX MOVSI,AX

=> 表示将AX的内容送到寄存器SI中.

若指令执行前, S I = 4455 H , A X = 2233 H SI = 4455H, AX = 2233H SI=4455H,AX=2233H,

=> 结果 : S I = 2233 H , A X = 2233 H SI = 2233H, AX = 2233H SI=2233H,AX=2233H

请添加图片描述

采用寄存器寻址方式, 虽然指令操作码在代码段中, 但操作数在内部寄存器 -> 很快!

3.2.4 寄存器间接寻址

寄存器间接寻址 - Register Indirect Addressing : 用寄存器的内容表示操作数的偏移地址Offset Addr

存放操作数偏移地址的寄存器只允许是 SI, DI, BX 和 BP -> 简称为间址寄存器 or 地址指针.

不同的间址寄存器涉及的段寄存器不同. 默认情况下,

  • 选择 SI, DI, BX -> 操作数在DS, 段基址由DS决定;

  • 选择 BP -> 操作数在堆栈段SS, 段基址由SS决定.

E.g. 已知, DS = 6000H, SI = 1200H

执行指令 M O V A X , [ S I ] MOV \qquad AX, [SI] MOVAX,[SI]

无段重设, 默认DS作段基址, 计算得物理地址: 60000 H + 1200 H = 61200 H 60000H + 1200H = 61200H 60000H+1200H=61200H

=> 执行结果 : AX = 3344H

请添加图片描述

若操作数存放在ES, 应段重设为: M O V E S , [ S I ] MOV \qquad ES, [SI] MOVES,[SI]

3.2.5 寄存器相对寻址

寄存器相对寻址中, 操作数在 内存中的 存放地址(偏移地址) 由间址寄存器 + 指令中给出的一个8/16bits的位移量组成.

操作数所在段由间址寄存器决定(同上).

E.g. 设: D S = 6000 H , B X = 1000 H , D A T A = 0008 H DS = 6000H, BX = 1000H, DATA = 0008H DS=6000H,BX=1000H,DATA=0008H

执行指令 M O V A X , D A T A [ B X ] MOV \qquad AX, DATA[BX] MOVAX,DATA[BX]

=> 操作数的物理地址 = 60000H + 1000H + 0008H = 61008H

=> 执行结果 : A X = 5566 H AX = 5566H AX=5566H

请添加图片描述

E.g. 某数据表的首地址(偏移地址)为TABLE, 要取出该表中的第10个字节 并存放到 AL 中 :

$MOV \qquad SI, 9$
$MOV \qquad AL, TABLE[SI]$

TIPS: 在ASM语言中, 相对寻址指令的书写格式允许多种不同的形式, 以下几种写法完全等价:

MOV   AL, DATA[SI]
MOV   AL, SI[DATA]
MOV   AL, DATA+[SI]
MOV   AL, [SI]+DATA
MOV   AL, [DATA+SI]
MOV   AL, [SI+DATA]

3.2.6 基址-变址寻址

基址-变址寻址 - Base-Indexed Addressing : 由一个基址寄存器(BX, BP)的内容 和 一个变址寄存器(SI, DI)的内容相加而形成操作数的偏移地址.

默认情况下, 基址寄存器若

  • 使用 BX - 则段地址在DS中;

  • 使用 BP - 作基址寄存器, 则段地址在SS中;

设: D S = 8000 H , B X = 2000 H , S I = 10000 H DS = 8000H, BX = 2000H, SI = 10000H DS=8000H,BX=2000H,SI=10000H

执行指令 : M O V A X , [ B X ] [ S I ] MOV \qquad AX, [BX][SI] MOVAX,[BX][SI]

则操作数的 物理地址 = 80000 H + 2000 H + 1000 H = 83000 H 物理地址 = 80000H + 2000H + 1000H = 83000H 物理地址=80000H+2000H+1000H=83000H

=> A L = [ 83000 H ] , A H = [ 83001 H ] AL = [83000H], AH = [83001H] AL=[83000H],AH=[83001H]

请添加图片描述

3.2.7 基址-变址-相对寻址

基址-变址-相对寻址 - Base-Indexed-Relative Addressing : 指令中指定一个基址寄存器 和 一个变址寄存器, 同时还给出一个8/16bits的位移量.

E.g.

执行指令 : M O V A X , 5 [ D I ] [ B X ] MOV \qquad AX, 5[DI][BX] MOVAX,5[DI][BX]

该指令将段地址为DS, 偏移地址为 BX+DI+5 的连续两个存储单元的内容送到AX.

则操作数的 物理地址 = ( 假设 D S 为 ) 80000 H + 2000 H + 1000 H + 0005 = 83005 H 物理地址 = (假设DS为)80000H + 2000H + 1000H + 0005 = 83005H 物理地址=(假设DS)80000H+2000H+1000H+0005=83005H

请添加图片描述

使用这种寻址方式可以很方便地访问二维数组 <=> 基址寄存器 存放数组的首地址(OA) ; 变址寄存器和位移分量分别存放 行和列.

3.2.8 隐含寻址

隐含寻址 - Implicit Addressing : 将一个操作数隐含在指令码中的寻址方式.(如乘法指令MUL)

E.g. 执行指令 M O V B L MOV \qquad BL MOVBL

=> 把AL中的内容于BL相乘, 乘积送到AX寄存器

=> A L × B L − > A X AL \times BL -> AX AL×BL>AX

3.3 8086指令系统

首先给出一些常用术语符号 :

O P R D 泛指操作数 m e m 存储器操作数 a c c 累计爱妻操作数 d e s t 目标操作数 s r c 源操作数 d i s p 8 / 16 b i t s 偏移量 , 可用符号地址表示 D A T A 8 / 16 b i t s 立即数 p o r t I O 端口 , 可用数字或表达式表示 [ ] 表示存储器操作数 , 方括号中的内容表示数据的偏移地址 \begin{aligned} OPRD & \qquad 泛指 操作数 \\ mem & \qquad 存储器操作数 \\ acc & \qquad 累计爱妻操作数 \\ dest & \qquad 目标操作数 \\ src & \qquad 源操作数 \\ disp & \qquad 8/16bits偏移量, 可用符号地址表示 \\ DATA & \qquad 8/16bits立即数 \\ port & \qquad IO端口, 可用数字或表达式表示 \\ [] & \qquad 表示存储器操作数, 方括号中的内容表示数据的偏移地址 \\ \end{aligned} OPRDmemaccdestsrcdispDATAport[]泛指操作数存储器操作数累计爱妻操作数目标操作数源操作数8/16bits偏移量,可用符号地址表示8/16bits立即数IO端口,可用数字或表达式表示表示存储器操作数,方括号中的内容表示数据的偏移地址

3.3.1 数据传送指令 - Data Transfer

按照功能可将其分为4小类 :

  1. 通用数据传送指令 - universal data transfer
  2. 目标地址传送指令 - address transfer
  3. 标志传送指令 - flag operation
  4. 输入输出指令 - input/output
1 - 通用数据传送指令 - universal data transfer

包括 :

  1. 一般传送指令 M O V MOV MOV
  2. 堆栈操作指令 P U S H , P O P PUSH, POP PUSH,POP
  3. 交换指令 X C H G XCHG XCHG
  4. 查表转换指令 X L A T XLAT XLAT
  5. 字位扩展指令
一般传送指令 M O V MOV MOV

指令格式及操作:

M O V d e s t , s r c ; ( d e s t ) ← ( s r c ) MOV \qquad dest, src \qquad ;(dest)\leftarrow (src) MOVdest,src;(dest)(src)

功能: 将一个操作数从源地址传送到目标地址, 而源地址中数据不变.

指令可以实现的操作, 一些例子:

请添加图片描述

M O V MOV MOV指令的要求:

  1. M O V MOV MOV指令中两个操作数字长必须一致.
  2. 两个操作数不能同时为存储器操作数.
  3. 不能用立即数直接给段寄存器幅值.
  4. 两个操作数不能同时为段寄存器
  5. 一般情况下, 指令指针IP 及 代码段寄存器CS的内容不通过MOV指令修改 ->不作 d e s t dest dest, 可作 s r c src src.
  6. 通常情况下, F L A G S FLAGS FLAGS整体不作操作数

E.g. 把内存中首地址为 MEM1 的200个字节送到首地址为 MEM2的区域中. 下面的程序段中某些指令还没学到但是咱这里先用用.

        MOV     SI, OFFSET MEM1 ; 源数据块(偏移地址)送入SI
        MOV     DI, OFFSET MEM2 ; 目标数据块(偏移地址)送入DI
        MOV     CX, 200         ; 数据块长度送CX <=> 循环次数CX
NEXT:   MOV     AL, [SI]        ; 源数据块中, 当前字节 送入 AL
        MOV     [DI], AL        ; AL中, 内容 送入 目标数据块
        INC     SI              ; SI+1, 修改源地址指针
        INC     DI              ; DI+1, 修改目的地址指针
        DEC     CX              ; CX-1, 修改循环次数 
        JNZ     NEXT            ; 若循环次数(CX)不为零, 跳回NEXT标号处
        HLT                     ; 停止
堆栈操作指令 PUSH 和 POP

=> 堆栈的概念. 堆栈用以存放寄存器or存储器中暂时不用又必须保存的数据. 内存中处于堆栈段, 其短地址放在堆栈寄存器SS中, 可以将堆栈看作是一个小存储器, 遵循以下的原则:

  1. 堆栈的存取每次必须是一个字(16bits), 即堆栈指令中的操作数必须是16位, 而且只能是寄存器或存储器操作数.
  2. 遵循"LIFO(last in first out)"原则. 向堆栈段中存放数据时, 总是从高地址向低地址方向增长; 从堆栈段中国取出数据时, 相反.
  3. 堆栈段在内存中的位置由SS决定, 堆栈指针SP总是指向栈顶 -> SP的内容 = 当前栈顶的偏移地址.

在程序中, 堆栈主要应用于 子程序调用, 中断响应 等操作时的参数保护.

堆栈操作指令 : P U S H PUSH PUSH - 压栈 / P O P POP POP - 出栈

P U S H s r c P O P d e s t \begin{aligned} PUSH \qquad & src \\ POP \qquad & dest \\ \end{aligned} PUSHPOPsrcdest

其中, 指令中的操作数必须为 字操作数(16bits), 可以为: 1. 16bits 通用寄存器 or 段寄存器(CS除外, PUSH CS合法但POP CS不合法).

  1. 压栈指令 P U S H O P R D PUSH \qquad OPRD PUSHOPRD

将指令中指定的字操作数压入堆栈, 执行过程为:

SP - 2 -> SP
OPRD 高8位 -> [SP+1]
OPRD 低8位 -> [SP]

例如:

PUSH AX                 ;通用寄存器内容压入堆栈
PUSH WORD PIR[DATA+ SI] ;数据段中两个连续存储单元内容压入堆栈
POP DS                  ;从栈顶弹出一个字到段寄存器
POP WORD PIR[BX]        ;从栈顶弹出一个字到数据段两个连续存储单元中

请添加图片描述请添加图片描述

  1. 出栈指令 P O P O P R D POP OPRD POPOPRD

将当前栈顶的一个字送到指定的目标地址, 并紧接着修改堆栈指针, 使 SP 指向新的栈顶位置. 指令的执行过程为 :

[SP] -> OPRD低8位
[SP+1] -> OPRD低高位
SP+2 -> SP

请添加图片描述

交换指令 X C H G XCHG XCHG

指令格式及操作 :

XCHG  OPRD1, OPRD2      ;(OPRD1)<->(OPRD2)

将源地址与目标地址中的内容进行互换.

要求 :

  1. src 和 dest 可用使寄存器 或存储器, 但不能同时是存储器.
  2. 不能为段寄存器操作数 -> 段寄存器的内容不可以参加交换.
  3. 两个操作数字长必须相同.

例如,

XCHG    AX, BX      ;AX->BX, BX->AX
XCHG    CL, DL      ;CL->DL, DL->CL

E.g.请添加图片描述

XLAT

为一条字节的查表转换指令, 可以根据表中元素的序号查出表中相应元素的内容.

预先将要查找的代码排成一个表放在内存某个区域中, 指令要求 :

  • 将表的首地址 送寄存器BX
  • 要查找的元素的序号送寄存器AL

执行XLAT指令后, 表中指定序号的元素被存入AL, 指令格式为:

XLAT                ;   将偏移地址为BX+AL所指单元的内容送到AL中
XLAT    src_table   ;   (src_table表示要查找的表的首地址)

E.g.

请添加图片描述

2 - 输入输出指令 - Input/Output

专门面向IO端口读写的指令, 共有两条 : I N , O U T IN, OUT IN,OUT

  • I N IN IN 用于把 累加器AL/AX的内容 写到 IO端口

  • O U T OUT OUT 用于把 IO端口 的内容 读到 累加器AL/AX

-> 只有累加器AL允许与IO端口进行数据传输

指令中表示IO端口时, 允许使用两种寻址方式 :

  1. 直接寻址 : 指令中的IO端口地址为8bits, 此时允许寻址256个端口(0~FFH).
  2. 寄存器间接寻址 : 端口地址为16bits, 此时允许寻址 256个端口(0~FFFFH).

间接寻址适用范围更大, 一般采用这种.

输入指令 I N IN IN

指令格式:

IN acc, port        ; 直接寻址, port为用8位 立即数 表示的端口地址
IN acc, DX          ; 间接寻址, DX给出 16bits端口地址

E.g.

MOV     DX, 03B0H   ;16bits端口地址送DX
IN      AL, DX      ;从地址为3B0H的端口输入一个字节到AL
IN      AX, 3FH     ;直接寻址, 从地址3FH的端口输入一个字节到AX
输出指令 O U T OUT OUT

指令格式:

OUT     port, acc   ;直接寻址, port为8bits立即数
OUT     DX, acc     ;间接寻址, DX给出16bits port

E.g.

OUT     43H,AL      ;将AL的内容输出到地址为43H的端口
OUT     44H,AX      ;将AX的内容输出到地址为44H的端口
MOV     DX, 33EH    ;端口地址33EH送DX
OUT     DX,AL       ;将AL的内容输出到地址为33EH的端口
  1. 取偏移地址指令

指令格式:

LEA REG16, mem

将存储器操作数mem 的16位偏移地址送到指定的寄存器. 这里, 源操作数必须是存储器操作数, 目标操作数必须是16位通用寄存器.

给出几个E.g., 注意LEA和MOV的区别 :

E.g.

LEA     BX, BUFFER  ;
MOV     AL, [BX]    ;
MOV     AH, [BX+1]  ;

E.g.

请添加图片描述

=> 执行结果: 第1条指令执行后 B X = 1050 H BX = 1050H BX=1050H; 第2条指令执行后 B X = 3344 H BX = 3344H BX=3344H.

请添加图片描述

4 - 其他传送指令

请添加图片描述

3.3.2 算术运算指令

8086提供基本的四则运算指令 -> 可实现字节或字, 无符号数或有符号数 的运算.

1 - 加法运算指令
  1. 普通加法指令 A D D ADD ADD
  2. 带进位位的加法指令 A D C ADC ADC
  3. 加一指令 I N C INC INC
普通加法指令 A D D ADD ADD

指令格式:

ADD     OPRD1, OPRD2    ;OPRD1 <- OPRD1 + OPRD2

将src与dest相加送回dest地址.

注意合法操作与非合法操作:

请添加图片描述

ADD指令的执行会 对全部六个状态标志位产生影响:

E.g.

MOV     AL, 7EH
ADD     AL, 5BH

这两条指令执行后, 状态标志位的状态分别为 :

AF=1    表示 D_3 向 D_4 有进位
CF=0    表示MSB最高位向前无进位
OF=1    表示若为有符号数加法,其运算结果产生溢出
PF=0    表示8位的运算结果中,"1"的个数为奇数
SF=1    表示运算结果的最高位为"1"
ZF=0    表示运算结果不为"0"

事实上, 指令执行后, AL=D9H > 7FH (8bits signed 的最大值), 但 D9H < FFH (8bits unsigned 的最大值)

=> 有 CF=0, OF=1

带进位位的加法指令 ADC

指令格式:

ADC     OPRD1, OPRD2        ;OPRD1 <- OPRD1 + OPRD2 + CF

ADC指令与ADD指令在功能, 格式 及 对标志位的影响上基本一致.

E.g. 设 CF = 1

MOV     AL, 7EH
ADC     AL, 0ABH

指令执行后 => AL = 7EH + 0ABH + 1 = 2AH, 且有进位CF = 1.

E.g.

请添加图片描述

加 1 指令 I N C INC INC

指令格式:

INC     OPRD    ;OPRD <- OPRD + 1

指定操作数自增1, 再送回该操作数.

2 - 减法指令

共5条指令 :

  1. 不考虑借位的普通减法指令 S U B SUB SUB
  2. 考虑借位的减法指令 S B B SBB SBB
  3. 减 1 指令 D E C DEC DEC
  4. 求补指令 N E G NEG NEG
  5. 比较指令 C M P CMP CMP
不考虑借位的普通减法指令 S U B SUB SUB

指令格式:

SUB     OPRD1, OPRD2    ;OPRD1 <- OPRD1 -  OPRD2

dest - src 并送回dest所在地址.

E.g.

请添加图片描述

考虑借位的减法指令 S B B SBB SBB

指令格式:

SBB     OPRD1, OPRD2    ;OPRD1 <- OPRD1 -  OPRD2 - CF

dest - src - CF 并送回dest所在地址.

E.g.

请添加图片描述

减 1 指令 D E C DEC DEC

指令格式:

DEC     OPRD1    ;OPRD1 <- OPRD1 - 1

指定操作数自 - 1, 再送回该操作数所在ADDR.

E.g.

请添加图片描述

DEC指令常用在循环中修改循环次数:

请添加图片描述

求补指令 N E G NEG NEG

指令格式:

NEG     OPRD        ;OPRD <- 0 - OPRD

用 0 减去 OPRD, 结果送回该操作是所在地址.

OPRD 可以是寄存器或存储器操作数, 利用该指令可以得到负数的绝对值(对一个负数取补码(~)就相当于用0-该数)

E.g. 设 A L = F F H AL = FFH AL=FFH, 执行 :

NEG     AL

=> AL = 0 - FFH = 01H, 即实现了对 FFH (-1的补码)的求补.

注意:

  1. 执行 N E G NEG NEG 指令后, 一般情况下都会使CF为1.

  2. 当指定的操作数的值为 80H(-128) or 8000H(-32768), 则执行NEG指令后结果不变, 但OF置1.

比较指令 C M P CMP CMP

指令格式:

CMP     OPRD1, OPRD2        ;OPRD1 - OPRD2, 结果不送回OPRD1

用 dest - src, 结果不送回, 只会影响6个状态标志位.

C M P CMP CMP 主要用于比较两个数的大小关系. 执行完 C M P CMP CMP之后 可以 根据标志位的状态判断两个操作数的大小关系, 判据如下:

  1. 相等关系 <=> ZF = 1

  2. 大小关系 - unsigned <=>
    CF = 0 -> 被减数OPRD2 > 减数OPRD1
    -> OPRD1 - OPRD2 不需要借位
    CF = 1 -> OPRD1 < OPRD2

  3. 大小关系 - signed (稍微复杂一些) <=>

    对于两个同符号数, 相减不会发生OVERFLOW
    -> OF = 0

    • SF = 0 -> OPRD1 < OPRD2
    • SF = 1 -> OPRD1 > OPRD2

    对于两个非同符号数, 可能发生OVERFLOW
    -> 若 OF = 0 :

    • SF = 0 -> OPRD1 < OPRD2
    • SF = 1 -> OPRD1 > OPRD2

    -> 若 OF = 1 :

    • SF = 0 -> OPRD1 > OPRD2
    • SF = 1 -> OPRD1 < OPRD2

    归纳 =>

    • O F ⨁ S F = 0 OF \bigoplus SF = 0 OFSF=0 -> OPRD1 < OPRD2
    • O F ⨁ S F = 1 OF \bigoplus SF = 1 OFSF=1 -> OPRD1 > OPRD2

E.g. 在内存数据段从DATA开睡的单元中存放两个8bits unsigned num, 比较大小并将打的数送MAX单元.

        LEA     BX, DATA    ;DATA 偏移地址送 BX
        MOV     AL, [BX]    ;第一个unsigned送AL
        INC     BX          ;BX++, 指向第二个unsigned
        CMP     AL, [BX]    ;比较
        JNC     DONE        ;若CF=0(无进位, 表示第一个数大), 转向DONE
        MOV     AL, [BX]    ;否则, 第二个unsigned送AL
DONE:   MOV     MAX, AL     ;将较大的unsigned送MAX
        HLT
3 - 乘法指令

采用隐含寻址方式, 隐含的dest为AX(与DX), src由指令给出.

支持字节相乘和字相乘, 对8/16bits 的乘法, 乘积为16/32bits, 存放在AX/高16位放在DX, 低16位放在AX.

对于unsigned乘法, 如果乘积的高半部分 != 0 -> CF=OF=1 -> AH / DX 中包含乘积的有效数字.

对于signed乘法, 如果乘积的高半部分 为 低半部分的符号位的扩展 -> CF=OF=0, 否则 CF=OF=1.

无符号乘法

指令格式:

MUL OPRD

指令的操作为 :

  • 字节乘法 A X ← O P R D × A L AX \leftarrow OPRD \times AL AXOPRD×AL
  • 字乘法 D X : A L ← O P R D × A X DX:AL \leftarrow OPRD \times AX DX:ALOPRD×AX

E.g.

请添加图片描述

4 - 除法指令

指令格式:

DIV OPRD

规定: 被除数的字长必须为除数字长的两倍. 对8/16bits 的除数, 被除数为16/32bits, 存放在高16位放在DX, 低16位放在AX/AX.

指令的操作为 :

  • 字节除法 A L ← A X   %   O P R D AL \leftarrow AX ~\% ~ OPRD ALAX % OPRD
  • 字除法 A X ← D X : A X   %   O P R D AX \leftarrow DX:AX ~ \% ~ OPRD AXDX:AX % OPRD
5 - 其他算术运算指令

请添加图片描述

3.3.3 逻辑运算和移位指令

1 - 逻辑运算指令

共5条: AND, OR, NOT, XOR 及 TEST 指令

与 AND

指令格式:

AND     OPRD1, OPRD2    ;OPRD1 <- OPRD1 & OPRD2
或 OR
OR      OPRD1, OPRD2    ;OPRD1 <- OPRD1 | OPRD2
非 NOT
NOT     OPRD    ;OPRD <- ~OPRD
亦或 XOR
XOR      OPRD1, OPRD2    ;OPRD1 <- OPRD1 (+) OPRD2
测试指令 TEST

指令格式(与AND类似) :

TEST    OPRD1, OPRD2

TEST与AND基本完全一致, 区别在于TEST指令的运算结果不送回dest, 而只影响操作位.

E.g.

请添加图片描述

2 - 移位指令

包括 : 非循环移位 和 循环移位 指令两种.

当移动一位时, 移动次数由指令直接给出; 多位移位时 -> 移动的次数放入 CL寄存器

非循环移位 SAL, SAR, SHL, SHR
算术左移和移位左移 SAL / SHL

指令格式:

SHL     OPRD, 1
SAL     OPRD, 1
或者
SHL     OPRD, CL
SAL     OPRD, CL

操作如其名, 不多作解释.

请添加图片描述

逻辑右移指令 SHR

SHR格式与SHL相同:

SHR OPRD, 1
SHR OPRD, CL

如果移位次数=1, 且移位之后新的最高位 != 次高位 ->OF = 1; 否则 -> OF = 0; 若移位次数 != 1, OF状态不定.

E.g.

MOV     AL, 82H
SHR     AL, 1

=> AL = 41H, CF = 0, OF = 1.

请添加图片描述

SHR每右移一位, 右边的LSB移入CF, 左边MSB补零.

算术右移指令 - SAR

格式与SHR相同.

指令的操作: 如其名.

与SHR指令的区别是: SAR算术右移时, MSB不是补零, 而是保持不变(保持符号位)

请添加图片描述

循环移位 ROL

4条 :

  • 不带进位标志位CF的 ROL, ROR
  • 带进位标志位CF的 RCL, RCR

格式与非循环移位指令一致:

ROL/RCR..   OPRD, 1
ROL/RCR..   OPRD, CL

与非循环移位指令的主要区别在于, 移出的MSB/LSB会循环补回LSB/MSB.

4条指令的操作示意图如图所示:

请添加图片描述

3.3.4 状态指令 - Flag instruction

  • LAHF, SAHF - 隐含操作数AH, 无操作数
  • PUSHF, POPF - 隐含操作数FLAGS, 无操作数

LAHF

将FLAGS的低五位状态送AH.

请添加图片描述

SAHF

将AH数据送FLAGS.

请添加图片描述

PUSHF, POPF 同前两个指令相似, 主要用于将当前FLAGS的状态出入栈以保护和恢复状态.

PUSHF

(SP)-1←Flag register high 8-bit
(SP)-2←Flag register low 8-bit
(SP)←(SP)-2

POPF

Flag register high 8-bit←(SP)
Flag register high 8-bit←(SP)+1
(SP)←(SP)+2

3.3.4 串操作指令 - String operation

1 - 串操作指令的通性

人们将存储器中的地址两虚的若干单元的字符或数据称为字符串or数据串. 串操作指令 - 用来对串中每个字符/数据做同样操作的指令

串指令 可以处理字串, 字节串, 并在每完成对一个字/字节的操作后自动修改指针继续向下执行.

所有串操作都具有以下共同点 :

  1. 源串(src) : 默认在DS中, 允许段重设. 偏移地址用 SI 指定 -> 源串指针为 DS : SI.

  2. 目标串(dest) : 默认在ES中, 不允许段重设. 偏移地址用 SI 指定 -> ES : SI.

  3. 串长度值放在 CX寄存器中.

  4. 串操作指令本身可实现 地址指针的自动修改. 在对每个字节(or字)操作后, SI 和 DI 寄存器的内容会自动修改, 修改方向与标志位 DF 有关.

    DF = 0 -> SI 和 DI 按地址增量方向修改; 反之为按减量方向修改.

  5. 可重复使用前缀. -> 每次串操作之后, CX–.

2 - 重复操作前缀

用重复操作前缀 可以重复执行指令直到 计数器CX自减归零.

分为两类 : 无条件重复前缀, 有条件重复前缀.

  1. REP : 无条件重复 -> 重复执行操作,直到CX自减归零.
  2. REPE/REPZ : 相等/结果为零时重复, ZF = 1 且CX != 0 时重复.
  3. REPNE/REPNZ : 不相等/结果不为零时重复, ZF = 1 且CX != 0 时重复.

那么加了重复操作前缀之后程序执行起来变什么样了呢 ?

  1. 执行规定的操作.
  2. SI, DI自动增量(或减量)
  3. CX–
  4. 根据ZF的状态自动决定是否重复执行.
3 - 串操作指令 MOVS, CMPS, SCAS, LODS, STOS
  1. 传传送指令 - MOVS/Moving String

格式(3种):

MOVS    OPRD1, OPRD2
MOVSB
MOVSW

第一种多用于段重设的case. 第二三种隐含了两个操作数的地址, 此时源串 和 目标串 的地址必须符合默认值 -> src在DS, OFFSET src在SI; dest在ES, OFFSET dest在DI. (MOVSB/MOVSW 依次完成一个Byte/Word的传送)

E.g. 将 2000H:1200H地址开始的100各字节 送 6000H : 0000H 开始的内存单元中

MOV     AX, 2000H
MOV     DS, AX      ;src
MOV     AX, 6000H
MOV     ES, AX      ;dest
MOV     SI, 1200H
MOV     DI, 0000H

MOV     CX, 100
CLD                 ;DF=0, 串操作地址增量方向顺序执行
REP     MOVSB
HLT
  1. 串比较指令 - CMPS/Compare String

格式(3种) :

CMPS    OPRD1, OPRD2
CMPSB
CMPSW

类似于CMP指令, CMPS进行的时两个数据串的比较. 将src串与dest串按byte/word逐位比较, 比较结果反映在标志位上.

通常 + 条件重复前缀 REPE(REPZ) 或 REPNE(REPNZ) 连用 -> 检查两个字符串是否相等.

在加条件重复前缀的情况下, 结束串比较指令的执行时有两种可能 :

  1. 因CX=0停止.
  2. 因不满足 条件重复前缀 条件而停止.

对于 REPE(REPZ) 每判断一次之后检查ZF, 当 ZF = 1 为真 就会重复进行下去直至CX=0; 对于 REPENE(REPNZ) ZF = 0 为真 重复进行

串扫描指令 - SCAS/Scan String
SCAS    OPRD
SCASB
SCASW

隐含操作数 src = AL/AX (SCASB/SCASW)

类似CMPS, 用来在一个字符串中搜索特定的关键字. 用累加器AX/AL 与 目标串(ES:DI指定)中的值相比较

E.g. 在ES段中从2000H单元开始存放了10各Char, 找其中有无"A", 若有则记下搜索次数(放DATA1), 并计息存放"A"的地址(放DATA2)

请添加图片描述

串装入指令 - LODS/Load String
LODS OPRD
LODSB
LODSW

把由DS:SI指向的src源串 取到AL中, 并修改SI指向下一个Byte/Word. 可用REP对连续的存储单元存入相同的值. 不影响标志位.

E.g. 以MEM为首地址的内存区域中有10个以非压缩BCD码形式存放的十进制数, 值为是0~9中任意. 编一个程序将这10个数顺序显示在屏幕上.

        LEA     SI, MEM
        MOV     CX, 10
        CLD
        MOV     AH, 02H
NEXT:   LODSB           ;fetch a char into AL   
        ADD     AL, '0' ; form ASCII code
        MOV     DL, AL
        INT     21H
        DEC     CX
        JNZ     NEXT    ; ZF = 0 则重叠
        HLT

LODSB 等价于
MOV AL
INC SI

LODSW 等价于
MOV AX, [SI]
INC SI
INC SI

串存储指令 - STOS/Store String

格式 :

STOS    OPRD    
STOSB
STOSW

把累加器AL中的Byte/Word中的字 存到 ES:DI指向的存储单元中, 并自动修改DI.

E.g. 把 6000H:1200H单元开始的100个字存储单元清零.

MOV     AX, 6000H
MOV     ES, AX
MOV     DI, 1200H
MOV     CX, 100
CLD                 ;
MOV     AX, 0       ;AX <- 0, 用来覆盖存储空间
REP     STOSW
HLT

3.3.5 程序控制指令

包括 转移指令, 循环控制指令, 过程调用指令 和 中断控制指令 -> 用于程序的分支转移, 循环控制及过程调用

无条件转移指令 - JMP

指令格式:

JMP     LABEL

这里, LABEL是一个标号(符号地址), 它表示转移到目的地. 指令被汇编时, 汇编程序会计算出JMP指令的下一条指令到LABEL所指示的目标地址之间的位移量(相距多少个字节单元).

指令的操作是: IP的当前值加上计算出的地址位移量形成新的IP, 并使CS保持不变, 从而使程序按新地址继续运行.

E.g.

请添加图片描述

条件转移指令

请添加图片描述

3.3.6 处理器控制指令 Processor control instruction

两类: 标志操作指令 Flag operation instruction 和 外部同步指令External synchronous instruction

标志操作指令 :

请添加图片描述

外部同步指令 :

请添加图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值