8088/8086 指令系统
以8088/8086 CPU 指令系统为基础, 介绍指令的一般概念和执行过程, CISC 和 RISC 指令的概念, 寻址方式 以及 不同类型指令的功能.
Goal:
- 了解 指令的的一般概念, 指令的基本格式以及指令的执行过程.
- 熟悉 指令对操作数的各种寻址方式
- 深入理解 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个.
相应的, 指令由三种格式:
- 零操作数指令 : 操作数是隐含存在的. 这类指令操作的对象通常为处理器本身.
- 单操作数指令.
- 双操作数指令
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系统中, 分为两种 :
- 寻找操作数的地址.
- 寻找要执行的下一条指令的地址, 即程序的地址.
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小类 :
- 通用数据传送指令 - universal data transfer
- 目标地址传送指令 - address transfer
- 标志传送指令 - flag operation
- 输入输出指令 - input/output
1 - 通用数据传送指令 - universal data transfer
包括 :
- 一般传送指令 M O V MOV MOV
- 堆栈操作指令 P U S H , P O P PUSH, POP PUSH,POP
- 交换指令 X C H G XCHG XCHG
- 查表转换指令 X L A T XLAT XLAT
- 字位扩展指令
一般传送指令 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指令的要求:
- M O V MOV MOV指令中两个操作数字长必须一致.
- 两个操作数不能同时为存储器操作数.
- 不能用立即数直接给段寄存器幅值.
- 两个操作数不能同时为段寄存器
- 一般情况下, 指令指针IP 及 代码段寄存器CS的内容不通过MOV指令修改 ->不作 d e s t dest dest, 可作 s r c src src.
- 通常情况下, 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中, 可以将堆栈看作是一个小存储器, 遵循以下的原则:
- 堆栈的存取每次必须是一个字(16bits), 即堆栈指令中的操作数必须是16位, 而且只能是寄存器或存储器操作数.
- 遵循"LIFO(last in first out)"原则. 向堆栈段中存放数据时, 总是从高地址向低地址方向增长; 从堆栈段中国取出数据时, 相反.
- 堆栈段在内存中的位置由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不合法).
- 压栈指令 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] ;从栈顶弹出一个字到数据段两个连续存储单元中
- 出栈指令 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)
将源地址与目标地址中的内容进行互换.
要求 :
- src 和 dest 可用使寄存器 或存储器, 但不能同时是存储器.
- 不能为段寄存器操作数 -> 段寄存器的内容不可以参加交换.
- 两个操作数字长必须相同.
例如,
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端口时, 允许使用两种寻址方式 :
- 直接寻址 : 指令中的IO端口地址为8bits, 此时允许寻址256个端口(0~FFH).
- 寄存器间接寻址 : 端口地址为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的端口
- 取偏移地址指令
指令格式:
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 - 加法运算指令
- 普通加法指令 A D D ADD ADD
- 带进位位的加法指令 A D C ADC ADC
- 加一指令 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条指令 :
- 不考虑借位的普通减法指令 S U B SUB SUB
- 考虑借位的减法指令 S B B SBB SBB
- 减 1 指令 D E C DEC DEC
- 求补指令 N E G NEG NEG
- 比较指令 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的补码)的求补.
注意:
-
执行 N E G NEG NEG 指令后, 一般情况下都会使CF为1.
-
当指定的操作数的值为 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之后 可以 根据标志位的状态判断两个操作数的大小关系, 判据如下:
-
相等关系 <=> ZF = 1
-
大小关系 - unsigned <=>
CF = 0 -> 被减数OPRD2 > 减数OPRD1
-> OPRD1 - OPRD2 不需要借位
CF = 1 -> OPRD1 < OPRD2 -
大小关系 - 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 OF⨁SF=0 -> OPRD1 < OPRD2
- O F ⨁ S F = 1 OF \bigoplus SF = 1 OF⨁SF=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 AX←OPRD×AL
- 字乘法 D X : A L ← O P R D × A X DX:AL \leftarrow OPRD \times AX DX:AL←OPRD×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 AL←AX % OPRD
- 字除法 A X ← D X : A X % O P R D AX \leftarrow DX:AX ~ \% ~ OPRD AX←DX: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数据串. 串操作指令 - 用来对串中每个字符/数据做同样操作的指令
串指令 可以处理字串, 字节串, 并在每完成对一个字/字节的操作后自动修改指针继续向下执行.
所有串操作都具有以下共同点 :
-
源串(src) : 默认在DS中, 允许段重设. 偏移地址用 SI 指定 -> 源串指针为 DS : SI.
-
目标串(dest) : 默认在ES中, 不允许段重设. 偏移地址用 SI 指定 -> ES : SI.
-
串长度值放在 CX寄存器中.
-
串操作指令本身可实现 地址指针的自动修改. 在对每个字节(or字)操作后, SI 和 DI 寄存器的内容会自动修改, 修改方向与标志位 DF 有关.
DF = 0 -> SI 和 DI 按地址增量方向修改; 反之为按减量方向修改.
-
可重复使用前缀. -> 每次串操作之后, CX–.
2 - 重复操作前缀
用重复操作前缀 可以重复执行指令直到 计数器CX自减归零.
分为两类 : 无条件重复前缀, 有条件重复前缀.
- REP : 无条件重复 -> 重复执行操作,直到CX自减归零.
- REPE/REPZ : 相等/结果为零时重复, ZF = 1 且CX != 0 时重复.
- REPNE/REPNZ : 不相等/结果不为零时重复, ZF = 1 且CX != 0 时重复.
那么加了重复操作前缀之后程序执行起来变什么样了呢 ?
- 执行规定的操作.
- SI, DI自动增量(或减量)
- CX–
- 根据ZF的状态自动决定是否重复执行.
3 - 串操作指令 MOVS, CMPS, SCAS, LODS, STOS
- 传传送指令 - 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
- 串比较指令 - CMPS/Compare String
格式(3种) :
CMPS OPRD1, OPRD2
CMPSB
CMPSW
类似于CMP指令, CMPS进行的时两个数据串的比较. 将src串与dest串按byte/word逐位比较, 比较结果反映在标志位上.
通常 + 条件重复前缀 REPE(REPZ) 或 REPNE(REPNZ) 连用 -> 检查两个字符串是否相等.
在加条件重复前缀的情况下, 结束串比较指令的执行时有两种可能 :
- 因CX=0停止.
- 因不满足 条件重复前缀 条件而停止.
对于 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
标志操作指令 :
外部同步指令 :