指令系统基本概念
指令
- 是指人向计算机发出的并且能够被计算机所识别的一种命令,它用来控制 (要求) 计算机做一些具体的事情。
- 这里所说的指令是面向机器的指令,它和具体的 CPU 是密切相关的,这意味着,对于不同的 CPU,它所能够识别的指令是不完全一样的。一个 CPU 所能够识别的指令是有限的。
一条指令应包含的三点信息:
一种 CPU 所能够识别的所有指令集和称为指令系统
同一系列机的指令是向前兼容的
操作码:执行何种操作
- 操作数 (地址码):指令执行的对象
- []:表示可选项,可以为空
- 第一个操作数:目标操作数
- 第二个操作数:源操作数
- 操作数表示了参加操作的数据或数据存放的地址
按照操作数的个数可将指令分为:
- 零操作数指令:操作码
- 操作对象是隐式的
- 单操作数指令:操作码 操作数
- 双操作数指令:操作码 [操作数] , [操作数]
-
指令中的操作数
立即数:
- 由指令直接给出,是常数性质
- 无地址含义,只表示运算的数据,不能作为目标操作数
- 寄存器操作数
- 表示运算的数据存放在寄存器中
- 多数情况下,寄存器操作数指通用寄存器
- 在三类操作数中所需运行时间最短
- 存储器操作数
- 表示运算的数据存放在内存
- 指令中 [ ] 里是数据所在单元的偏移地址
- 在三类操作数中所需运行时间最长。
8088/8086 是 16 位 CPU,指令操作数可以是 8 位或 16 位
【例】存储器操作数
- 存储器操作数传输单元个数取决于另一个操作数的大小
- 图中第一条指令的目标操作数是 AL,是一个 8 位寄存器,所以传输一个内存地址,将内存单元 1200H 中数据 22H 存入 8 位寄存器 AL 中
图中第二条指令的目标操作数是 AX,是一个 16 位寄存器,所以传输两个内存,单元将内存单元 1200H 中数据 22H 存入 16 位寄存器的低 8 位,将内存单元 1201H 中数据 11H 存入 16 位寄存器的高 8 位
寻址方式
寻址方式:寻找操作数所在地址的方法
源操作数OPS
- 运算数据的来源
- 目标操作数OPD
- 运算结果的去向
- 另一个运算数据
操作数可能的来源或运算结果可能的去处:
- 由指令直接给出
- 寄存器
- 内存单元
寻找操作数所在地址的方法可以有四种大类型 :
- 指令直接给出的方式
- 运算对象由指令直接给出
- 存放于寄存器中的寻址方式
- 参加运算的数据存放在 CPU 的某个通用寄存器中
- 存放于存储器中的寻址方式
- 指令操作的对象在内存中,表现形式为:[ ]
- 指令中给出运算对象在内存某个逻辑段中的偏移地址:[偏移地址]
- 逻辑段的段基地址通过默认或重设方式给出。
- 存储器操作数的字长本身不确定,其字长取决于指令中另一个寄存器操作数,或通过其他方式指定字长
-
立即寻址
寄存器寻址
直接寻址
寄存器间接寻址
寄存器相对寻址
相对寻址主要用于一维数组的操作
常将位移量作为 “表头” 地址,间址寄存器的值作为表内相对地址
基址、变址寻址
基址、变址、相对寻址
隐含寻址
小结
例题
数据传送指令
通用数据传送指令
一般数据传送指令 MOV⭐
格式:MOV dest, src
- 操作:src → dest
- 注意点:
- 两操作数字长必须相同;
- 两操作数不允许同时为存储器操作数;
- 两操作数不允许同时为段寄存器;
- 在源操作数是立即数时,目标操作数不能是段寄存器;
- IP 和 CS 不作为目标操作数,FLAGS 一般也不作为操作数在指令中出现。
堆栈操作指令⭐
堆栈操作的原则
- 先进后出
-
压栈指令 PUSH
格式:PUSH OPRD
- 说明:
- OPRD:16 位寄存器或存储器的两个单元
- 执行过程:
- SP - 2 → SP:栈顶指针 SP 向上移动,腾出两个字节的空间
- 操作数高字节 → SP+1:操作数高字节数据存入栈顶指针 SP 指向的下一个单元
- 操作数低字节 → SP:操作数低字节数据存入栈顶指针 SP 指向的单元
- PUSHFD 指令把 FLAGS 寄存器内容压入栈顶单元
PUSHAD 指令按照 EAX、ECX、EDX、EBX、ESP(执行 PUSHAD 之前的值)、EBP、ESI 和 EDI 的顺序,将所有 32 位通用寄存器压入堆栈
出栈指令 POP
格式: POP OPRD
- 执行过程:
- 操作数低字节 → SP:弹出 SP 指向的单元内容
- 操作数高字节 → SP + 1:栈顶指针向下移动一个单元,弹出 SP 指向的单元内容
- SP + 2 → SP:栈顶指针向下移动一个单元,指向当前栈顶
注意
- 堆栈中栈底是高地址,栈顶是低地址
- 指令的操作数必须是 16 位;
- 操作数可以是寄存器或存储器,但不能是立即数;
- 不能从栈顶弹出一个字给 CS;
- PUSH 和 POP 指令在程序中一般成对出现;
- PUSH 指令的操作方向是从高地址向低地址,而 POP 指令的操作正好相反。
- POPFD 指令则把栈顶单元内容弹出到 FLAGS 寄存器
-
交换指令 XCHG⭐
格式:
- XCHG REG,MEM/REG
- 注:
- 两操作数必须有一个是寄存器操作数
- 不允许使用段寄存器。
例:
格式:
- XLAT
- 说明:
- 用 BX 的内容代表表格首地址,AL 内容为表内位移量,BX+AL 得 到要查找元素的偏移地址
操作:
将符号数的符号位扩展到高位;
- 指令为零操作数指令,采用隐含寻址,隐含的操作数为 AX 及 AX,DX
- 无符号数的扩展规则为在高位补 0
字节到字的扩展指令
- 格式:
- CBW
- 操作:
- 将 AL 内容扩展到 AX
- 规则:
- 若最高位 = 1,则执行后 AH=FFH
- 若最高位 = 0,则执行后 AH=00H
字到双字的扩展指令
- 格式:
- CWD
- 操作:
- 将 AX 内容扩展到 DX AX
规则:
I/O 端口:I/O 接口中用于存储数据、可以直接被 CPU 访问的寄存器
计算机输入输出系统中可以包含若干接口控制电路 (芯片),每个接口中都包含了 1 个或多个端口
指令寻址方式
根据端口地址码的长度,指令具有两种不同的端口地址表现形式。
- 直接寻址
- 端口地址为 8 位时,指令中直接给出 8 位端口地址;
- 寻址 256 个端口。
- 间接寻址
- 端口地址为 16 位时,指令中的端口地址必须由 DX 指定;
- 可寻址 64K 个端口。
地址传送指令 LEA
- 操作:
- 将变量的偏移地址写入到目标寄存器
- 当程序中用符号表示内存偏移地址时,须使用该指令。
- 格式:
- LEA REG,MEM
指令要求:
隐含操作数 AH
- LAHF(Load AH from Flags)
- 指令格式:
- LAHF
- 操作:将 FLAGS 的低 8 位装入 AH
- 指令格式:
- SAHF(Store AH into Flags)
- LAHF(Load AH from Flags)
隐含操作数 FLAGS
格式:
- ADD OPRD1,OPRD2
- 操作:
- OPRD1 + OPRD2 → OPRD1
【例】ADD 指令的执行,对全部 6 个状态标志位都产生影响:
带进位位的加法指令 ADC
- 指令格式、对操作数的要求、对标志位的影响与 ADD 指令完全一样
- 指令的操作:
- OPRD1 + OPRD2 + CF → OPRD1
-
加 1 指令 INC
格式:
- INC OPRD
- 操作:
- OPRD+1 → OPRD
- 说明:OPRD 不能是段寄存器,不能是立即数
-
减法运算指令
普通减法指令 SUB
格式:
- SUB OPRD1,OPRD2
- 操作:
- OPRD1 - OPRD2 → OPRD1
-
考虑借位的减法指令 SBB
指令格式、对操作数的要求、对标志位的影响与 SUB 指令完全一样
指令的操作:
格式:
- DEC OPRD
- 操作:
- OPRD - 1 → OPRD
指令对操作数的要求与 INC 相同,指令常用于在程序中修改计数值
比较指令 CMP
格式:
- CMP OPRD1,OPRD2
- 操作:
- OPRD1- OPRD2
- 指令执行的结果不影响目标操作数,仅影响标志位
- 用途:
- 用于比较两个数的大小,可作为条件转移指令转移的条件
- 指令对操作数的要求及对标志位的影响与 SUB 指令相同
- 两个无符号数的比较:
- CMP AX,BX
- 若 AX ≥ BX → CF=0
- 若 AX < BX → CF=1
- 若 AX = BX → CF=0,ZF=1
- 两个带符号数的比较:
- CMP AX,BX
- 两个数的大小由 OF 和 SF 共同决定
- OF 和 SF 状态相同 AX ≥ BX
- OF 和 SF 状态不同 AX < BX
详细分析:
- OF=1,SF=1,AX - BX 有溢出,AX - BX < 0 不成立,AX - BX ≥ 0,AX ≥ BX
- OF=0,SF=0,AX - BX 无溢出,AX - BX ≥ 0 成立,AX ≥ BX
- OF=1,SF=0,AX - BX 有溢出,AX - BX ≥ 0 不成立,AX - BX < 0,AX < BX
OF=0,SF=1,AX - BX 无溢出,AX - BX < 0 成立,AX < BX
求补指令 NEG
格式:
- NEG OPRD
- 操作:
- 0 - OPRD → OPRD
说明:
格式:
- MUL OPRD
- 说明:OPRD 不能是立即数
操作:
格式:
- IMUL OPRD
- 指令格式及对操作数的要求与 MUL 指令相同。
指令执行原理:
格式:
格式:
- IDIV OPRD
注意:除法指令要求被除数是除数的双倍字长
若 OPRD 是字节数
- 执行:AX / OPRD
- 结果:
- AL = 商;AH = 余数
若 OPRD 是双字节数
- 执行: DXAX / OPRD
- 结果:
- AX = 商;DX = 余数
小结
- 算术运算类指令的执行会影响状态标志位。其中:
- INC 和 DEC 指令的执行不会影响 CF
- 算术运算类指令中所有单操作数指令都要求操作数:
- 不能是立即数
- 如果是存储器操作数,需要声明操作数的字长 (用 PTR 运算符)
- 乘法运算中:乘积是乘数的双倍字长
-
逻辑运算指令⭐
对操作数的要求:
- 大多与 MOV 指令相同。
- “非” 运算指令要求操作数不能是立即数;
对标志位的影响
格式:
- AND OPRD1,OPRD2
- 操作:
- 两操作数相 “与”,结果送目标地址。
应用:
格式:
- OR OPRD1,OPRD2
- 操作:
- 两操作数相 “或”,结果送目标地址
应用:
格式:
- NOT OPRD
- 操作:
- 操作数按位取反再送回原地址
- 注:
- 指令的执行对标志位无影响
例:
格式:
- XOR OPRD1,OPRD2
- 操作:
- 两操作数相 “异或”,结果送目标地址
例:
格式:
- TEST OPRD1,OPRD2
- 操作:
- 执行 “与” 运算,但运算的结果不送回目标地址
应用:
- 常用于测试某些位的状态
移位操作指令
定点数移位运算:传送门
移位操作指令:控制二进制位向左或向右移动的指令,可分为两类:
- 常用于测试某些位的状态
非循环移位指令
- 循环移位指令
指令格式在形式上为双操作数,本质上为单操作数;
指令的目标操作数为被移动对象,源操作数为移动次数
- 当目标为存储器操作数时,需要说明其字长
- 源操作数只能是 1 或 CL
移动移动 1 位时由指令直接给出;移动两位及以上时,移位次数必须由 CL 指定。
循环移位指令的应用:
- 用于对某些位状态的测试;
- 高位部分和低位部分的交换;
-
非循环算术左移指令 SAL
用于有符号数
SAL OPRD , 1
-
非循环逻辑左移指令 SHL
用于无符号数
SHL OPRD,1
-
非循环算术右移指令 SAR
用于有符号数
SAR OPRD,1
-
非循环逻辑右移指令 SHR
用于无符号数
SHR OPRD,1
-
不带进位位的循环移位 ROL、ROR
带进位位的循环移位 RCL、RCR
串操作指令⭐
串操作指令说明:
针对数据块或字符串的操作
- 可实现存储器到存储器的数据传送;
- 待操作的数据串称为源串,目标地址称为目标串
- 串操作指令的操作对象是多个字节数 (一串字符或数据), 因此,指令的执行需要确定:
- 串所在的区域
- 串的首地址 (原串、目标串起始地址)
- 串长度 (大小)
- 串的操作方向
串操作指令的要求:
- 串所在区域及首地址:
- 源串一般存放在数据段,偏移地址由 SI 指定。允许段重设
- 目标串必须在附加段,偏移地址由 DI 指定
- 串长度:
- 串长度值由 CX 指定
- 通过增加重复前缀,可以实现对 CX 值的自动修改
- 串的操作方向:
- 由 DF 标志位决定。指令根据 DF 状态自动修改地址指针
- DF=0 → 增地址方向
- DF=1 → 减地址方向
- 由 DF 标志位决定。指令根据 DF 状态自动修改地址指针
- 若按增地址方向操作,串操作结束时:
- 串传送指令:指针将指向串尾 + 1
- 串比较类指令:指针将指向结束位 + 1
- 若按减地址方向操作,串操作结束时:
- 串传送指令:指针将指向串尾 - 1
- 串比较类指令:指针将指向结束位 - 1
重复前缀:
- 无条件重复
- REP
- 若 CX≠0,则 REP 后的指令将继续重复执行
- 常用于传送类指令前 → 未传送完则继续传送
- REP
条件重复
功能:
- 将源数据串传送到目标地址
- 格式:
- MOVS OPRD1,OPRD2 → 此格式仅用于源操作数需段重设的情况下
- MOVSB → 按字节传送
- MOVSW → 按字传送
- 串传送指令常与无条件重复前缀连用
串比较指令 CMPS
- 功能:
- 用于实现两个数据串的比较
- 操作:
- 目标串 - 源串,结果不写回目标地址
- 常与条件重复前缀连用
- 格式:
- CMPS OPRD1,OPRD2
- CMPSB
- CMPSW
- 前缀的操作对标志位不影响
串扫描指令 SCAS
常用于在指定存储区域中寻找某个关键字
- 格式:
- SCAS OPRD
- SCASB
- SCASW
- 执行与 CMPS 指令相似的操作,区别是:
- 这里的源操作数是 AX 或 AL
【例】串扫描指令应用
在 ES 段中从 2000H 单元开始存放了 10 个字符,寻找其中有无字符 “A”。若有则记下搜索次数,将搜索次数写入到 DATA1 单元,并将存放“A” 的地址写入 DATA2 单元。
串装入指令 LODS
- 格式:
- LODS OPRD
- LODSB
- LODSW
- 操作:
- 对字节: AL ← [DS:SI]
- 对字:AX ← [DS:SI]
应用
格式:
- STOS OPRD
- STOSB
- STOSW
- 操作:
- 对字节:AL → [ES:DI]
- 对字: AX → [ES:DI]
- 应用:
- 常用于将内存某个区域置同样的值
- 此时:
- 将待送存的数据放入 AL(字节数) 或 AX(字数据);
- 确定操作方向 (增地址 / 减地址) 和区域大小(串长度值);
- 使用串存储指令 + 无条件重复前缀,实现数据传送。
串操作指令应用注意事项
- 需要定义附加段
- 目标操作数必须在附加段
- 需要设置数据的操作方向
- 确定 DF 的状态
- 源串和目标串指针分别为 SI 和 DI
- 串长度值必须由 CX 给出
注意重复前缀的使用方法
程序控制类指令以 “隐含” 的方式修改 CS 和 IP ,以实现控制程序走向的目的(Intel 指令集不允许由指令直接修改 CS 和 IP)
- 通过修改 IP 或 CS 和 IP,实现程序的三种基本控制结构
- 顺序,选择 (分支),循环
学习程序控制类指令需要重点关注:
无条件转移指令 → 无条件转移到目标地址
条件转移指令 → 当具备一定条件 (通常指状态标志位) 时转移到目标地址
无条件转移指令
段内转移
下一条要执行指令的偏移地址 = 当前 IP + 位移量
- 段内直接转移
- 转移的目标地址由指令直接给出
- 格式:
- JMP Label
- 段内间接转移
- 转移的目标地址存放在某个 16 位寄存器或存储器的某两个单元中
- 例:
- MOV BX,1200H
- JMP BX
- 执行完上述指令后:
- IP=1200H
段间转移
- 段间直接转移
- 转移的目标地址由指令直接给出
- 格式:
- JMP FAR Label
- 段间间接寻址
- 转移的目标地址由指令中的 32 位操作数给出
- 32 位目标地址须存放于内存中
- 例:
- JMP DWORD PTR[BX]
条件转移指令 JC 等
- 在满足一定条件下,程序转移到目标地址继续执行
- 条件转移指令均为段内短转移,即转移范围为:
- -128 —— +127
- 基于 1 个标志位状态实现转移的指令
- JC/JNC
- 判断 CF 的状态。常用于两个无符号数大小比较
- JZ/JNZ
- 判断 ZF 的状态。常用于循环体的结束判断
- JO/JNO
- 判断 OF 的状态。常用于有符号数溢出的判断
- JP/JPE
- 判断 PF 的状态。用于判断运算结果低 8 位中 1 的个数是否为偶数
- JS /JNS
- 判断 SF 的状态。常用于判断数的性质
- JC/JNC
- 基于 2 个或 3 个标志位状态实现转移的指令:
- JA/JAE/JB/JBE
- 判断 CF 或 CF+ZF 的状态。常用于无符号数大小的比较
- JG/JGE/JL/JLE
- 判断 SF+OF 或 SF+OF+ZF 的状态。常用于有符号数大小的比较
- JA/JAE/JB/JBE
- 基于 CX 内容转移的指令
- JCXZ
- 可根据指令执行后 CX 的结果实现转移
- JCXZ
循环控制
- 循环范围:
- 以当前 IP 为中心的 - 128~+127 范围内循环
- 循环次数由 CX 寄存器指定
循环指令:
格式:
- LOOP LABEL
- 循环条件:
- CX ≠ 0
操作:
功能:
- 先使 CX-1,再根据 CX 中的值及 ZF 值来决定是否继续循环
- 格式:
- LOOPZ Label → 继续循环的条件:CX≠0,且 ZF=1
- LOOPNZ Label → 继续循环的条件:CX≠0,且 ZF=0
过程调用指令 CALL
- 过程调用指令
- 用于调用一个子过程
- 与转移指令的比较
- 子过程执行结束后要返回原调用处
调用指令的执行过程
- 保护断点
- 将调用指令的下一条指令的地址(断点)压入堆栈
- 获取子过程的入口地址
- 子过程第 1 条指令的偏移地址
- 执行子过程
- 功能实现,参数的保存及恢复
恢复断点,返回原程序。
子过程与原调用程序不在同一代码段 , 调用前需保护断点的段基地址和偏移地址
- 先将断点的 CS 压栈,再压入 IP
返回指令 RET
- 功能:
- 从堆栈中弹出断点地址,返回原程序
- 格式:
- RET
-
中断控制
中断的概念
- 某种异常或随机事件使处理器暂时停止正在运行的程序,转去执行一段特殊处理程序,并在处理结束后返回原程序被中断处继续执行的过程。
- 中断指令:
- 引起 CPU 产生一次中断的指令
中断与过程调用
将 FLAGS 压入堆栈;
- 将 INT 指令的下一条指令的 CS、IP 压栈;
- 由 n×4 得到存放中断向量的地址;
- 将中断向量 (中断服务程序入口地址) 送 CS 和 IP 寄存器;
- 转入中断服务程序。