指令集体系
概述
指令集架构(Instruction Set Architecture, ISA)
- IA-32(x86)
 - MIPS
 - RISC-V
 
中央处理器(Central Processing Unit, CPU)
- IA-32 架构(CISC)
 - MIPS 架构(RISC)
 - RISC-V 架构(RISC)
 - ARM 架构(RISC)
 
指令集体系
- 某台计算机能够执行的机器指令的集合
 - 计算机能够执行的操作和每一步操作涉及的数据
- 运算部件
 - 寄存器、存储器
 
 - 明确了在这台机器上编写软件时所要注意的全部信息
 - 规定了程序员使用机器语言编程时的全部信息
 - 将高级语言编写的程序翻译为机器语言时,ISA 也为翻译器提供了关于该计算机的信息
 - ISA 规定了存储器的组织,寄存器集和指令集,包括操作码,数据类型和寻址模式等
 
寄存器(Register)
- 机器语言和汇编语言不能使用各种类型的变量
 - 寄存器变量没有数据类型
 - 机器语言和汇编语言的操作对象是寄存器
 - 好处:寄存器是最快的数据存取单元
 - 缺陷:寄存器数量有限,需仔细高效的使用
 - MIPS 包括 32 个通用寄存器,字长 32 位,即
 
MIPS 寄存器
- 32 个 32 位通用寄存器 $0~$31
 - 32 个 32 位单精度浮点寄存器 $f0~$f31
 - 2 个 32 位乘、商寄存器 Hi 和 Lo
 - 1 个程序寄存器 PC
 - 无程序状态寄存器
 
| 通用寄存器 | 助记符 | 释义 | 
|---|---|---|
| 0 | $zero | 固定值为 0,硬件置位 | 
| 1 | $at | 汇编器保留,临时变量 | 
| 2~3 | $v0~$v1 | 函数调用返回值 | 
| 4~7 | $a0~$a3 | 函数调用参数 | 
| 8~15 | $t0~$t7 | 暂存寄存器,被调用者按需保存 | 
| 16~23 | $s0~$s7 | 保存寄存器,调用者保存 | 
| 24~25 | $t8~$t9 | 暂存寄存器,同上 | 
| 26~27 | $k0~$k1 | 操作系统保留,中断异常处理 | 
| 28 | $gp | 全局指针(Global Pointer) | 
| 29 | $sp | 栈指针(Stack Pointer) | 
| 30 | $fp | 帧指针(Frame Pointer) | 
| 31 | $ra | 函数返回地址(Return Address) | 
RISV-V 整型寄存器
| Register name | Symbolic name | Description | Saved by | 
|---|---|---|---|
| x0 | Zero | Always zero | - | 
| x1 | ra | Return address | Caller | 
| x2 | sp | Stack pointer | Callee | 
| x3 | gp | Global pointer | - | 
| x4 | tp | Thread pointer | - | 
| x5 | t0 | Temporary/alternate return register | Caller | 
| x6~x7 | t1~t2 | Temporary | Caller | 
| x8 | s0/fp | Saved register/frame pointer | Callee | 
| x9 | s1 | Saved register | Callee | 
| x10~x11 | a0~a1 | Function argument/return value | Caller | 
| x12~x17 | a2~a7 | Function argument | Caller | 
| x18~x27 | s2~s11 | Saved register | Callee | 
| x28~x31 | t3~t6 | Temporary | Caller | 
寄存器数据存储
- 字长:32 位,4 字节
 - 5 位编码识别
 - 左边字节称为高字节
 - 右边字节称为低字节
 - 大端(Big Endian):高位优先
 - 小端(Little Endian):低位优先
 - 存储在每个寄存器中的位数通常是 1 个字(即 32 位)
 
浮点寄存器
- 用于单精度或双精度计算
 - 5 位编码识别
 - 单精度浮点数使用 1 个寄存器
 - 双精度浮点数使用 2 个寄存器
 
MIPS 指令
加减法运算
1  | a = b + c;  | 
对应的 MIPS 汇编指令(a, b, c 对应寄存器 $s0, $s1, $s2;d, e, f 对应寄存器 $s3, $s4, $s5)
1  | add $s0, $s1, $s2  | 
另一个例子
a = b + c + d - e;  | 
对应的 MIPS 汇编指令
1  | add $t0, $s1, $s2 # temp = b + c  | 
和常数相加,如
g += 4; // 4 在内存中  | 
对应的 MIPS 汇编指令
1  | lw $t0, 0($s3) # $s3 = Address(4)  | 
上面的 0($s3) 中,0 是偏移量,$s3 是基址寄存器。
g += 4; // 4 在指令中  | 
对应的 MIPS 汇编指令(加立即数,i 即 immediate)
addi $s0, $s0, 4 # g = g + 4  | 
内存数据访问
g = h + A[8];  | 
对应的 MIPS 汇编指令
1  | lw $t0, 32($s3) # $s3 = Address(A[0])  | 
变址寻址:基址寄存器 + 地址偏移量
上面的例子中,,因为一个字长为 4 字节。
写内存
A[12] = h + A[8];  | 
对应的 MIPS 汇编指令
1  | lw $t0, 32($s3) # $s3 = Address(A[0])  | 
条件判断
1  | if (a == b) {  | 
等效 C 代码
1  | if (a == b) goto L1;  | 
等效 MIPS 汇编指令(beq 即 branch if equal,j 即 jump)
1  | beq $s0, $s1, L1  | 
跳转
条件跳转
1  | beq reg1, reg2, label  | 
无条件跳转
j label  | 
指令集
指令:
- 操作码(Opcode):指令的类型
 - 操作数(Operand):指令的参数
 
指令集是由一组操作码、操作数和寻址模式定义的指令集合。
寻址模式决定了如何计算将要读取/存储的存储单元的地址。
- CISC(Complex Instruction Set Computer):复杂指令集计算机
- 指令系统复杂:变长操作码/变长指令字/指令多/寻址方式多/指令格式多
 - 指令周期长:绝大多数指令需要多个时钟周期才能完成
 - 各种指令都能访问存储器:除了专门的存储器读写指令外,运算指令也能访问存储器
 - 采用微程序控制
 - 有专用寄存器
 - 难以进行编译优化来生成高效目标代码
 
 - RISC(Reduced Instruction Set Computer):精简指令集计算机
- 简化的指令系统:指令少/寻址方式少/指令格式少/指令长度一致
 - 以 RR(register-to-register)方式工作:除 Load/Store 指令
可访问存储器外,其余指令都只访问寄存器 - 指令周期短:以流水线方式工作,因而除 Load/Store 指令外,其他简单指令都只需一个或一个不到的时钟周期就可完成
 - 采用大量通用寄存器,以减少访存次数
 - 采用组合逻辑电路控制,不用或少用微程序控制
 - 采用优化的编译系统,力求有效地支持高级语言程序
 
 
指令分类
表示一条指令的机器字,称为指令字,简称指令。
- 按计算机系统的层次结构分类
- 微指令
 - 机器指令
 - 宏指令
 
 - 按操作数物理位置分类
- 存储器-存储器(SS)型
 - 寄存器-寄存器(RR)型
 - 寄存器-存储器(RS)型
 
 - 按指令长度分类
- 定长指令
 - 变长指令
 
 - 按操作数个数分类
- 四操作数
 - 三操作数
 - 二操作数
 - 单操作数
 - 零操作数
 
 - 按指令功能分类
 
指令长度:指令中包含二进制代码的位数。
- 指令字越长,地址码长度越长,可直接寻址空间越大
 - 指令字越长,占用空间越大,取指令越慢
 
指令寻址方式
- 指令寻址
- 顺序寻址
 - 跳转寻址
 
 - 操作数寻址
- 立即寻址
 - 寄存器寻址
 - 间接寻址
 - 基址/变址寻址
 
 
指令寻址
- 顺序寻址
- 程序对应的机器指令序列按先后顺序依次存放
 - 程序执行时从第一条指令开始,逐条取出并顺序依次执行
 
 - 跳转寻址
- 当程序中出现分支或循环时,就会改变程序的执行顺序
 - 下条指令的地址由指令本身给出
 
 
操作数寻址
- 立即寻址:操作数字段是操作数本身。
 
例如 MOV $s0, 38H(将立即数 38H 移动到寄存器 $s0)

- 寄存器寻址:操作数在 CPU 的内部寄存器中。
 
例如 PUSH $s0(将寄存器 $s0 的内容压入栈)

MIPS 指令集
- 指令格式
- R 型指令
 - I 型指令
 - J 型指令
 
 - 无寻址方式,隐藏在了操作码字段 OP 中
 
R-Type 指令格式(Register)
- op: 操作码
 - rs: 源寄存器
 - rt: 目的寄存器
 - rd: 目的寄存器
 - shamt: 移位量
 - funct: 功能码
 

I-Type 指令格式(Immediate)
- op: 操作码
 - rs: 源寄存器
 - rt: 目的寄存器
 - immediate: 立即数
 

J-Type 指令格式(Jump)
- op: 操作码
 - target address: 目标地址(立即数)
 

R-Type 指令
ADD
| op(10) | rs | rt | rd | shamt | funct(10) | 
|---|---|---|---|---|---|
| 0 | Reg | Reg | Reg | 0 | 32 | 
ADD rd, rs, rt  | 
即 GPR[rd] <- GPR[rs] + GPR[rt][1]。
SUB
| op(10) | rs | rt | rd | shamt | funct(10) | 
|---|---|---|---|---|---|
| 0 | Reg | Reg | Reg | 0 | 34 | 
SUB rd, rs, rt  | 
即 GPR[rd] <- GPR[rs] - GPR[rt]。
AND
| op(10) | rs | rt | rd | shamt | funct(10) | 
|---|---|---|---|---|---|
| 0 | Reg | Reg | Reg | 0 | 36 | 
AND rd, rs, rt  | 
即 GPR[rd] <- GPR[rs] and GPR[rt]。
OR
| op(10) | rs | rt | rd | shamt | funct(10) | 
|---|---|---|---|---|---|
| 0 | Reg | Reg | Reg | 0 | 37 | 
OR rd, rs, rt  | 
即 GPR[rd] <- GPR[rs] or GPR[rt]。
SLL(Shift Left Logical)
即逻辑左移。
| op(10) | rs | rt | rd | shamt | funct(10) | 
|---|---|---|---|---|---|
| 0 | 0 | Reg | Reg | X | 0 | 
SLL rd, rt, shamt  | 
即 GPR[rd] <- GPR[rt] << shamt。
SLT(Set on Less Than)
| op(10) | rs | rt | rd | shamt | funct(10) | 
|---|---|---|---|---|---|
| 0 | Reg | Reg | Reg | 0 | 42 | 
SLT rd, rs, rt  | 
即 GPR[rd] <- (GPR[rs] < GPR[rt])。若 GPR[rs] < GPR[rt],则 GPR[rd] 被置为 1,否则置为 0。
JR(Jump Register)
| op(10) | rs | rt | rd | shamt | funct(10) | 
|---|---|---|---|---|---|
| 0 | Reg | 0 | 0 | 0 | 9 | 
JR rs  | 
即 PC <- GPR[rs][2]。
程序设计中 rs 应使用除了 31($ra) 以外的寄存器。
I-Type 指令
ADDI
| op(10) | rs | rt | immediate | 
|---|---|---|---|
| 8 | Reg | Reg | 16bits 立即数 | 
ADDI rt, rs, immediate  | 
即 GPR[rt] <- GPR[rs] + immediate。immediate 为有符号数。
当结果的二进制补码发生溢出时,rt 不会被修改,结果被丢弃。
v6 中舍弃了
ADDI指令,使用ADDIU代替,因为二者在不溢出时行为相同。
ANDI
| op(10) | rs | rt | immediate | 
|---|---|---|---|
| 12 | Reg | Reg | 16bits 立即数 | 
ANDI rt, rs, immediate  | 
即 GPR[rt] <- GPR[rs] and ZeroExtend(immediate)。immediate 被零扩展为 32 位。
SLTI
| op(10) | rs | rt | immediate | 
|---|---|---|---|
| 10 | Reg | Reg | 16bits 立即数 | 
SLTI rt, rs, immediate  | 
即 GPR[rt] <- (GPR[rs] < SignExtend(immediate))。immediate 被符号扩展为 32 位。
LW(Load Word)
| op(10) | rs | rt | immediate | 
|---|---|---|---|
| 35 | Reg | Reg | 16bits 立即数 | 
LW rt, immediate(rs)  | 
即 GPR[rt] <- Memory[GPR[rs] + SignExtend(immediate)]。将主存(Memory)中 32 位的字取到寄存器 rt 中。
地址由 GPR[rs] + offset 确定,rs 寄存器中的值为基址,immediate 16 位立即数为有符号偏移量,相加后给出有效地址。
LH(Load Halfword)
| op(10) | rs | rt | immediate | 
|---|---|---|---|
| 33 | Reg | Reg | 16bits 立即数 | 
LH rt, immediate(rs)  | 
即 GPR[rt] <- SignExtend(Memory[GPR[rs] + SignExtend(immediate)])。将主存中 16 位的半字取到寄存器 rt 中。
LUI(Load Upper Immediate)
| op(10) | rs | rt | immediate | 
|---|---|---|---|
| 31 | 0 | Reg | 16bits 立即数 | 
LUI rt, immediate  | 
即 GPR[rt] <- immediate << 0(16)。将 16 位立即数取到寄存器 rt 的高 16 位半字中。
实际操作中,是将 16 位立即数左移 16 位,再与 16 个低位的 0 拼接,将拼接后的 32 位数存入寄存器 rt 中。
SW(Store Word)
| op(10) | rs | rt | immediate | 
|---|---|---|---|
| 43 | Reg | Reg | 16bits 立即数 | 
SW rt, immediate(rs)  | 
即 Memory[GPR[rs] + SignExtend(immediate)] <- GPR[rt]。将寄存器 rt 中低 32 位的字存入主存中。
BEQ(Branch on Equal)
| op(10) | rs | rt | immediate | 
|---|---|---|---|
| 4 | Reg | Reg | 16bits 立即数 | 
BEQ rs, rt, immediate  | 
即 if GPR[rs] == GPR[rt] then branch。如果寄存器 rs 和 rt 中的值相等,则发生跳转。
跳转地址为 PC + 4 + (SignExtend(immediate) << 2)。
则 BEQ r0, r0, immediate 为无条件跳转。
使用这样的方式,branch 就为 PC + 4 * (immediate + 1)。
BNE(Branch on Not Equal)
| op(10) | rs | rt | immediate | 
|---|---|---|---|
| 5 | Reg | Reg | 16bits 立即数 | 
BNE rs, rt, immediate  | 
即 if GPR[rs] != GPR[rt] then branch。如果寄存器 rs 和 rt 中的值不相等,则发生跳转。
J-Type 指令
J(Jump)
| op(10) | target address | 
|---|---|
| 2 | 26bits 立即数 | 
J target  | 
即 PC <- PC[31:28] || target || 00。
跳转地址为,26bits 立即数左移两位,与 PC 的高四位拼接。
JAL(Jump and Link)
| op(10) | target address | 
|---|---|
| 3 | 26bits 立即数 | 
JAL target  | 
即 GPR[31] <- PC + 4 与 PC <- PC[31:28] || target || 00。
将返回地址存入 31($ra) 寄存器,再跳转。
RISC-V 指令集
略。