第 4 章 指令系统
408 计算机组成原理第 4 章 指令系统复习讲义,涵盖核心概念、重点考点与典型题型。
Last updated on
一、先建立整章框架
这一章本质上是在回答一个问题:
高级语言写出来的程序,最终到底怎样变成 CPU 能识别、寻址、执行的一条条机器指令?
可以把整章串成下面这条主线:
高级语言
↓ 编译
汇编语言
↓ 汇编
机器指令
↓
┌──────────────────────────┐
│ 操作码:到底要干什么 │
│ 地址码:数据到底在哪里 │
└──────────────────────────┘
↓
寻址方式计算有效地址 EA
↓
寄存器 / 主存中取得操作数
↓
CPU 执行
↓
条件码、跳转、函数调用因此,第 4 章和后面的第 5 章 CPU 是强关联的:
第 4 章告诉你“指令长什么样、表达什么意思”;第 5 章告诉你“CPU 内部究竟怎样执行它”。
从考试角度,建议优先级这样理解:
| 内容 | 重要程度 | 常见题型 |
|---|---|---|
| 各种寻址方式、EA 计算 | ★★★★★ | 选择题、综合题 |
| 相对/基址/变址寻址 | ★★★★★ | 高频计算 |
| 汇编代码与 C 程序对应 | ★★★★★ | 代码阅读、综合题 |
| 大小端、边界对齐 | ★★★★★ | 地址计算 |
| 扩展操作码 | ★★★★★ | 编码设计计算 |
| 指令格式 | ★★★★☆ | 地址数、指令数量、访存次数 |
| ISA | ★★★☆☆ | 概念判断 |
| CISC/RISC | ★★★★☆ | 特点辨析 |
| 普通指令分类 | ★★★☆☆ | 概念题 |
二、4.1 指令系统
2.1 什么叫“指令”
机器指令就是 CPU 能直接识别和执行的命令。
比如高级语言:
c = a + b;在机器层面不可能直接写成“把 赋给 ”。
它要被翻译成类似:
mov eax, [a]
add eax, [b]
mov [c], eax再进一步转换成二进制机器代码。
一台机器能够执行的所有机器指令的集合,就是它的指令系统或指令集。
三、ISA:软硬件之间最关键的接口
资料把 ISA,即 Instruction Set Architecture,视为本章的重要概念,并标出了 2022、2025 的考点追踪。
ISA 可以理解成:
CPU 对软件公布的“使用说明书”。
软件并不需要知道 CPU 内部到底有几个 ALU、流水线分几级、晶体管怎样连,但软件必须知道:
“有哪些寄存器?”
“有哪些指令?”
“指令怎么编码?”
“数据怎么寻址?”
“整数是多长?”
“内存怎么编址?”
“哪些状态对程序员可见?”
因此 ISA 规定的核心包括指令格式、寻址方式、操作类型、操作数类型、大小端方式、程序可访问的寄存器、存储空间及编址方式,以及 PC、条件码等程序员可见状态。
这正好解释第 1 章的一句话:
ISA 是软件和硬件的界面。
同一个 ISA 可以有完全不同的 CPU 实现。
例如不同 x86 CPU 都能执行 x86 指令,但内部微架构可能完全不同。
四、指令的基本格式
最简单的模型就是:
┌──────────┬──────────────┐
│ 操作码 OP │ 地址码 A │
└──────────┴──────────────┘资料中定义:
操作码 OP告诉 CPU“执行什么操作”。
例如:
ADD
SUB
MOV
JMP
AND地址码告诉 CPU“操作对象在哪里”。
这里的“地址”不要狭义理解成主存地址,它可能是:
寄存器编号、主存地址、立即数、转移目标、偏移量等。
五、指令字长
指令字长就是一条机器指令占多少二进制位。
例如:
16 bit
32 bit
64 bit它与“机器字长”不是同一个东西。
机器字长更偏向 CPU 一次整数运算的数据宽度;指令字长是“一条机器指令有多长”。
指令可以分成两大类。
定长指令字意味着所有指令长度相同,例如全部都是 32 bit。优点是取指、译码非常规律,特别有利于流水线。
变长指令字意味着不同指令长度不同。例如某些 x86 指令可能短,某些比较长。优点是编码紧凑,但译码复杂。
资料也明确指出:定长指令字更容易取指、译码和流水线实现。
六、零地址、一地址、二地址、三地址指令
这是理解指令格式的基础。
假设我们计算:
6.1 三地址指令
形式:
OP A1 A2 A3含义可以理解为:
例如:
ADD A, B, C即:
优点是结果不会破坏源操作数。
缺点是三个地址字段太占位。
若三个操作数都在主存,则通常需要:
取指一次、读 一次、读 一次、写 一次。
因此共:
次访存。资料也直接给出了这一结论。
6.2 二地址指令
形式:
OP A1 A2通常表示:
例如:
ADD A, B表示:
也就是说:
A 既是操作数,又是结果位置。
因此会破坏原来的 。
现代汇编中非常常见这种形式,例如 Intel:
add eax, ebx表示:
6.3 一地址指令
一地址指令常见两种含义。
一种是单操作数运算,例如:
NOT A
INC A即:
另一种是隐含另一个操作数,例如隐含累加器 ACC:
ADD A可以理解为:
若操作数 在主存,通常需要:
取指、取 、写结果。
因此可能有 3 次访存。
6.4 零地址指令
形式:
OP没有显式地址。
常见于两个情况。
第一类是不需要操作数的指令,例如:
NOP
HALT第二类是堆栈型机器。
比如栈:
栈顶 a
次栈顶 b执行:
ADDCPU 隐含:
然后将结果压回栈顶。
所以虽然指令没写地址,但不等于没有操作数,而是:
操作数的位置被隐含了。
七、定长操作码
如果操作码字段固定为 位,最多能够表示:
种不同的操作。
例如 OP 为 6 位:
因此最多 64 种操作。
资料将这个考点追踪到 2015。
这类题最简单。
比如一条 32 bit 指令:
OP 6 bit | R1 5 bit | R2 5 bit | 剩余那么最多支持:
个基本操作码。
八、扩展操作码:本章最重要计算题之一
资料将扩展操作码的设计和分析标记为 2017、2021、2022 考点。
这个知识点很多人死记公式,其实理解之后非常简单。
8.1 本质
假设 16 位指令:
┌────┬────┬────┬────┐
│ OP │ A1 │ A2 │ A3 │
│4bit│4bit│4bit│4bit│
└────┴────┴────┴────┘对于三地址指令,需要三个地址,因此操作码只有 4 bit。
但是二地址指令少用了一个地址字段。
那么:
没使用的地址字段能不能拿来继续扩展操作码?
当然可以。
这就是扩展操作码。
资料给出的典型设计是三地址指令使用 15 个基本操作码,把 1111 留作扩展前缀;之后继续向后借地址字段扩展二地址、一地址、零地址指令。
九、扩展操作码最重要的做题方法
不要背某一个固定结果。
永远使用:
“剩余前缀 × 下一层可扩展数量”
的思路。
来看资料题库中的 2022 真题。题意大致为:
16 位定长指令,地址码 6 bit,包含二地址、一地址、零地址指令。
已知:
二地址:12 条
一地址:254 条问最多有多少零地址指令。
第一步:算二地址操作码长度
二地址已经需要:
bit 地址。
所以基本 OP:
bit。
共:
个基本前缀。
二地址用了 12 个。
还剩:
个。
第二步:扩成一地址指令
一地址少了一个 6 bit 地址字段,因此每一个剩余前缀能扩出:
种一地址操作码。
总共:
种。
但实际只用:
个。
因此还有:
个一地址前缀没有用。
第三步:继续扩成零地址
每个剩余一地址前缀继续释放最后一个 6 bit 地址字段。
所以每个可以变成:
种零地址指令。
于是:
所以答案:
这就是扩展操作码最标准的解法。
十、寻址方式:本章核心中的核心
资料对寻址方式的定义非常重要:
指令地址字段中的值叫形式地址 。
真正的操作数地址叫有效地址 。
换句话说:
指令中写的 ,不一定就是操作数真正的位置。
CPU 要根据寻址方式把:
转换成:
资料还强调,不同寻址方式下地址字段位数的意义完全不同。例如立即寻址决定立即数范围,直接寻址决定可直接访问的存储空间,寄存器寻址决定可编码寄存器数量。
十一、八种常见寻址方式
这部分建议你最终形成下面这张表。
| 寻址方式 | 关键关系 | 操作数在哪里 | 执行阶段典型访存 | 特点 |
|---|---|---|---|---|
| 隐含寻址 | 隐含 | ACC/栈顶等 | 视情况 | 指令短 |
| 立即寻址 | 指令中 | 0 | 最快,范围小 | |
| 直接寻址 | 主存 | 1 | 简单,范围受地址字段限制 | |
| 间接寻址 | 主存 | 2 | 范围大,但慢 | |
| 寄存器寻址 | 操作数 | 寄存器 | 0 | 快、地址字段短 |
| 寄存器间接 | 主存 | 1 | 灵活、范围大 | |
| 相对寻址 | 通常转移目标 | — | 转移指令高频 | |
| 基址寻址 | 主存 | 1 | 重定位、多道程序 | |
| 变址寻址 | 主存 | 1 | 数组、循环 |
资料的总结也基本按这个框架组织。
下面重点把真正容易混的几个讲透。
十二、立即寻址
例如:
mov eax, 100这里 100 不是地址,而就是操作数。
因此:
执行阶段无需再去主存寻找该操作数,所以速度非常快。
资料将立即寻址直接标为 2023 考点。
立即数字段如果是 位,注意它的范围取决于是否解释为有符号数。
若是无符号:
若是补码有符号:
易错点:
mov eax, 100和
mov eax, [100]完全不同。
前者是:
后者是:
十三、直接寻址
定义:
例如指令地址字段:
那么:
操作数:
执行阶段只需要访问一次主存取得操作数。
资料把“地址位数与直接寻址范围”的关系标记到 2010、2021。
例如地址码 24 bit:
因此最多直接编码 个不同地址。
注意这里还要结合:
按字编址还是按字节编址。
如果按字节编址,一个地址对应 1B。
十四、间接寻址
定义:
这里括号表示:
地址 A 所对应单元中存放的内容。
假设:
而:
那么:
真正操作数:
过程就是:
指令给 1000
↓
M[1000] = 5000
↓
EA = 5000
↓
M[5000] = 操作数所以执行阶段通常要访问主存两次:
第一次:
第二次:
资料将间接寻址 EA 分析列为 2016 考点。
它最大的优势是:
地址可以动态变化。
非常像高级语言中的指针。
十五、寄存器寻址
指令地址字段保存的是寄存器编号。
例如:
add eax, ebx操作数直接在 eax、ebx 中。
执行阶段:
不需要访问主存。
资料标出了 2022、2024 对“寄存器数量与编号位数”的考查。
如果有 个寄存器,需要的寄存器编号位数:
例如 32 个寄存器:
所以只需 5 bit。
这也是为什么 RISC 大量使用寄存器的同时仍能保持比较规整的指令格式。
十六、寄存器间接寻址
这是考试非常爱混淆的地方。
寄存器寻址:
Ri 里面就是操作数寄存器间接寻址:
Ri 里面是操作数地址数学上:
然后:
例如:
则:
再读取:
资料把寄存器间接寻址的取数操作标到了 2010。
它比普通间接寻址快,因为 EA 在寄存器里,不需要先从主存取得 EA。
所以执行阶段一般只需要一次主存访问。
十七、相对寻址:⭐⭐⭐⭐⭐
这是本章最重要的寻址方式之一。
资料标出的考频非常高:
2009、2010、2013、2014、2019、2023。
基本公式:
其中 是偏移量。
最重要的细节是:
这里的 PC 通常是取指结束以后,已经指向下一条指令的 PC。
这是无数错题的来源。
17.1 例子
假设:
当前指令首地址:
指令长:
那么取指结束后:
如果偏移量:
则:
所以:
资料中正好使用了这个例子。
17.2 为什么相对寻址这么重要
因为转移目标可以跟着程序一起“移动”。
比如某条指令说:
向后跳 20B。
程序原来放在:
1000H后来整体搬到:
5000H只要代码内部相对位置没变,“向后 20B”仍然正确。
因此相对寻址特别适合:
条件转移、循环、分支、位置无关代码。
17.3 偏移量范围
如果相对偏移量 为 位补码:
若按字节编址,则它决定可向前、向后跳多少字节。
做题时一定按顺序:
先算取指后的 PC
→ 再把偏移量符号扩展
→ 再做 PC + A不要用当前指令首地址直接加。
十八、基址寻址
公式:
资料标记 2019 考查基址寻址的 EA 计算。
理解方法:
BR 提供:
这一大块数据/程序从哪里开始。
A 提供:
在这一大块中的偏移多少。
例如:
则:
基址寄存器的典型用途是:
程序重定位和多道程序设计。
程序员写程序时只需要写偏移量。
程序到底装到主存哪个位置,由系统决定 BR。
资料中强调,在典型基址寻址语义下,基址由操作系统设定,形式地址 A 则作为程序使用的偏移量。
十九、变址寻址:⭐⭐⭐⭐⭐
公式和基址几乎一样:
资料标记的考频是:
2013、2016、2024。
看起来跟基址完全一样,但用途不同。
变址寻址最经典用途:
数组。
假设数组基地址:
数组元素大小为 4B。
第 个元素:
如果把:
那么:
这就是变址。
循环中每次更新 IX:
IX = IX + 4就能访问:
a[0]
a[1]
a[2]
...二十、基址和变址怎么区分
这是必考辨析。
数学公式:
完全一样。
区别在“谁固定、谁变化”。
典型教材语义下:
| 基址寻址 | 变址寻址 |
|---|---|
| BR 提供程序/数据块基地址 | IX 提供元素偏移 |
| BR 通常系统控制 | IX 通常程序员控制 |
| A 是程序内部偏移 | A 常作为数组基地址 |
| 主要为了重定位 | 主要为了数组、循环 |
可以记一句:
基址:搬程序。变址:扫数组。
资料的综合总结同样指出,基址寻址适合多道程序地址重定位,而变址寻址适合数组遍历和循环。
二十一、大小端:⭐⭐⭐⭐⭐
这是跨第 2 章和第 4 章都极其重要的内容。
资料标记大小端考查年份包括:
2016、2018、2019、2024、2025,而判断大小端存放顺序又有 2019、2023。
假设一个 32 bit 数:
0x12345678拆成四个字节:
12 34 56 78其中:
12 = MSB
78 = LSB21.1 大端
高有效字节放低地址。
若起始地址为 1000H:
| 地址 | 内容 |
|---|---|
| 1000H | 12 |
| 1001H | 34 |
| 1002H | 56 |
| 1003H | 78 |
可以记:
大端看起来和十六进制书写顺序一样。
21.2 小端
低有效字节放低地址。
因此:
| 地址 | 内容 |
|---|---|
| 1000H | 78 |
| 1001H | 56 |
| 1002H | 34 |
| 1003H | 12 |
资料用 01234567H 也给出了完全对应的示例。
一句话记忆:
小端:小的低位放小地址。
二十二、小端机器代码为什么看起来反着写
这是机器级代码题非常关键的一点。
例如逻辑立即数:
0x08049464分字节:
08 04 94 64如果小端存储,内存低地址到高地址看到的是:
64 94 04 08所以反汇编机器代码中,经常会看到“数字倒过来了”。
资料明确使用机器码示例说明了这一点。
二十三、边界对齐:高频计算题
资料题库中有经典 2012 真题直接同时考查:
结构体 + 小端 + 边界对齐。
最常用的理解:
32 位系统中通常:
char 1B 对齐
short 2B 对齐
int 4B 对齐意味着:
char 可以从任意地址开始;
short 的首地址通常应为 2 的整数倍;
int 的首地址通常应为 4 的整数倍。
资料解释了对齐的本质:让数据尽量能一次完成访存;若一个数据跨越存储边界,可能需要两次访问并重新拼接。
二十四、结构体对齐典型题
例如:
struct {
int a;
char b;
short c;
} record;假设:
那么:
a : 4B占:
C008
C009
C00A
C00B接下来 b:
C00C占 1B。
理论上 c 接在:
C00D但 short 需要 2B 对齐。
C00D 不是偶地址。
所以 C00D 填充。
c 从:
开始。
这就是:
为了时间效率牺牲少量空间。
二十五、机器级代码:本章另一大核心
资料明确说明 x86 是这里重点采用的例子,而且统考题通常使用 Intel 格式。
你不需要成为汇编程序员。
408 真正要求的是:
看懂汇编代码究竟对应什么 C 语句。
二十六、x86 常见寄存器
资料中 32 位 x86 使用的主要寄存器如下。
| 寄存器 | 常见意义 |
|---|---|
| EAX | 累加器、常作为返回值 |
| EBX | 基址类寄存器 |
| ECX | 计数器 |
| EDX | 数据寄存器 |
| ESI | 源变址 |
| EDI | 目的变址 |
| EBP | 当前栈帧基址 |
| ESP | 当前栈顶 |
而:
EAX低 16 bit 是:
AXAX 又可以拆:
AH AL二十七、Intel 汇编最重要规则
统考主要看 Intel 格式。
最重要的是:
操作码 目的, 源例如:
mov eax, ebx表示:
左边是目的,右边是源。
千万别看反。
资料也专门对比了 AT&T 和 Intel 两种格式,并明确指出统考通常使用 Intel 格式。
二十八、mov 和 lea 必须分清
例如:
mov eax, [ebx]是:
也就是:
去内存读数据。
但:
lea eax, [ebx+ecx*4+8]表示:
它求的是:
地址本身。
不会读取那个地址里的数据。
资料的 Intel/AT&T 示例直接给出了 lea 的这种含义。
所以:
mov = 取值
lea = 算地址这是做机器代码题非常实用的一条。
二十九、cmp 与 test
资料强调这两个指令都:
只改条件码,不保存运算结果。
cmp A,B 可以理解为偷偷计算:
但不写回。
仅更新:
CF
ZF
SF
OF等标志位。
所以:
cmp eax, ebx
jle L1含义是:
if (eax <= ebx)
goto L1;test A,B 相当于偷偷计算:
但不保存结果。
最经典:
test eax, eax
jz L1因为:
所以如果:
则:
然后 jz 跳转。
三十、条件转移必须分有符号和无符号
这是机器级代码题非常容易扣分的地方。
有符号整数通常看:
jg
jge
jl
jle无符号通常看:
ja
jae
jb
jbe资料中的一个例子:
int get_cont(int *p1,int *p2)因为指针比较按无符号地址比较处理,因此使用:
jbe而不是 jle。
所以看到 C 代码首先要问:
这是 signed 还是 unsigned?
三十一、if-else 如何变成机器代码
C 语言:
if (x > y)
a = 1;
else
a = 2;机器层面没有“if”这个概念。
本质只有:
比较
条件跳转
执行一段代码
无条件跳转
执行另一段代码逻辑相当于:
if (!(x > y))
goto false;
a = 1;
goto done;
false:
a = 2;
done:资料也是用这种 goto 形式解释高级语言选择结构。
因此做汇编还原 C 代码时:
不要逐句死翻,要先找控制流图。
重点找:
cmp
test
jxx
jmp
label三十二、循环的机器级表示:⭐⭐⭐⭐⭐
资料把循环代码分析列为:
2014、2017、2019、2023。
机器语言里也不存在:
for
while
do while最终全部变成:
比较 + 跳转32.1 do-while
C:
do {
body;
} while (condition);本质:
loop:
body
test condition
if true goto loop所以最大的特征:
先执行循环体,后判断。
32.2 while
C:
while (condition) {
body;
}大致变:
先判断 condition
false → 退出
true → body
↓
回到判断所以循环体可能一次都不执行。
32.3 for
例如:
for(i=1;i<=n;i++)
result += i;拆开:
初始化 i=1
↓
判断 i<=n
↓
循环体
↓
i++
↓
重新判断资料给出的对应汇编很典型:
mov ecx, dword ptr [ebp+8]
mov eax, 0
mov edx, 1
cmp edx, ecx
jg .L2
.L1:
add eax, edx
add edx, 1
cmp edx, ecx
jle .L1
.L2:这里:
ecx = n
eax = result
edx = i看到:
add edx,1你就应该想到:
i++看到:
cmp edx,ecx
jle .L1就应该想到:
i <= n资料对这个例子进行了逐句解释。
三十三、函数调用:call 和 ret
函数调用是机器级代码非常重要的一部分。
资料标记:
call 指令功能:2019。
假设:
P();内部调用 Q。
call Q 干两件关键事:
第一,把返回地址保存起来。
返回地址就是:
call下一条指令的地址。
第二:
转去执行 Q。
ret 做相反操作:
从栈中取出:
然后:
所以:
call = 保存回来去哪 + 跳过去
ret = 找回来去哪 + 跳回来资料对此给出了完整过程。
三十四、函数调用完整过程
假设 P 调用 Q,可以这样理解:
P
│
│ 参数准备
│
├─ call Q
│ ├─ 返回地址压栈
│ └─ PC ← Q
│
▼
Q
│
├─ 建立栈帧
├─ 保存必要寄存器
├─ 分配局部变量
├─ 执行函数体
├─ 返回值放约定位置
├─ 恢复现场
└─ ret
↓
P 中 call 后的下一条指令资料把这一过程概括为参数传递、控制转移、栈帧建立、函数执行、结果返回、恢复现场和 ret。
三十五、EBP 和 ESP 怎么理解
在传统 32 位 x86 栈帧模型中:
ESP → 当前栈顶
EBP → 当前函数栈帧的基准位置因此函数参数常看到:
[ebp+8]
[ebp+12]而局部变量经常:
[ebp-4]
[ebp-8]直觉上:
参数
↑
返回地址
旧 EBP
← EBP
局部变量
↓
ESP所以:
+ 偏移 → 参数区域
- 偏移 → 局部变量区域对于传统 32 位考试代码,这个模型非常实用。
三十六、数据寻址与数组访问在汇编中的结合
看到:
mov eax, [ebx+ecx*4+8]有效地址:
这通常很像:
a[i]如果数组元素大小为 4B,那么:
所以汇编里的 scale 非常关键。
常见:
1
2
4
8刚好对应:
char
short
int/float
double等常见元素尺度。
三十七、CISC 和 RISC
资料分别标记:
CISC 特点:2017
RISC 特点:2009、2025。
37.1 CISC
CISC:
Complex Instruction Set Computer
核心思想:
让一条指令干很多事情。
典型特征是指令多、指令复杂、指令长度可能不固定、寻址方式多、很多指令可直接访问主存、不同指令执行时间差异较大,传统实现经常采用微程序控制。
37.2 RISC
RISC:
Reduced Instruction Set Computer
核心思想不是单纯“指令少”,而是:
只把高频、简单、规整的操作设计成硬件指令,让复杂功能由简单指令组合实现。
资料总结的典型特征包括:指令数量相对少、定长指令、寻址方式少、只有 LOAD/STORE 访问主存、大量通用寄存器、易采用流水线、通常采用硬布线控制、非常依赖编译器优化。
三十八、LOAD/STORE 是理解 RISC 的关键
例如计算:
RISC 不喜欢直接:
ADD c,a,b让 ALU 一边去主存找 、一边找 。
而是:
LOAD R1, a
LOAD R2, b
ADD R3, R1, R2
STORE c, R3也就是:
内存
↓ LOAD
寄存器
↓ ALU 运算
寄存器
↓ STORE
内存因此:
访存和运算分离。
资料也特别把这一点作为 LOAD/STORE 架构的核心特征。
三十九、CISC/RISC 高频辨析
| 特征 | CISC | RISC |
|---|---|---|
| 指令数量 | 多 | 较少 |
| 指令复杂度 | 高 | 简单 |
| 指令长度 | 常不固定 | 通常固定 |
| 寻址方式 | 多 | 少 |
| 访存 | 很多指令可访存 | 通常仅 LOAD/STORE |
| 通用寄存器 | 相对较少 | 多 |
| 控制器 | 传统常微程序 | 常硬布线 |
| 流水线 | 较难规整 | 非常适合 |
| 编译优化 | 相对困难 | 很重要 |
| 一条指令功能 | 强 | 简单 |
但一个经典陷阱是:
“CISC 不能流水线”——错误。
现代 CISC 同样大量采用流水线,只是译码和指令边界处理比 RISC 更复杂。
资料的模拟题也专门用这个选项设过陷阱。
四十、本章最重要的公式整理
寻址方式最重要的几条:
直接寻址:
间接寻址:
寄存器间接:
相对寻址:
基址寻址:
变址寻址:
寄存器编码位数:
位固定操作码最多指令数:
位有符号相对位移:
四十一、408 做这章题目的统一解题流程
这章看起来知识点很多,但真正做题可以统一成一个思维模式:
- 先判断题目究竟在问“值”还是“地址”。
看到:
mov eax,[ebx]要知道这是取值。
看到:
lea eax,[ebx]要知道这是算地址。
- 寻址题永远先算 EA。
不要一上来就找操作数。
先:
再:
- 相对寻址先更新 PC。
通常:
再:
- 机器码题先判断大小端。
逻辑值和内存中的字节顺序是两回事。
- 结构体题先做地址布局,再处理大小端。
“边界对齐”解决的是:
字段在哪。
“小端/大端”解决的是:
一个字段内部各字节怎么放。
这两个千万不要混。
- 汇编题先画控制流,而不是逐句翻译。
先找到:
cmp/test
jxx
jmp
call
ret
label再还原:
if
while
for
函数调用- 扩展操作码题逐级算“剩余前缀”。
不要背结论,只需反复做:
四十二、应重点达到什么程度
学完这一章,如果下面这些事情能够独立完成,基本就到 408 要求了:
看到一个指令格式,能算操作码位数、寄存器编号位数、最大指令数量;看到扩展操作码题,能逐层计算二地址、一地址、零地址数量;看到任何寻址方式,能立刻写出 ;看到相对寻址,能想到“取指后的 PC”;能一句话区分基址和变址;能根据地址表还原大小端;能完成结构体边界对齐;看到 Intel 汇编能正确判断目的操作数和源操作数;能区分 mov 与 lea、cmp 与 test;能根据 jle/jbe 判断有符号和无符号;能从汇编中的跳转结构还原 if/for/while;能解释 call/ret 和返回地址;最后能准确区分 CISC 与 RISC。
如果只选本章最值得反复做题的四块,就是:
扩展操作码 → 相对/基址/变址寻址 → 大小端/边界对齐 → C 与汇编代码互译。
这四部分基本也是本章最容易和第 2、3、5 章形成综合题的地方。