第四部分:指令系统
4.1 指令系统的基本概念
什么是指令? 指令是计算机执行某种操作(如加法、数据传送、条件跳转)的命令,由二进制编码表示。一台计算机所有指令的集合称为指令集或指令系统。
指令格式 一条指令通常由两部分组成:操作码 和 地址码(也称操作数地址)。
- 操作码:指明要执行的操作(如加法、减法、读内存)。
- 地址码:指明操作数所在的地址(寄存器编号、内存地址、立即数等)。
指令字长 一条指令的二进制位数。可以定长(如32位),也可以变长(如x86的1-15字节)。定长指令便于硬件译码,变长指令节省空间。
操作码的编码方式
- 定长操作码:所有指令的操作码位数相同,译码简单但指令数量有限。
- 扩展操作码(变长操作码):操作码的位数随指令类型变化,在地址码中腾出空间给操作码,可以支持更多指令。考研中常考扩展操作码的设计(例如:15条三地址指令,若干二地址指令,如何安排编码)。
扩展操作码设计完整示例: 假设指令字长为16位,有4位用于操作码,12位用于地址码(可分成3个4位的地址段)。要支持以下指令:
- 15条三地址指令
- 12条二地址指令
- 若干一地址指令
- 若干零地址指令 问:最多可以有多少条一地址指令?最多可以有多少条零地址指令?
解:
- 15条三地址指令操作码:0000~1110(用4位,共16种编码,留1种做扩展)
- 剩下一种三地址编码1111用于扩展:
- 三地址指令占用所有12位地址码(3x4位),所以扩展位从地址码中借。
- 对于二地址指令,用2个4位做地址码,剩下4位扩展操作码。
- 在1111后面加4位扩展,得到二地址指令操作码:1111 0000~1111 1111共16种。
- 12条二地址指令用掉1111 0000
1111 1011(12种),剩下1111 11001111 1111(4种)用于再次扩展。 - 对于一地址指令,在二地址基础上再扩展4位(地址码只剩1个4位段):
- 每种剩余的二地址编码可扩展16条一地址指令。
- 4种剩余编码 x 16 = 64条一地址指令。
- 假设用掉63条,剩下1种继续扩展。
- 对于零地址指令,在剩余的一地址编码基础上再扩展4位:
- 1种 x 16 = 16条零地址指令。
所以:最多63条一地址指令 + 16条零地址指令(如果先满足一地址再满足零地址)。
地址码的个数(按地址码数量分类)
- 三地址指令:
ADD R1, R2, R3表示 R1 <- R2 + R3。 - 二地址指令:
ADD R1, R2表示 R1 <- R1 + R2。常见于通用计算机。 - 一地址指令:
ADD R1表示 AC <- AC + R1(其中AC是累加器)。常见于早期或简单CPU。 - 零地址指令:
RET(返回)等,操作数隐含在堆栈或特定寄存器中。
💡 记忆技巧:
- 扩展操作码设计的核心思想:"操作码向地址码方向扩展"。先把短的地址码(三地址)分配完,留一个编码做扩展前缀,然后继续分配更长的操作码。
- 扩展原则:"短操作码不能是长操作码的前缀"(无歧义编码)。
⚠️ 易错点:
- 扩展操作码设计时,注意是从短操作码向长操作码扩展,而不是反过来。
- 三地址指令使用了全部地址码位,但扩展操作码需要使用某些地址码位来扩展,所以留出编码作为前缀。
4.2 寻址方式
寻址方式是指令中如何指明操作数的位置。掌握各种寻址方式,对于理解汇编程序和指令执行过程非常重要。
常见的寻址方式(以典型的RISC或CISC为例)
-
立即寻址
- 操作数直接包含在指令中(作为地址码部分)。
- 例如:
ADD R1, #5表示 R1 <- R1 + 5。 - 优点:不需要访问内存或寄存器,速度快。缺点:立即数的范围有限(受指令中地址码位数限制)。
-
直接寻址
- 指令中给出操作数所在主存单元的地址。
- 例如:
LOAD R1, 1000表示 R1 <- M[1000]。 - 优点:简单。缺点:地址位数固定,寻址范围有限;指令中地址固定,不易实现程序浮动(相对寻址可解决)。
-
间接寻址
- 指令中给出的地址不是操作数本身,而是操作数地址的地址。
- 例如:
LOAD R1, (1000),先访问M[1000]得到地址A,再访问M[A]得到操作数。 - 优点:可扩大寻址范围(通过多级间接)。缺点:速度慢(多次访存)。现代CPU很少使用。
-
寄存器寻址
- 操作数存放在寄存器中,指令中给出寄存器编号。
- 例如:
ADD R1, R2表示 R1 <- R1 + R2。 - 优点:速度快(不需要访存),指令短。缺点:寄存器数量有限。
-
寄存器间接寻址
- 寄存器中存放操作数的内存地址,指令中给出寄存器编号。
- 例如:
LOAD R1, (R2)表示 R1 <- M[R2]。 - 优点:地址可动态变化(修改寄存器内容即可),支持指针操作。
-
相对寻址
- 操作数地址 = 程序计数器(PC) + 指令中给出的偏移量。
- 常用于转移指令(如
BEQ label,偏移量相对于下一条指令的地址)。 - 优点:实现位置无关代码,便于程序在内存中浮动。
-
基址寻址
- 操作数地址 = 基址寄存器(如R2) + 指令中给出的偏移量。
- 常用于访问数据段或数组(基址指向数组首地址,偏移量为索引)。
- 基址寄存器内容由操作系统或用户程序设定,偏移量由指令给出(可以是立即数)。
-
变址寻址
- 操作数地址 = 变址寄存器(如R2) + 指令中给出的基地址(或相反)。
- 与基址寻址类似,但变址寄存器常用于循环中自动递增(变址寄存器内容可修改)。
- 区别:基址寻址中基址寄存器一般不变,偏移量可变;变址寻址中变址寄存器可变,基地址固定。实际应用中常混合使用。
-
堆栈寻址
- 操作数隐含在堆栈中,由堆栈指针(SP)指向栈顶。
- 例如:
PUSH R1将R1压栈;POP R2弹出栈顶到R2。 - 常用于函数调用、中断保存现场。
寻址方式计算综合示例: 假设某计算机的寄存器值和内存内容如下:
- PC = 1000(当前指令地址)
- R1 = 2000
- R2 = 3000
- M[2000] = 5000
- M[3000] = 4000
- M[5000] = 100
- M[1500] = 200
现在有一条指令位于地址1000,指令内容是 LOAD R1, addr,其中地址字段的值是1500。分别用以下寻址方式求操作数:
- 立即寻址:操作数 = 1500(指令中的地址字段就是操作数本身)。
- 直接寻址:操作数 = M[1500] = 200(访问内存地址1500)。
- 间接寻址:先取M[1500]=200,再访问M[200]=? 但M[200]未定义。注意这里地址字段是1500,M[1500]=200,但200可能不是有效地址。正确的间接寻址应该是:M[1500]=200,然后访问M[200]。但实际上问题中是给的"地址字段值=1500",所以一间接:M[1500]=200。如果题目改为地址字段=2000,则操作数=M[M[2000]]=M[5000]=100。
- 寄存器寻址:操作数 = R1 = 2000(R1的内容)。
- 寄存器间接寻址:操作数 = M[R1] = M[2000] = 5000。
- 相对寻址:操作数地址 = PC + 偏移 = 1000 + 1500 = 2500,操作数 = M[2500](未定义)。
- 基址寻址(假设基址寄存器为R2):操作数地址 = R2 + 偏移 = 3000 + 1500 = 4500,操作数 = M[4500](未定义)。
⚠️ 相对寻址的易错点:
- PC值在取指后已经更新为下一条指令的地址(如PC+指令长度)。如果考试中给出的偏移是"相对于当前指令"还是"相对于下一条指令",需要根据题目说明判断。
- 例如:PC=1000,指令长4字节,偏移量为+50,则有效地址 = 1004 + 50 = 1054(而不是1000+50=1050)。
寻址方式在指令中的编码 通常指令中会有一个"寻址方式字段"指示使用哪种寻址方式,然后跟对应的地址码(寄存器编号、偏移量等)。
💡 记忆技巧:
- 九种寻址方式分类记忆:
- 数据在哪里?立即(在指令中)、寄存器(在寄存器中)、直接(地址在指令中)
- 地址在哪里?寄存器间接(地址在寄存器中)、间接(地址在内存中)
- 地址怎么算?相对(PC+偏移)、基址(基址寄存器+偏移)、变址(变址寄存器+偏移)
- 特殊:堆栈寻址(栈顶隐含)
- 基址 vs 变址的区别:基址中的基址寄存器"基本不变",变址寄存器"经常变化"。记住:"基不变,变常变"。
4.3 指令类型
按功能分类,指令通常包括以下几类:
-
数据传送类指令
- 功能:将数据从源位置复制到目的位置(不改变数据内容)。
- 常见:
MOV、LOAD(从内存读寄存器)、STORE(从寄存器写内存)、PUSH/POP(堆栈操作)。
-
算术逻辑运算类指令
- 算术:
ADD、SUB、MUL、DIV(注意是否区分整数/浮点)。 - 逻辑:
AND、OR、XOR、NOT。 - 移位:
SHL、SHR、SAR(算术右移)、ROL(循环左移)。 - 比较:
CMP(通常只影响标志位,不保存结果)。
- 算术:
-
移位指令(也可归入逻辑类,但单独列出)
- 逻辑左移、逻辑右移、算术右移、循环移位。
-
转移类指令(改变程序执行顺序)
- 无条件转移:
JMP(直接跳转到目标地址)。 - 条件转移:
BEQ(相等则转移)、BNE、BGT、BLT等,根据标志位(ZF、SF、OF等)决定是否跳转。 - 子程序调用与返回:
CALL(保存返回地址)、RET(恢复返回地址)。 - 中断返回:
IRET(恢复中断前的状态)。
- 无条件转移:
-
输入输出类指令
- 专用I/O指令:
IN(从I/O端口读)、OUT(写I/O端口),用于独立编址的I/O系统。 - 内存映射I/O:使用普通的数据传送指令访问I/O设备(因为I/O端口映射到内存地址空间)。
- 专用I/O指令:
-
特权指令与系统控制指令
- 只能在操作系统内核态执行,如修改页表、关闭中断、停机
HLT等。
- 只能在操作系统内核态执行,如修改页表、关闭中断、停机
条件转移与标志位关系示例:
假设执行 CMP R1, R2(比较R1和R2,执行R1-R2,只设置标志位):
- 若R1 = R2,则 ZF=1
- 若R1 > R2(有符号比较),则 SF=OF(正-负得正时无溢出,负-正得负时无溢出... 实际处理器用 SF XOR OF = 0 判断大于)
- 条件转移
BGT label(大于则跳转)会根据SF和OF的组合判断是否跳转。
4.4 CISC与RISC
这是指令系统设计的两大流派,考研中常考对比。
CISC(Complex Instruction Set Computer)
- 特点:指令数量多、功能复杂(一条指令可以完成多个低级操作,如字符串复制)。
- 指令格式:变长,寻址方式丰富。
- 寄存器数量较少(因为很多操作可以直接在内存中完成)。
- 大量使用微程序控制(因为指令复杂)。
- 典型代表:x86(Intel/AMD)。
- 优点:编译后代码密度高(程序占用内存少);高级语言编程容易(一条指令对应复杂功能)。
- 缺点:指令执行速度慢(复杂指令需要多个时钟周期);硬件设计复杂,功耗高;不利于流水线(指令长度和功能差异大)。
RISC(Reduced Instruction Set Computer)
- 特点:指令数量少、功能简单(每条指令只完成一个基本操作,如
LOAD、ADD、STORE)。 - 指令格式:定长(如32位),寻址方式少(通常只有立即、寄存器、寄存器间接、基址偏移等)。
- 寄存器数量多(32个或更多),以便暂存中间结果。
- 采用硬布线控制(指令简单,可以用组合逻辑快速译码)。
- 典型代表:MIPS、ARM、RISC-V。
- 优点:执行速度快(大多数指令在一个时钟周期内完成),硬件简单,易于实现流水线和高性能。
- 缺点:代码密度低(完成同一任务需要更多条指令);对编译器要求高(需要优化寄存器分配)。
计数示例:同一任务在CISC和RISC上的表现
任务:计算 A = B + C,其中B和C在内存中。
CISC(x86):
ADD [A], [B], [C] // 1条指令,但需要多个时钟周期
RISC(MIPS):
LOAD R1, B // 1周期
LOAD R2, C // 1周期
ADD R3, R1, R2 // 1周期
STORE R3, A // 1周期
共4条指令,但每条指令都是单周期(理想流水线下,总时间甚至可能比CISC短)。
对比表格
| 特性 | CISC | RISC |
|---|---|---|
| 指令数量 | 多(几百条) | 少(几十条) |
| 指令长度 | 变长(1-15字节) | 定长(如32位) |
| 寻址方式 | 丰富(十几种) | 简单(几种) |
| 寄存器数量 | 较少(如8-16个) | 较多(32个以上) |
| 控制方式 | 微程序为主 | 硬布线为主 |
| 执行周期 | 每条指令多个周期 | 大多数指令单周期 |
| 代码密度 | 高 | 低 |
| 典型架构 | x86 | ARM、MIPS、RISC-V |
现代趋势:CISC内部实际上使用RISC内核(如x86微指令转换),两者界限逐渐模糊。但考研中仍要求掌握经典区别。
CISC vs RISC 深度理解:可以把CISC想象成"多功能瑞士军刀"——一把刀解决很多问题,但使用起来可能不太顺手;RISC像"一套专业厨具"——每个工具只做一件事,但配合起来效率极高。
💡 记忆技巧:
- CISC = Complex(复杂),RISC = Reduced(精简)。
- CISC特点关键词:多、长、少、微、慢、高(指令多、格式长、寄存器少、微程序、速度慢、代码密度高)。
- RISC特点关键词:少、定、多、硬、快、低(指令少、定长、寄存器多、硬布线、速度快、代码密度低)。
跨学科联系(操作系统):CISC/RISC的选择影响操作系统的设计。例如,x86(CISC)提供了丰富的特权指令和复杂的中断机制,而ARM(RISC)则更依赖操作系统软件来处理复杂操作。在嵌入式系统和移动设备中,RISC因其低功耗优势占据主导地位。
4.5 指令系统在408中的常见考点
- 扩展操作码的设计
- 给定指令字长和地址码位数,设计不同地址数指令的编码,使得没有重叠。
- 解题思路:从短地址码(或零地址)开始分配,利用未使用的操作码模式作为前缀扩展。
扩展操作码考题示例: 某计算机指令字长16位,采用扩展操作码技术。地址码为4位。已设计出10条三地址指令、15条二地址指令、20条一地址指令,问最多还可设计多少条零地址指令?
解:
- 操作码共16位,三地址指令使用4位操作码(剩余12位为地址码)。
- 4位操作码最多16种编码,10条三地址指令用掉10种,剩余6种。
- 用剩余6种中的每种,向地址码扩展4位作为操作码(二地址指令操作码8位)。
- 6x16=96种可能的二地址编码,用掉15条,剩余81种。
- 用剩余81种中的每种,再向地址码扩展4位(一地址指令操作码12位)。
- 81x16=1296种可能的一地址编码,用掉20条,剩余1276种。
- 用剩余1276种中的每种,再扩展4位(零地址指令操作码16位)。
- 1276x16=20416条零地址指令。
-
寻址方式的计算
- 给定指令、寄存器初始值、内存内容,求指令执行后的结果。
- 例如:相对寻址中的偏移量计算(注意PC是否已更新)。
-
CISC与RISC的对比(简答题或选择题)
-
指令执行对标志位的影响(结合第五部分CPU内容)
📌 408考点提示
考查形式:
- 选择题高频:各种寻址方式的计算结果、CISC和RISC的区别、指令格式的组成。
- 综合题常考:扩展操作码的设计(如2012年、2017年408真题)。
- 简答题:CISC和RISC的对比表格题。
常见命题模式:
- 给出指令字长、地址码位数、各类指令数量,要求设计扩展操作码(或计算最多能支持多少条指令)。
- 给出各寄存器的值和内存内容,用不同寻址方式计算操作数或有效地址。
- 判断某条指令属于什么类型的寻址方式(如基址寻址 vs 变址寻址的区别)。
学生常犯错误:
- 扩展操作码设计中,忘记已有指令占用的编码数量,导致后续扩展的基数计算错误。
- 相对寻址的有效地址计算中,忘记PC在取指后已经更新(指向下一条指令)。
- 基址寻址和变址寻址混淆,考试时注意题目对寄存器的命名(通常base指基址,index指变址)。
备考建议:
- 扩展操作码是高频考点,务必练习3~5道不同类型的题目。
- 寻址方式的计算题,建议先把每种寻址方式的地址计算公式写出来,再代入数值。
- CISC vs RISC对比表必须能完整默写(至少列出6个对比维度)。
第四部分小结
| 主题 | 核心要点 |
|---|---|
| 指令格式 | 操作码 + 地址码;定长/变长;三/二/一/零地址 |
| 寻址方式 | 立即、直接、间接、寄存器、寄存器间接、相对、基址、变址、堆栈 |
| 指令类型 | 数据传送、算术逻辑、移位、转移、I/O、特权 |
| CISC vs RISC | 指令数量、长度、寻址方式、寄存器数、控制方式、执行速度、代码密度 |