Skip to main content

指令系统

4.1 指令系统

4.1.1 指令集体系结构

指令集体系结构(Instruction Set Architecture,ISA)是计算机系统中软件与硬件之间的接口。它定义了程序员(尤其是汇编语言程序员)所能够看到的计算机的抽象模型,包括:

  • 指令格式:指令的二进制编码方式。
  • 操作数类型:支持的数据类型(整数、浮点数、地址等)及其长度。
  • 寻址方式:如何计算操作数的有效地址。
  • 寄存器组织:通用寄存器的数量、位数和用途。
  • 存储空间:主存的编址方式、大小和访问方式。
  • 异常与中断:处理机制。
  • 输入输出:I/O控制方式。

ISA 是计算机体系结构中最核心的部分,它决定了软件的兼容性。同一系列计算机(如 x86 系列)通常保持 ISA 的向后兼容,使得为早期处理器编写的程序能在新型处理器上运行。

4.1.2 指令的基本格式

一条指令通常由操作码地址码两部分组成:

操作码地址码(可能多个)
  • 操作码(Opcode):指明指令要执行的操作,如加法、减法、转移等。操作码的位数决定了指令系统中最多能支持的指令条数。
  • 地址码(Address):指明操作数的地址或操作数本身。根据地址码的个数,指令可分为零地址、一地址、二地址、三地址甚至四地址指令。

指令字长是指一条指令所占的二进制位数。指令字长可以等于机器字长(单字长指令),也可以是其整数倍(双字长指令等)。定长指令字结构实现简单,但可能浪费存储空间;变长指令字结构可以节省空间,但控制复杂。

常见指令格式

  1. 零地址指令
    只给出操作码,没有显式的地址码。常用于不需要操作数的指令(如停机、空操作)或堆栈计算机中的运算指令(操作数隐含在堆栈顶部)。

  2. 一地址指令
    格式:OP A1
    含义有两种:

    • 单操作数指令:OP (A1) → A1(如加1、取反)
    • 双操作数指令:(ACC) OP (A1) → ACC(另一个操作数隐含在累加器 ACC 中)
  3. 二地址指令
    格式:OP A1, A2
    含义:(A1) OP (A2) → A1(或 A2,视约定而定)
    通常第一个地址既是源操作数又是目的操作数,第二个地址是另一个源操作数。

  4. 三地址指令
    格式:OP A1, A2, A3
    含义:(A1) OP (A2) → A3
    三个地址分别存放两个源操作数和结果,不破坏源操作数。

  5. 四地址指令
    格式:OP A1, A2, A3, A4
    含义:(A1) OP (A2) → A3A4 给出下一条指令的地址。
    实际上,现代计算机中下一条指令地址通常由程序计数器(PC)提供,因此很少使用四地址指令。

4.1.3 定长操作码指令格式

定长操作码是指所有指令的操作码字段长度相同,并且位于指令字的固定位置。例如,若操作码占 8 位,则最多可表示 256 条指令。这种格式的优点是指令译码简单、速度快,适合指令数量较少的情况。RISC 处理器通常采用定长操作码。

4.1.4 扩展操作码指令格式

扩展操作码技术是为了在指令字长有限的情况下增加指令数量而设计的。其基本思想是:将操作码的长度随地址码数量的减少而增加。即,当指令中地址码个数较少时,可以将空闲的地址码位用于扩展操作码。

例如,一个 16 位指令字,操作码固定为 4 位(可表示 16 种),但需要更多指令时,可以这样设计:

  • 用 4 位操作码的前 15 种作为三地址指令(操作码 0000~1110),剩余一种(1111)作为扩展标志。
  • 当操作码为 1111 时,表示接下来的 4 位(原本是第一个地址码)也作为操作码,于是得到 8 位操作码(可表示 256 种),但此时只有两个地址码字段。
  • 依此类推,可继续扩展。

扩展操作码的设计要点

  • 不允许短操作码是长操作码的前缀(即短码不能与长码的前面部分相同)。
  • 各指令的操作码必须唯一。
  • 通常将使用频率高的指令分配较短的操作码,使用频率低的分配较长的操作码,以缩短平均指令长度。

4.1.5 指令的操作类型

指令系统通常包含以下几类操作:

  1. 数据传送
    包括寄存器与寄存器之间、寄存器与主存之间的数据传送。典型指令:MOVLOADSTOREPUSHPOP

  2. 算术与逻辑运算
    算术运算:加(ADD)、减(SUB)、乘(MUL)、除(DIV)、加1(INC)、减1(DEC)。
    逻辑运算:与(AND)、或(OR)、非(NOT)、异或(XOR)、移位(SHL、SHR)。

  3. 移位操作
    算术移位(考虑符号位)、逻辑移位(补0)、循环移位(带进位或不带进位)。

  4. 转移操作
    无条件转移(JMP)、条件转移(JZ、JC、JG 等)、子程序调用(CALL)、返回(RET)、陷阱(TRAP)。

  5. 输入输出操作
    用于 CPU 与外部设备交换数据,通常有专门指令(如 IN、OUT)或通过内存映射 I/O 实现。


4.2 指令的寻址方式

4.2.1 指令寻址和数据寻址

寻址方式分为两类:

  • 指令寻址:确定下一条将要执行的指令的地址。
  • 数据寻址:确定本条指令所操作的数据的地址。

1. 指令寻址

  • 顺序寻址:通过程序计数器 PC 自动加 1(指令长度)形成下一条指令地址。
  • 跳跃寻址:通过转移指令改变 PC 的值,使程序跳转到指定地址。跳跃后的地址可以是绝对地址,也可以是相对地址。

2. 数据寻址

数据寻址的方式很多,通常在指令中设置一个寻址特征字段来指明采用哪种寻址方式。指令格式变为:

操作码寻址特征形式地址 A

形式地址(A)不是操作数的真实地址,需要根据寻址方式计算出有效地址(EA)

4.2.2 常见的数据寻址方式

  1. 隐含寻址
    操作数地址隐含在指令中(如累加器 ACC),指令中不显式给出地址。
    优点:缩短指令字长。
    缺点:需要额外硬件。

  2. 立即寻址
    形式地址 A 就是操作数本身。
    格式:OP #A
    优点:不需要访存,速度快。
    缺点:A 的位数限制了操作数的范围。

  3. 直接寻址
    有效地址 EA = A,即形式地址就是操作数的实际地址。
    优点:简单,只需一次访存。
    缺点:A 的位数限制了寻址范围,且操作数地址不易修改。

  4. 间接寻址
    有效地址 EA = (A),即形式地址指向的存储单元中存放的是操作数的有效地址。
    优点:可扩大寻址范围(因为存储单元可以存放更大位数的地址)。
    缺点:需要多次访存(一次间接需2次访存),速度慢。

  5. 寄存器寻址
    有效地址 EA = Ri,即 Ri 中存放操作数本身。
    优点:无需访存,速度快;寄存器编号短,指令字短。
    缺点:寄存器数量有限,价格高。

  6. 寄存器间接寻址
    有效地址 EA = (Ri),即 Ri 中存放操作数的地址。
    优点:既扩大了寻址范围(寄存器位数通常等于地址线数),又只需一次访存。
    缺点:仍需访存。

  7. 相对寻址
    有效地址 EA = (PC) + A,其中 A 是相对于当前 PC 的偏移量(补码表示)。
    优点:便于程序浮动(程序在内存中移动时无需修改指令中的地址)。
    广泛用于转移指令。

  8. 基址寻址
    有效地址 EA = (BR) + A,其中 BR 是基址寄存器(可由操作系统指定)。
    优点:可扩大寻址范围,便于多道程序分配存储空间。
    基址寄存器的内容在程序执行过程中通常不变,而 A 可变。

  9. 变址寻址
    有效地址 EA = (IX) + A,其中 IX 是变址寄存器(用户可修改)。
    优点:便于处理数组,通过修改 IX 的值可顺序访问数组元素。
    变址寄存器的内容可变,A 作为基地址。

    注意:基址寻址与变址寻址的区别在于——基址寻址面向系统(基址由OS设定),变址寻址面向用户(变址值由程序改变)。

  10. 堆栈寻址
    操作数隐含在堆栈中,由堆栈指针 SP 自动管理。
    适用于堆栈计算机,指令多为零地址。

4.2.3 偏移寻址

相对寻址、基址寻址、变址寻址统称为偏移寻址,因为它们都是将一个寄存器的内容与一个形式地址相加得到有效地址。区别在于所涉及的寄存器和用途不同。


4.3 程序的机器级代码表示

本节以 x86 汇编语言(Intel 格式)为例,介绍高级语言程序如何转换为机器代码。

4.3.1 常用汇编指令介绍

1. 相关寄存器

x86 处理器有 8 个 32 位通用寄存器:EAX、EBX、ECX、EDX、ESI、EDI、EBP、ESP。为了兼容 16 位和 8 位,它们可被拆分为 AX(低16位)、AH/AL(高/低8位)等。

2. 汇编指令格式(Intel 格式)

  • 指令格式:操作码 目的操作数, 源操作数
  • 操作数类型:寄存器(如 eax)、内存(如 [eax])、立即数(如 5)
  • 常见指令:
    • mov:数据传送。mov eax, ebx 将 ebx 的值复制到 eax。
    • push / pop:压栈/出栈。push eax 将 eax 压入栈顶。
    • add / sub:加/减。add eax, 5 将 eax 的值加5。
    • inc / dec:自增/自减。
    • imul:有符号乘法。imul eax, ebx, 2 将 ebx*2 放入 eax。
    • and / or / xor:逻辑运算。
    • shl / shr:逻辑左移/右移。
    • jmp / jz / jg:无条件/条件转移。
    • call / ret:子程序调用/返回。

4.3.2 选择语句的机器级表示

C 语言中的 if-else 语句通常编译为条件转移指令。

if (x > y)
a = x;
else
a = y;

对应汇编(简化):

    mov eax, x
cmp eax, y ; 比较 x 和 y
jle else_part ; 如果 x ≤ y,跳转到 else
mov a, eax ; 执行 then 部分
jmp end
else_part:
mov eax, y
mov a, eax
end:

4.3.3 循环语句的机器级表示

循环通常由条件转移指令和向后跳转实现。

int sum = 0;
for (int i = 0; i < 10; i++)
sum += i;

对应汇编:

    xor eax, eax      ; sum = 0
xor ecx, ecx ; i = 0
loop_start:
cmp ecx, 10
jge loop_end
add eax, ecx
inc ecx
jmp loop_start
loop_end:
mov sum, eax

4.3.4 过程调用的机器级表示

过程调用通过 call 指令和 ret 指令实现。call 将返回地址(下一条指令的地址)压栈,然后跳转到过程入口;ret 从栈顶弹出地址并跳转。

参数传递通常通过栈或寄存器完成。例如,C 函数调用:

int add(int a, int b) {
return a + b;
}
...
int x = add(3, 5);

对应汇编(简化,cdecl 调用约定):

    push 5
push 3
call add
add esp, 8 ; 清除参数
mov x, eax ; 返回值在 eax

add:
push ebp
mov ebp, esp
mov eax, [ebp+8] ; 第一个参数
add eax, [ebp+12] ; 第二个参数
pop ebp
ret

4.4 CISC 和 RISC 的基本概念

4.4.1 CISC(复杂指令系统计算机)

  • 特点:指令数量多,指令功能强,一条指令可完成复杂操作(如字符串处理、多项式求值)。
  • 指令长度不固定,寻址方式多样。
  • 优点:高级语言编译后指令条数少,对内存访问次数少(因为复杂操作由硬件完成)。
  • 缺点:指令系统复杂,硬件设计难度大,不利于流水线技术。

典型代表:x86 架构(Intel、AMD)。

4.4.2 RISC(精简指令系统计算机)

  • 特点:指令数量少,每条指令功能简单,通常只完成一个基本操作。
  • 指令长度固定,寻址方式少(通常只有寄存器寻址、立即寻址、相对寻址等)。
  • 只有 LOAD/STORE 指令才能访问主存,其他指令只能在寄存器之间操作。
  • 优点:硬件设计简单,易于实现流水线,编译优化容易。
  • 缺点:完成复杂功能需要多条指令,可能增加指令条数和内存访问次数。

典型代表:ARM、MIPS、RISC-V。

4.4.3 CISC 和 RISC 的比较

对比项CISCRISC
指令数量
指令长度不固定固定
寻址方式
访存方式算术指令可直接访存仅 LOAD/STORE 访存
寄存器数量较少较多
指令执行时间不均衡,差别大基本一个时钟周期
流水线实现困难容易
编译优化困难容易

实际上,现代处理器已融合两者优点,如 x86 处理器内部将 CISC 指令转换为 RISC 微操作来执行,既保持了软件兼容性,又获得了流水线的高性能。


4.5 本章小结

本章主要介绍了指令系统的基本概念,包括指令格式、寻址方式、机器级代码表示以及 CISC 和 RISC 的区别。指令系统是计算机软硬件之间的接口,其设计直接影响计算机的性能和编程效率。

重点回顾

  • 指令由操作码和地址码组成,地址码的个数决定了指令格式类型。
  • 扩展操作码技术可以在不增加指令字长的情况下扩大指令数量。
  • 数据寻址方式决定了有效地址的计算方法,常见的寻址方式有立即、直接、间接、寄存器、寄存器间接、相对、基址、变址、隐含等。
  • 了解 x86 汇编的基本指令和程序结构,有助于理解高级语言程序的底层实现。
  • CISC 强调指令功能强大,RISC 强调指令精简、流水线高效。

4.6 常见问题和易混淆知识点

  1. 指令字长、机器字长、存储字长的关系
    三者可以相等,也可以不等。机器字长是 CPU 一次能处理的位数,指令字长是单条指令的位数,存储字长是存储器一次能读写的位数。现代计算机中,存储字长通常等于数据总线宽度,而指令字长可能是其整数倍。

  2. 扩展操作码的编码原则
    必须保证短操作码不能是长操作码的前缀,否则无法译码。例如,若 4 位操作码 1111 用于扩展,则所有以 1111 开头的长操作码必须唯一。

  3. 相对寻址的偏移量计算
    执行转移指令时,PC 已经指向了下一条指令的地址,因此相对地址 EA = (PC) + A,其中 A 是补码表示的偏移量。

  4. 基址寻址与变址寻址的区别

    • 基址寻址:BR 内容由操作系统管理,程序执行中不变,A 可变;主要用于程序重定位和存储保护。
    • 变址寻址:IX 内容由用户程序修改,A 作为基地址;主要用于数组访问。
  5. CISC 与 RISC 的对比记忆

    • CISC:指令多、功能强、长度不固定、访存方式灵活、硬件复杂。
    • RISC:指令少、功能简单、长度固定、LOAD/STORE 结构、流水线高效。
  6. 汇编语言与机器语言的关系
    汇编语言是机器语言的助记符表示,每条汇编指令对应一条机器指令,但汇编语言需要经过汇编程序翻译成机器语言才能被硬件执行。

  7. 过程调用中栈的作用
    栈用于保存返回地址、传递参数、保存局部变量和寄存器现场。调用约定(如 cdecl、stdcall)规定了参数传递顺序和栈清理方式。

  8. 寻址方式中的访存次数

    • 立即寻址:0 次
    • 寄存器寻址:0 次
    • 直接寻址:1 次
    • 寄存器间接寻址:1 次
    • 间接寻址:2 次
    • 变址/基址/相对寻址:1 次(如果 EA 在主存中)或 0 次(如果 EA 指向寄存器)