指令是计算机执行某种操作的命令,是计算机运行的最小功能单位。一台机器能够识别并执行的全部机器指令构成它的指令系统,也称为指令集ISA。

指令系统处在软件和硬件之间:高级语言程序最终必须被翻译成机器指令;硬件则按指令中的字段完成取数、运算、访存、转移和控制等动作。不同体系结构的机器指令格式和指令含义不同,因此一台机器通常只能直接执行自己指令系统中的指令。

指令格式

一条机器指令是一组有意义的二进制代码,通常由操作码字段地址码字段组成。

指令字段

  • 操作码(Operation Code, OP):指出指令要做什么操作,例如加法、传送、转移、停机。
  • 地址码:指出操作对象在哪里,可能表示操作数地址、结果地址、寄存器编号、立即数或下一条指令地址。

地址码的数量并不固定。根据显式地址码个数,指令可以分为零地址、一地址、二地址、三地址和四地址指令。

按地址码数分类

类型 常见格式 典型含义 执行阶段访存次数
零地址指令 OP 不需要操作数;或在堆栈机中隐含使用栈顶、次栈顶 通常为 0
一地址指令 OP A1 OP(A1) -> A1,或 (ACC) OP (A1) -> ACC 常见为 1-2
二地址指令 OP A1 A2 (A1) OP (A2) -> A1 常见为 3
三地址指令 OP A1 A2 A3 (A1) OP (A2) -> A3 常见为 3
四地址指令 OP A1 A2 A3 A4 (A1) OP (A2) -> A3,且 A4 给出下址 常见为 3
Note

上表的访存次数只统计执行阶段访问操作数和写回结果的次数,不统计取指本身。若某个操作数在寄存器中,或结果写入寄存器,访存次数会减少。

固定指令字长下,地址码数量越多,单个地址码能分到的位数越少,直接表示的地址范围就越小。

指令字长

指令字长是一条机器指令包含的二进制位数,也就是 OP 字段、地址码字段以及其他辅助字段合起来的总位数。

例如某条指令格式为:

1
OP(6) + A1(10)

则这类指令的指令字长为:

$$
6+10=16\text{ 位}
$$

  • 定长指令字结构:所有指令长度相同,取指和译码较简单。
  • 变长指令字结构:不同指令长度可以不同,编码更灵活,但取指和译码更复杂。

如果指令字长等于存储字长,则一次访存通常能取出一条指令;如果指令字长大于存储字长,则一条指令可能需要多次访存才能取完。

指令字长、机器字长、存储字长不是一个概念
  • 指令字长:一条机器指令有多少位,回答“这条指令编码有多长”。
  • 机器字长:CPU 一次能直接处理的数据位数,常与通用寄存器位数、ALU 位数相关。
  • 存储字长:主存一个存储单元可保存的二进制位数,或一次存储读写的数据宽度。
  • 主存地址位数:地址编码有多少位,决定最多能区分多少个可寻址单元。

它们之间可能相等,也可能不相等。比如某机器的机器字长为 32 位,指令字长也可能是 16 位、32 位或变长;主存按字节编址时,一个地址只指向 8 位的字节,但 CPU 仍然可以一次处理 32 位数据。

Tip

求指令格式、操作码扩展、地址码分配时,关注的是指令字长;求 ALU、通用寄存器、整数运算范围时,关注的是机器字长;求一次访存能取多少位、指令要取几次时,才需要看存储字长或编址单位。

# 操作码长度

操作码可以采用定长,也可以采用可变长。

类型 含义 优点 代价
定长操作码 所有指令的 OP 字段位数相同 译码简单,硬件实现方便 指令种类受固定 OP 位数限制
可变长操作码 不同指令的 OP 字段位数可以不同 在固定指令字长内容纳更多指令种类 译码规则更复杂

若操作码有 $n$ 位,且全部采用定长操作码,则最多可表示 $2^n$ 条不同指令。

扩展操作码

扩展操作码是一种常见的定长指令字结构可变长操作码设计:地址码少的指令,可以把原本不再需要的地址码字段继续用作操作码字段。

例如指令字长为 16 位,每个地址码为 4 位。若先把前 4 位作为基本操作码:

  • 三地址指令格式:OP(4) + A1(4) + A2(4) + A3(4)
  • 二地址指令格式:OP(8) + A1(4) + A2(4)
  • 一地址指令格式:OP(12) + A1(4)
  • 零地址指令格式:OP(16)

扩展操作码必须满足两个规则:

  1. 短操作码不能是长操作码的前缀。否则取到短码时,硬件无法判断它是一条短操作码指令,还是某条长操作码指令的开头。
  2. 任何两条指令的操作码不能重复。否则相同编码会对应两个不同操作,译码无法唯一确定。

扩展操作码前缀规则

扩展操作码指令设计

扩展操作码指令的设计要看码空间怎么被逐层占用

16 位指令字、4 位地址码为例:

指令类型 操作码位数 每保留 1 个上层短码可扩展出的本层编码数
三地址 4 -
二地址 8 $2^4=16$
一地址 12 $2^4=16$
零地址 16 $2^4=16$

若有 m 条三地址指令:

  1. 三地址层共有 $2^4=16$ 个基本短码。
  2. 用掉 m 个三地址短码后,还剩 $16-m$ 个短码可继续向下扩展。
  3. 每个剩余短码可接上 4 位,扩展出 $2^4$ 个二地址操作码。

所以二地址指令最多为:

$$
(16-m)\times 2^4
$$

如果还给定了二地址指令数量,就继续扣除二地址层已占用的编码,再把剩余二地址编码向下一层扩展。

Example

指令字长 16 位,每个地址码 4 位。若已定义 15 条三地址指令,则只剩 1 个四位短码可扩展,所以最多能定义:

  • 二地址指令:$1\times 2^4=16$ 条;
  • 若二地址指令只用 15 条,则还能留下 1 个八位操作码继续扩展;
  • 一地址指令最多:$1\times 2^4=16$ 条;
  • 若一地址指令只用 15 条,则还能留下 1 个十二位操作码继续扩展;
  • 零地址指令最多:$1\times 2^4=16$ 条。
反过来求指令字长至少多少位时,可以把低地址数指令逐层折算成高一层需要预留的操作码入口。

设每个地址码长度为 a,每向下一层扩展一次,就多出 $2^a$ 个更长操作码。因此:

  1. 若要容纳 $N_0$ 条零地址指令,至少要在一地址层预留 $\lceil N_0/2^a\rceil$ 个操作码入口。

  2. 若还要容纳 $N_1$ 条一地址指令,则一地址层总共需要

    $$
    N_1+\left\lceil\frac{N_0}{2^a}\right\rceil
    $$

    个操作码位置。

  3. 再把这些需求折算到二地址层:二地址层需要

    $$
    N_2+\left\lceil\frac{N_1+\left\lceil N_0/2^a\right\rceil}{2^a}\right\rceil
    $$

    个操作码位置。

  4. 继续向上折算到最高地址数那一层。最高层需要多少个操作码位置,就决定最高层操作码至少要多少位。

  5. 字长必须是计算机的编制单元位数的整数倍

Example

计算机按字节编址,地址码长度为 6 位,只有两种指令格式。要求支持 29 条三地址指令和 107 条二地址指令,求指令字长至少多少位。

这里只有三地址和二地址指令。二地址层需要 107 个操作码,折算到三地址层,需要预留:

$$
\left\lceil\frac{107}{2^6}\right\rceil=2
$$

个三地址层操作码入口。

三地址层还要直接容纳 29 条三地址指令,所以三地址层总共需要:

$$
29+2=31
$$

个操作码位置。

因为 $\lceil \log 31 \rceil = 5$,所以三地址层操作码至少要 5 位。三地址指令还需要 3 个地址码,每个 6 位,因此指令字长至少为:

$$
5+3\times6=23
$$

[!question] 因此最少指令字长为 23 位。对吗????

[!important] 错误!
由于计算机按字节编址,指令字长必须得是8的整数倍!
所以正确答案:24

# 指令类型

指令也可以按功能分类。

类型 作用 例子
数据传送指令 在寄存器、主存、I/O 端口之间传送数据 LOADSTOREMOV
算术逻辑指令 完成加减乘除、与或非、比较等操作 ADDSUBANDCMP
移位指令 对操作数进行逻辑移位、算术移位或循环移位 SHLSAR
转移指令 改变程序执行顺序 无条件转移、条件转移、调用、返回、陷阱
输入输出指令 在 CPU 和外设接口之间传送数据或状态 端口输入、端口输出
CPU 控制指令 改变 CPU 或系统运行状态 开中断、关中断、设置程序状态字

CPU 控制指令通常影响整个系统的运行状态,因此属于特权指令

CPU 区分两种运行状态:

特权指令与 CPU 状态

  • 用户态:运行普通应用程序,只能执行非特权指令。
  • 内核态:运行操作系统内核程序,可以执行特权指令。

CPU 当前处于哪种状态,通常由程序状态字寄存器(PSW)中的状态位表示。用户态程序若试图执行特权指令,会触发异常处理,CPU 转入内核态,由操作系统接管并决定如何处理。

Trap

陷阱(Trap)也会改变程序执行流。它常用于从当前程序转入预设的系统处理程序,例如系统调用、调试断点或异常处理入口。

# CISC 与 RISC

指令系统还可以从设计风格上分为 CISC 和 RISC