4.1 指令格式

指令
计算机执行某种操作的命令,是软件与硬件的接口。指令格式 = 操作码 OP + 地址码 A。

指令字长 = 操作码长度 + 地址码长度。按地址码字段个数,指令可分为:

类型格式含义访存次数
零地址指令OP无操作数(如 NOP、停机)或隐含寻址(如栈操作)0
一地址指令OP AACC ← OP(A),需另一个操作数隐含在 ACC2(读A、写ACC)
二地址指令OP A1 A2A1 ← (A1) OP (A2)3~4
三地址指令OP A1 A2 A3A1 ← (A2) OP (A3)4
四地址指令OP A1 A2 A3 A4A1 ← (A2) OP (A3),A4 为下条指令地址4
操作码扩展:固定长度操作码简单但浪费空间;可变长度操作码通过扩展技术,在地址数少的指令中用较长操作码,提高编码效率。

4.2 寻址方式

寻址方式
确定本条指令的数据地址及下一条待执行指令地址的方法。寻址方式的目标是在有限指令字长内灵活表示更大的地址空间。

有效地址 EA:操作数的真实地址(按字节或字编址)。形式地址 A 是指令中给出的地址字段。

各寻址方式对比

寻址方式有效地址 EA访存次数特点
立即寻址—(操作数在指令中)0快,但操作数长度受限
直接寻址EA = A1简单,地址范围受 A 长度限制
间接寻址EA = (A)2(或多重)扩大寻址范围,但慢
寄存器寻址EA = Ri(操作数在 Ri)0快,无需访存
寄存器间接寻址EA = (Ri)1比间接快,常用于循环
基址寻址EA = (BR) + A1BR 由 OS 管理,利于多道程序重定位
变址寻址EA = (IX) + A1IX 由用户管理,适合数组/循环
相对寻址EA = (PC) + A1A 为偏移量,用于转移指令,利于浮动
基址 vs 变址:基址寄存器内容通常不变(由操作系统控制,面向多道程序),形式地址 A 为位移量;变址寄存器内容可变(由用户控制,面向循环/数组),形式地址 A 给出基准。两者公式相同但用途不同。

4.2.1 数据对齐与大/小端存储 [22新增]

按边界对齐
n 字节数据的存放起始地址必须能被 n 整除。例如:4 字节 int 必须存放在地址为 4 的倍数的位置。不对齐的数据访问需要多次访存,降低性能。

大端模式(Big Endian):高位字节存放在低地址,低位字节存放在高地址。符合人类阅读习惯(从左到右),网络字节序通常采用大端。

小端模式(Little Endian):低位字节存放在低地址,高位字节存放在高地址。x86、ARM(默认)等主流 CPU 使用小端模式。

示例:0x12345678 的存储
地址大端模式小端模式
低地址(如 0x100)12(最高字节)78(最低字节)
0x1013456
0x1025634
高地址(0x103)78(最低字节)12(最高字节)

记忆口诀:大端 — "高对低"(高位字节在低地址);小端 — "低对低"(低位字节在低地址)。

4.2.2 编译器、汇编器与链接器

程序转换流程
源程序(.c) → 编译器 → 汇编代码(.s) → 汇编器 → 目标文件(.o) → 链接器 → 可执行文件(.exe/ELF)。
符号解析与重定位:符号解析解决"函数/变量在哪里"的问题;重定位将目标文件中的相对地址修改为可执行文件中的绝对地址。链接器需要修改指令中的地址字段,使跳转、访存指令指向正确的目标。

4.2.3 高级语言与机器级代码对应

高级语言程序最终被翻译为机器指令序列执行,下面以 C 语言为例说明常见结构的对应关系。

选择结构(if-else)→ 条件分支

C 语言中的 if-else 语句编译为比较指令(CMP) + 条件跳转指令(Bcond)

算法: if-else 的机器指令映射
输入: 变量 a, b, max 均为寄存器
输出: max = max(a, b)

C 代码:                对应机器指令:
if (a > b)             CMP  Ra, Rb       // 比较 a 和 b
  max = a;             BLE  ELSE         // 若 a≤b 跳转到 ELSE
else                   MOV  Rmax, Ra     // max = a (then 分支)
  max = b;             JMP  ENDIF        // 跳过 else 分支
                       ELSE:  MOV  Rmax, Rb  // max = b
                       ENDIF: ...            // 后续代码

循环结构(for/while)→ 循环分支

算法: for 循环的机器指令映射
输入: 初始 sum=0, 循环变量 i
输出: 计算 sum = 0+1+2+...+N-1

C 代码:                对应机器指令:
sum = 0;               MOV  Rsum, #0     // sum = 0
i = 0;                 MOV  Ri, #0       // i = 0
for (i=0; i<N; i++)    LOOP:  CMP  Ri, #N   // 比较 i 和 N
  sum += i;            BGE  EXIT         // 若 i≥N 退出循环
                       ADD  Rsum, Rsum, Ri  // sum += i
                       ADD  Ri, Ri, #1   // i++
                       JMP  LOOP         // 跳回循环头
                       EXIT: ...             // 循环结束

函数调用 → 栈帧与 call/ret

栈帧(Stack Frame)
每次函数调用在栈上分配的一块内存区域,用于保存返回地址、调用者保存的寄存器、局部变量等。由调用者(Caller)和被调用者(Callee)共同管理。

函数调用的关键步骤:

调用约定:x86 常见约定包括 cdecl(调用者清理栈)、stdcall(被调用者清理栈)。参数压栈顺序通常为从右向左(cdecl)。408 中需理解 call/ret 对栈指针 SP 和程序计数器 PC 的影响。

4.3 CISC 与 RISC

对比项CISC(复杂指令集)RISC(精简指令集)
指令系统复杂、庞大(100+ 条)简单、精简(<100 条)
指令长度可变固定
寻址方式多(>4 种)少(≤4 种)
执行时间差异大大多一个周期完成
通用寄存器较少较多(>32 个)
控制方式微程序控制为主硬布线控制为主
流水线难实现易实现,必须实现
代表x86MIPS、ARM、RISC-V
RISC 核心思想
选取使用频率最高的简单指令,固定指令长度、Load/Store 结构(只有访存指令访问内存)、硬布线控制、大量寄存器、指令流水线,以提高主频和 CPI。