指令系统
简介
- 指令: 描述 处理器(CPU) 的特定行为,是处理器执行的最小 功能 单位(非最小 执行 单位)
- 指令集: 处理器所能执行的全部指令的集合
- 指令集架构(ISA, Instruction-Set Architecture): 软件与硬件之间定义的一套 接口规范 ,隐藏了硬件中的电路元件的状态(内部实现是任意的),对外描述了硬件的一系列行为(从这个角度看,指令集架构是对硬件行为的抽象,仅为处理器的实现规定一个目标),是程序员使用软件编程操作硬件的唯一途径
指令系统所表示的内容相对广泛,与指令集,指令集架构的概念基本重合; 个人理解: 称为"系统",展现指令集之间协同作用使计算机运转的效果
前置
- 若未特殊说明,则下面列举的具体例子为 8086 芯片的指令集,使用 Intel 风格的汇编语言(不同风格的汇编语言差异可能会很大,要注意)
- 在 Intel 汇编语言中,8位的数据称为 字节(byte) ,16位的数据称为 字(word) ,32位的数据称为 双字(double words) ,64位的数据称为 四字(quad words)
指令格式
- 指令的实质是一串二进制编码,又称机器指令
- 使用助记符表示各个片段,使得可读性相对较好的指令,称为汇编指令
字段划分
- 指令一般划分为两部分: 操作码/操作符 + 地址码/操作数
- 操作码: 表示机器指令的特定操作,操作码的位数不一定是固定的
- 地址码: 个数不固定(可以没有,一个或多个); 在不同操作符中,含义有所不同,可以表示一个立即数,寄存器,内存地址,IO设备地址等等
例子(mov 指令为例)
8086指令划分

- 8086 的总线位数为16位,一次可以读取2个字节的指令
- 8086 的大多数指令遵循图中展示的字段划分,其中 OPCODE 部分为操作码,6位,其余位作用参考图中标注
mov 指令

1 | 1: c7 c0 58 00 -> mov ax, 0x58 |
- 指令1对应的是图中的第二行指令,
OPCODE = 110001,表示把立即数传入寄存器ax中 - 指令2对应的是图中的第一行指令,
OPCODE = 100010,表示把寄存器ax的值传入寄存器bx中(reg段为011,表示bx;r/m段为000,表示ax) - 指令3对应的是图中的第一行指令,
OPCODE = 100010,表示把寄存器cx的值传入寄存器ax中(reg段为000,表示ax;r/m段为001,表示cx)
注: 上述的操作指令 op a b,呈现出的结果是 a = a op b,这只是对应 intel 汇编指令的实现结果,在其它风格/架构的汇编指令中,可能存在: op a b -> b = a op b
寻址
指令寻址(如何找到下一条需要执行的指令的地址)
- 顺序寻址: 使用程序计数器指示当前指令的地址(取出指令 -> 计数器 + 1 -> 执行指令 -> 取出下一条指令…)
- 跳跃寻址: 执行跳转指令时,处理器跳转到指定的另一个地址执行,受状态寄存器和操作数的影响(从操作数中得到指令地址,填充到计数器中)
数据寻址(确定指令的操作数地址)
计算机支持多种数据寻址方式
总感觉有些翻译怪怪的。看了原英文后个人感觉更好理解一点
记立即数符号为 , 寄存器为
立即寻址(Immediate Addressing)
- 指令的操作数不是地址,直接就是数值(Immediate)
mov ax, 0x58中0x58为立即数
寄存器寻址(Register Addressing)
- 数据存放在寄存器中,操作数为寄存器标号(Register)
mov ax, bx中的ax, bx
直接寻址(Absolute Addressing)
- 数据存放在内存中,操作数为内存地址
mov [1000h], 0x12中的[1000h]
间接寻址(Indirect Addressing)
- 数据存放在内存中,操作数通常为寄存器标号(内存地址的话我目前没见过,感觉比较少见)
mov ax, [bx]中的[bx]
基址寻址(Base Addressing)
- 数据存放在内存中,操作数为基址寄存器标号(,加上立即数偏移量)
- 一般来说,基址寄存器(Base Register)指向一个内存段的起始地址,与立即数偏移量相加形成有效地址
- 有效地址(Effective Address)
mov ax, [bx + 4]中的[bx + 4], bx 为基址寄存器
变址寻址(Indexed Addressing)
- 数据存放在内存中,操作数包含基址寄存器,变址寄存器(,加上立即数偏移量)
- 变址寄存器(Index Register)(
为什么要翻译成这样),存储的是一个可变索引 - 有效地址(Effective Address)
mov ax, [bx + si + 4]中的[bx + si + 4], si(source index) 为变址寄存器
联想场景:
1
2
3
4
5
6
7
8
9 struct S {
char x;
short y;
int z;
};
struct S s;
s.x = 'a';
s.y = 20;
s.z = 10;其中,基址寄存器 bx 指向
s的起始地址,假设为 0x1000(16位); 那么 si 的值可以是 0x0000(x), 0x0002(y), 0x0004(z)(考虑内存对齐)(转成汇编的话应该都变成立即数偏移了,这个只是假想场景)更常见应该是在遍历数组上,bx 指向数组起始位置,si 表示当前的索引
比例变址寻址(Scaled Indexed Addressing)
取自《深入理解计算机系统》
- 本质为变址寻址,多了一个比例因子 s(可为1,2,4,8)
- 有效地址(Effective Address)
- 8086 没有比例变址寻址
- 在遍历数组上,s 标记为数组元素的大小,变址寄存器变化时直接+1/-1就行了,相对比较方便
功能分类
- 这里简单列下常见的指令,抽象层面的,不是具体某个指令集的指令,如果是想要了解具体指令的话,需要去查找相关架构的说明书
- 指令按照功能,大致可以分为以下几类:
数据传送
- mov 指令(数据赋值,移动)
- push/pop 指令(堆栈操作)
- xchg 指令(数据交换)
- lea 指令(加载有效地址到目标寄存器中,
lea ax [bx + 4]-> 把地址值bx + 4赋给ax) - in/out 指令(I/O设备交互用的)
算术逻辑运算
- add/sub 指令(加减法)
- mul/imul(无符号/有符号数乘法)
- div/idiv(无符号/有符号数除法)
- and
- or
- xor
- not
- shl/shr(逻辑左/右移)
- sal/sar(算术左/右移, sal == shl)
- rol/ror(循环左/右移)
- rcl/rcr(带进位循环左/右移)
- cmp(比较,本质为sub指令,只改变状态标志位/条件码,不改变寄存器值)
- test(本质为and指令,只改变状态标志位)
控制转移
- jmp(无条件跳转)
- je, jne, jz…(有条件跳转)
- int(中断跳转)
其它(管理CPU本身状态指令等)
- nop(无操作)
- hlt(停机)
指令集架构分类
复杂指令集(Complex Instruction Set Computer, CISC)
- 设计策略: 使用大量指令,复杂指令
- 特点:
- 指令长度不固定,寻址方式丰富
- 编译器开发相对容易(高级程序语言容易映射为机器指令)
- 依赖硬件,处理器硬件电路复杂,性能受硬件复杂度约束
- 代表:
- Intel x86 架构
- AMD x86 架构
精简指令集(Reduced Instruction Set Computer, RISC)
- 设计策略: 简化指令集,复杂指令由软件完成
- 特点:
- 指令长度固定(适配流水线化), 寻址方式简单
- 编译器开发需要使用多条简化指令表示一个复杂操作,相对复杂(将硬件复杂度转移给了软件)
- 处理器硬件电路简单,性能高,功耗低,使用指令流水线运行提高速度
- 代表:
- ARM 架构
- RISC-V 架构
其它
- 通篇看下来比较乱,因为这里稍微结合了实际用例来讲,但只简单讲了与指令相关的部分,有许多前置知识没有说明,例如 Intel 汇编语言,8086 寄存器分布,指令各个分块代表的含义等等,每一块都展开的话讲不完,我也不全懂,大致意思到了就行
- 其实还有很多方面没展开,例如:
- 指令格式按操作数划分(无操作数,单操作数,双操作数…)(我认为特意强调这个划分没啥必要,当查看说明书参考实际的使用时,会告诉你几个操作数,作用是什么之类的,除非是要自己设计一套指令集,或搞什么 操作码扩展 之类的东西)
- 寻址方式怎么切换? 这个其实蕴含在指令的某些位中,看具体架构
- 不同架构汇编语言差异? 自己查相关资料
参考资料/书籍
- 《The 8086 Family Users Manual》
- 《汇编语言第3版》王爽
- 《深入理解计算机系统》
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Forgotten Area!


