指令系统全:基本概念、寻址方式、CISC/RISC与x86机器级代码表示。

4.1 指令系统的基本概念

4.1.1 指令执行过程的描述

以取数指令为例,描述指令执行过程:

  1. 取指令:$(PC) \to MAR \to M \to MDR \to IR$

    • 将PC的内容送往MAR,MAR内容送地址线。
    • 主存根据地址读出指令送到数据线。
    • MDR从数据线接收指令,传送到IR。
  2. 分析指令:$OP(IR) \to CU$

    • 将IR中指令的操作码部分送控制器进行译码。
  3. 执行指令:$AD(IR) \to MAR \to M \to MDR \to ACC$

    • 将IR中的地址码送MAR。
    • 主存根据地址取出操作数,经MDR传送到ACC。
  4. 更新PC:$(PC) + 1 \to PC$,计算下一条指令地址。

注意

  • 括号表示寄存器中的内容,箭头左边需加括号,右边不加。
  • 多个箭头时,除第一个寄存器外,其余可省略括号。
  • 若要求每条语句只描述一次传输,需严格使用括号。

4.1.2 指令集体系结构 (ISA)

指令系统是ISA的核心部分,ISA定义了软件与硬件的接口,包括:

  • 指令格式、寻址方式、操作类型。
  • 操作数类型、存放方式(大端/小端)。
  • 可访问的寄存器、存储空间编址。
  • 指令执行控制方式(PC、条件码等)。

4.1.3 指令的基本格式

指令由操作码地址码组成:

字段 作用
操作码 指出指令执行的操作
地址码 给出操作数或操作数地址

指令字长

  • 与机器字长无固定关系,可为单字长、半字长、双字长。
  • 定长指令字:所有指令长度相等。
  • 变长指令字:指令长度随功能变化。

按地址码数量分类

类型 格式 说明
零地址 OP 无操作数(如空操作)或隐含操作数(如堆栈)
一地址 OP A1 单操作数:OP(A1) → A1
双操作数隐含ACC:(ACC) OP (A1) → ACC
二地址 OP A1, A2 (A1) OP (A2) → A1
三地址 OP A1, A2, A3 (A1) OP (A2) → A3
四地址 OP A1, A2, A3, A4 (A1) OP (A2) → A3,A4为下一条指令地址

4.1.4 可变长度操作码(扩展操作码)

  • 通过减少地址码位数来增加操作码位数。
  • 规定:短码不能是长码的前缀,操作码不可重复。

示例

指令字长16位,地址码4位:

  • 三地址指令:15条(1111留作扩展)
  • 二地址指令:15条(1111 1111留作扩展)
  • 一地址指令:15条(1111 1111 1111留作扩展)
  • 零地址指令:16条

典型例题

设指令字长16位,地址码6位。二地址指令15条,一地址指令34条,求零地址指令最多多少条?

解法

  • 二地址:操作码4位,0000~1110共15条。
  • 一地址:操作码10位,最高4位为1111,剩余6位,最多64条。用其中34条(如11110 00000 ~ 11111 00001)。
  • 零地址:操作码16位,最高5位为11111,次5位为00010~11111(30种),低6位任意,共 $30 \times 2^6$ 条。

4.2 指令的寻址方式

4.2.1 形式地址与有效地址

  • **形式地址 (A)**:指令中给出的地址码。
  • **有效地址 (EA)**:操作数在存储器中的真实地址。

4.2.2 常见寻址方式

寻址方式 有效地址 EA 访存次数 特点
隐含寻址 隐含(如ACC) 0 缩短指令字长
立即寻址 A 即操作数 0 速度快,但立即数范围受限
直接寻址 EA = A 1 简单,但寻址范围小
间接寻址 EA = (A) 2(一级) 扩大寻址范围
寄存器寻址 EA = Ri 0 速度快,寄存器有限
寄存器间接寻址 EA = (Ri) 1 扩大范围,速度快
相对寻址 EA = (PC) + A 1 用于转移指令,程序浮动
基址寻址 EA = (BR) + A 1 面向OS,动态重定位
变址寻址 EA = (IX) + A 1 面向用户,数组访问
堆栈寻址 隐含 SP 0/1 栈操作

基址与变址对比

  • 基址:BR不变,A可变(偏移量),面向系统。
  • 变址:IX可变,A不变(基地址),面向用户。

4.3 CISC 与 RISC 的基本概念

特性 CISC RISC
指令数量 多(>200)
指令长度 不固定 固定
指令格式
寻址方式
访存指令 不受限 仅 LOAD/STORE
通用寄存器 较少
控制器 微程序 硬布线
流水线 难实现 必须支持
指令执行时间 相差大 大多单周期

4.4 程序的机器级代码表示(x86)

4.4.1 常用汇编指令

寄存器

  • 32位通用寄存器:EAX, EBX, ECX, EDX, ESI, EDI, EBP, ESP
  • 低16位:AX, BX, CX, DX
  • 8位:AH/AL, BH/BL, CH/CL, DH/DL
  • ESP(栈指针),EBP(基址指针)

指令格式(Intel风格)

  • 目标操作数在前,源操作数在后。
  • 内存寻址用 [],如 [eax + ebx*2 + 8]
  • 数据长度:byte ptrword ptrdword ptr

数据传送指令

1
2
3
4
mov eax, ebx          ; ebx → eax
mov byte ptr [var], 5 ; 5 → var指向的字节
push eax ; eax入栈
pop eax ; 栈顶 → eax

算术与逻辑指令

1
2
3
4
5
6
7
8
9
10
11
add eax, ebx          ; eax + ebx → eax
sub eax, 10 ; eax - 10 → eax
inc eax ; eax++
dec dword ptr [var] ; var指向的4字节自减1
imul eax, [var] ; eax * [var] → eax
imul esi, edi, 25 ; 25 * edi → esi
idiv ebx ; edx:eax / ebx → eax商, edx余数
and eax, 0Fh ; 保留低4位
xor edx, edx ; edx清零
shl eax, 1 ; 左移1位
shr ebx, cl ; 右移cl位

控制流指令

1
2
3
4
5
6
7
8
label:                ; 标签
jmp label ; 无条件跳转
cmp eax, ebx ; 比较,设置条件码
je done ; 相等则跳转
jne done ; 不等则跳转
jg / jge / jl / jle ; 大于/大于等于/小于/小于等于
call func ; 调用子程序
ret ; 返回

4.4.2 选择语句的机器级表示

C语言 if-else 结构:

1
2
3
4
5
if (op1 == op2) {
X = 1; Y = 2;
} else {
X = 3; Y = 4;
}

对应汇编:

1
2
3
4
5
6
7
8
9
10
mov eax, op1
cmp eax, op2
jne L1
mov X, 1
mov Y, 2
jmp L2
L1:
mov X, 3
mov Y, 4
L2:

4.4.3 循环语句的机器级表示

编译器将 for/while 转换为 do-while 形式。

示例:自然数求和

1
2
3
4
int sum = 0;
for (int i = 1; i <= n; i++) {
sum += i;
}

转换为 do-while 形式后,对应汇编:

1
2
3
4
5
6
7
8
9
10
mov eax, 0        ; sum = 0
mov edx, 0 ; i = 0
cmp edx, ecx ; 比较 i 和 n (n在ecx)
jg L2 ; if i > n, 跳转
L1:
add eax, edx ; sum += i
add edx, 1 ; i++
cmp edx, ecx ; 比较 i 和 n
jle L1 ; if i <= n, 继续循环
L2:

4.4.4 过程调用的机器级表示

调用过程(P调用Q)

  1. P将参数放到Q能访问的位置。
  2. P保存返回地址,call Q 转移控制。
  3. Q保存P的现场(被调用者保存寄存器)。
  4. Q分配局部变量空间。
  5. 执行Q。
  6. Q恢复现场,将返回值放入 eax
  7. ret 返回P。

调用者保存与被调用者保存

类型 寄存器 职责
调用者保存 EAX, ECX, EDX 调用者负责保存和恢复
被调用者保存 EBX, ESI, EDI 被调用者负责保存和恢复

栈帧

  • EBP:当前过程栈帧基址(固定)
  • ESP:栈顶指针(动态变化)
  • 栈向低地址增长

示例:过程调用与栈帧

1
2
3
4
5
6
int add(int x, int y) { return x + y; }
int caller() {
int temp1 = 125, temp2 = 80;
int sum = add(temp1, temp2);
return sum;
}

汇编实现(caller部分):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
caller:
push ebp ; 保存上一栈底
mov ebp, esp ; 设置新栈底
sub esp, 24 ; 分配24字节空间
mov [ebp-12], 125 ; temp1
mov [ebp-8], 80 ; temp2
mov eax, [ebp-8] ; temp2 → eax
mov [esp+4], eax ; 参数2入栈
mov eax, [ebp-12] ; temp1 → eax
mov [esp], eax ; 参数1入栈
call add ; 调用add,返回值在eax
mov [ebp-4], eax ; 返回值存入sum
mov eax, [ebp-4] ; sum → eax(返回值)
leave ; 恢复栈帧
ret

栈帧结构示意

1
2
3
4
5
6
7
8
9
10
11
12
高地址
+-----------------+
| 调用者栈帧 |
+-----------------+ ← ebp (调用者)
| 返回地址 |
+-----------------+
| 保存的ebp | ← ebp (caller)
+-----------------+
| 局部变量 |
| ... |
+-----------------+ ← esp
低地址

备考tips

  • 指令执行过程:重点关注微操作序列与数据通路结合题。
  • 扩展操作码:掌握计算最大指令数的方法。
  • 寻址方式:区分基址与变址,理解有效地址计算。
  • CISC vs RISC:对比记忆,关注RISC特点(LOAD/STORE架构)。
  • 汇编与C对应:能根据C代码写出关键汇编片段,或反之理解程序逻辑。
  • 过程调用:理解栈帧变化、参数传递、返回值存放(eax)。