简介

  • 指令: 描述 处理器(CPU) 的特定行为,是处理器执行的最小 功能 单位(非最小 执行 单位)
  • 指令集: 处理器所能执行的全部指令的集合
  • 指令集架构(ISA, Instruction-Set Architecture): 软件与硬件之间定义的一套 接口规范 ,隐藏了硬件中的电路元件的状态(内部实现是任意的),对外描述了硬件的一系列行为(从这个角度看,指令集架构是对硬件行为的抽象,仅为处理器的实现规定一个目标),是程序员使用软件编程操作硬件的唯一途径

指令系统所表示的内容相对广泛,与指令集,指令集架构的概念基本重合; 个人理解: 称为"系统",展现指令集之间协同作用使计算机运转的效果

前置

  • 若未特殊说明,则下面列举的具体例子为 8086 芯片的指令集,使用 Intel 风格的汇编语言(不同风格的汇编语言差异可能会很大,要注意)
  • 在 Intel 汇编语言中,8位的数据称为 字节(byte) ,16位的数据称为 字(word) ,32位的数据称为 双字(double words) ,64位的数据称为 四字(quad words)

指令格式

  • 指令的实质是一串二进制编码,又称机器指令
  • 使用助记符表示各个片段,使得可读性相对较好的指令,称为汇编指令

字段划分

  • 指令一般划分为两部分: 操作码/操作符 + 地址码/操作数
    • 操作码: 表示机器指令的特定操作,操作码的位数不一定是固定的
    • 地址码: 个数不固定(可以没有,一个或多个); 在不同操作符中,含义有所不同,可以表示一个立即数,寄存器,内存地址,IO设备地址等等

例子(mov 指令为例)

8086指令划分

8086指令字段划分

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

mov 指令

mov 指令

1
2
3
1: c7 c0 58 00 -> mov ax, 0x58
2: 8b d8 -> mov bx ax
3: 8b c1 -> mov ax cx
  1. 指令1对应的是图中的第二行指令,OPCODE = 110001,表示把立即数传入寄存器ax中
  2. 指令2对应的是图中的第一行指令,OPCODE = 100010,表示把寄存器ax的值传入寄存器bx中(reg段为011,表示bx; r/m段为000,表示ax)
  3. 指令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 -> 执行指令 -> 取出下一条指令…)
  • 跳跃寻址: 执行跳转指令时,处理器跳转到指定的另一个地址执行,受状态寄存器和操作数的影响(从操作数中得到指令地址,填充到计数器中)

数据寻址(确定指令的操作数地址)

计算机支持多种数据寻址方式

总感觉有些翻译怪怪的。看了原英文后个人感觉更好理解一点

记立即数符号为 ImmImm, 寄存器为 rxr_{x}

立即寻址(Immediate Addressing)

  • 指令的操作数不是地址,直接就是数值(Immediate)

mov ax, 0x580x58 为立即数

寄存器寻址(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) EA=rb+ImmEA = r_{b} + Imm

mov ax, [bx + 4] 中的 [bx + 4], bx 为基址寄存器

变址寻址(Indexed Addressing)

  • 数据存放在内存中,操作数包含基址寄存器,变址寄存器(,加上立即数偏移量)
  • 变址寄存器(Index Register)(为什么要翻译成这样),存储的是一个可变索引
  • 有效地址(Effective Address) EA=rb+ri+ImmEA = r_{b} + r_{i} + Imm

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) EA=rb+ris+ImmEA = r_{b} + r_{i} * s + Imm
  • 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版》王爽
  • 《深入理解计算机系统》