北航计算机组成原理课程设计-2021秋PreProject-MIPS-MIPS指令集架构
北航计算机学院-计算机组成原理课程设计-2021秋
PreProject-MIPS
MIPS 指令集架构
本系列所有博客,知识讲解、习题以及答案均由北航计算机学院计算机组成原理课程组创作,解析部分由笔者创作,如有侵权联系删除。
从本节开始,课程组给出的教程中增添了很多视频讲解。为了避免侵权,本系列博客将不会搬运课程组的视频讲解,⽽对于⽂字讲解也会相应地加以调整,重点在于根据笔者⾃⼰的理解给出习题的解析。因此带来的讲解不到位敬请见谅。
认识 MIPS 汇编指令
初识指令
在真正开始掌握 MIPS 汇编指令之前,我们需要先知道,什么是「指令」。指令,即是由处理器指令集
架构(Instruction Set Architecture,可以理解为计算机体系结构中对程序相关的部分所做的定义)定义的处理器的独⽴操作,这个操作⼀般是运算、存储、读取等。⼀个指令在 CPU 中真正的存在形式是⾼低电平,也可以理解为由 01 序列组成的机器码。但因为机器码⼈类难以阅读和理解,所以指令⼀般由汇编语⾔来表⽰,也就是我们俗称的汇编指令。从这个⾓度上来说,汇编指令只是指令的⼀种表⽰形式⽽已,其实质是⼀样的。
⼀条指令的功能较为单⼀,⼀般不具有复杂的逻辑。例如「将某两个寄存器的值相加并存⼊另⼀个寄存器」,或者是「如果某个寄存器的值满⾜某个条件则跳转⾄某条指令」。不过,虽然这些指令很简单,但最终,我们可以⽤它们组合出丰富多彩、功能强⼤的程序。
那么,指令究竟长什么样呢?让我们来⼀起看⼀⼩段汇编程序:
其中第 5 ⾏的 addi $t0, $0, 100 就是⼀条指令,它的含义为“将 $0 寄存器的值加上 100,并将结果存⼊ $t0 寄存器”。
需要注意的是,虽然其主要由指令构成,但汇编语⾔并⾮全部由指令组成。上⾯的代码中,第 5-14、16-17 ⾏为严格意义上的指令,其余有标签、伪指令等。相信看了上⾯⼀段代码样例,你已经出了指令的规律,下⾯我们就来详细地进⾏解读。
指令的格式
在 MIPS 汇编语⾔中,指令⼀般由⼀个指令名作为开头,后跟该指令的操作数,中间由空格或逗号隔开。指令的操作数的个数⼀般为 0-3个,每⼀个指令都有其固定操作数个数。⼀般来说,指令的格式如下:
指令名操作数 1, 操作数 2, 操作数 3
不过,也有如下的指令格式,⼀般⽤于存取类指令:
指令名操作数 1, 操作数 3(操作数 2)
所谓操作数,即指令操作所作⽤的实体,可以是寄存器、⽴即数或标签,每个指令都有其固定的对操
作数形式的要求。⽽标签最终会由汇编器转换为⽴即数。所谓⽴即数,即在指令中设定好的常数,可以直接参与运算,⼀般长度为 16 位⼆进制。⽽标签,⽤于使程序更简单清晰。标签⽤于表⽰⼀个地址,以供指令来引⽤。⼀般⽤于表⽰⼀个数据存取的地址(类似于数组名)、或者⼀个程序跳转的地址(类似于函数名,或者 C 语⾔中 goto 的跳转⽬标)。在 MIPS 汇编中(以及其他⼤部分汇编语⾔中),标签⽤如下的⽅式写出:
name:
其中的「name」代表这个标签的名称,可以⾃⾏取名。
常见的指令格式样例:
当然,前⾯说过,可以使⽤标签来代替某个地址,因此也可以以如下⽅式书写:
这⾥的 loop 就是⼀个标签,他所代表的是⼀段代码的起始地址。在进⾏汇编时,汇编器会⾃动把标签转换成我们所需要的⽴即数,这样就不⽤我们⾃⼰去计算这些地址偏移量,简化了编程难度。
注意:在 MARS 中,跳转指令只能使⽤标签来进⾏跳转,不能使⽤⽴即数!
由此可以看出,在 MIPS 汇编语⾔中,操作数的形式并⾮绝对严格固定的,⽽是具有⼀定的灵活度。虽然在 MIPS 标准指令集中,⼀条机器码指令的格式是固定的,但汇编器可以将多种形式的汇编指令转换为同样意思的机器码指令。因此,许多指令有⽐标准写法简单的写法。这部分内容会在后⾯进⾏讲解(详见「扩展指令」),或者也可以⾃⾏查阅 MARS 的帮助⽂档。
为了更好的理解汇编指令,下⼀⼩节将详细讲解 MIPS 机器码指令,这⼀汇编指令转换后的形式。
PS:在本教程中,没有特殊说明的情况下都不需要考虑到延迟槽的存在。
汇编⼊门测试
汇编⼊门测试1
汇编语⾔是_____?
A. 机器语⾔
B. 低级语⾔
C. ⾼级语⾔
D. C语⾔
答案:B
汇编⼊门测试2
指令ori $t0,$0,100具有_____个操作数
答案:3
汇编指令有多少个汇编⼊门测试3
请判断下列说法是否正确:
1 每⼀条指令⾄少有两个操作数?
2 每⼀条指令⾄少需要使⽤⼀个寄存器?(32个基础寄存器)
答案:错;错
汇编⼊门测试4
在mips汇编语⾔中,标签作为指令的操作数,最终会由汇编器转化为:
A. 寄存器
B. ⽴即数
C. 指令
答案:B
机器码指令
认识机器码
⼤家都知道,计算机只能理解⼆进制形式的数据。⽽我们前⾯所说的汇编语⾔,最终就会转化为机器语⾔——也就是机器码指令, CPU 可以直接识别这种机器语⾔,从⽽去完成相应的操作。在我们学习的 MIPS 汇编中,所有的指令长度均为 32 位,即 4 字节,或者说 1 字。同时,从硬件的⾓度来讲,
每条指令的执⾏周期⼤多为 1 个 CPU 周期,这在深⼊学习之后可以更好地理解,这⾥只是阐述⼀个概念。因此机器码就是 CPU 最基本的⼀种操作,也是原⼦操作,不可被打断。
所有指令长度均相同,这是精简指令集(RISC,Reduced Instruction Set Computing)的特征,这种指令集包括 MIPS 和⼿机中常⽤的ARM 等;与之相对的是复杂指令集(CISC,Complex Instruction Set Computing),包括 PC 中常⽤的 x86 架构,这种指令集的特点是指令数⽬多、指令长度并不完全相同。
⼀段汇编语⾔可以转换为⼀段机器码,例如下⾯这段汇编指令:
其转换后的结果为(16 进制):
机器码的指令格式
32 位的机器码需要⼀定的格式才能被理解。⼀般来说,在 MIPS 指令集中,指令分为三种格式:R 型、I 型和 J 型。
R 型指令
R 型指令的操作数最多,⼀般⽤于运算指令。例如 add、sub、sll 等。其格式如下(左侧为⾼位,右侧为低位):
I 型指令
I 型指令的特点是有 16 位的⽴即数(偏移也是⼀样的道理)。因此,I 型指令⼀般⽤于 addi、subi、ori 等与⽴即数相运算的指令(这⾥需要注意:在写汇编语⾔的时候,需要使⽤负号来标记负数,⽽不要和机器码⼀样认为⾸位的 1 就代表负数),或 beq、bgtz 等⽐较跳转指令,因为它们要让两个寄存器的值相⽐并让 PC 偏移 offset 这么多,刚好利⽤了全部的字段。还有存取指令,例如 sw、lw,它们在使⽤时需要对地址指定⼀个偏移值,也会⽤到⽴即数字段。
J 型指令
J 型指令很少,常见的为 j 和 jal。他们需要直接跳转⾄某个地址,⽽⾮利⽤当前的 PC 值加上偏移量计算出,因此需要的位数较多。
需要注意的是,严格来说,并⾮所有的指令都严格遵守上⾯三种格式,有的如 eret、syscall 指令⼀样
没有操作数;有的如 jalr 指令⼀样某些字段被固定为某个值。不过,就⼤部分指令⽽⾔,都可按上⾯三种格式进⾏解释,某些字段被固定也可以按照格式来识别为 R、I、J 中的⼀种,因此这三种格式要着重理解。
解读:
op:也称 opcode、操作码,⽤于标识指令的功能。CPU 需要通过这个字段来识别这是⼀条什么指令。不过,由于 op 只有 6 位,不⾜以表⽰所有的 MIPS 指令,因此在 R 型指令中,有 func 字段来辅助它的功能。
func: ⽤于辅助 op 来识别指令。
s、rt、rd: 通⽤寄存器的代号,并不特指某⼀寄存器。范围是$0~$31,⽤机器码表⽰就是 00000~11111。
shamt:移位值,⽤于移位指令。
offset:地址偏移量。
immediate:⽴即数。
address:跳转⽬标地址,⽤于跳转指令。
结合⼿册读懂指令
下⾯我们结合⽂档《MIPS-C 指令集》来讲解如何读懂⼀条机器码指令的功能与格式。下表为 add 指令的指令详解:
编码:⼆进制指令各字段的值和功能。这⾥可以看出,add 指令的 opcode 为 000000;其后跟随着 rs、rt、rd 三个指⽰寄存器的字段;之后为全 0 的 shamt 字段,最后的 func 字段值为 100000。
格式:汇编指令的书写格式。其中的 rd、rs 和 rt 在真正编写时要替换为具体的寄存器。
描述:指令的功能。GPR 表⽰寄存器堆,在此和中括号括起来的寄存器编号结合起来可以理解为某个寄存器。例如 GPR[rd] 表⽰编号为 rd 的寄存器。add 指令的描述表⽰该指令的功能为将编号为 rs 和 rt 的寄存器的值相加,存⼊编号为 rd 的寄存器中。
操作:指令的具体执⾏过程及细节。 \tt{GPR[rs]31||GPR[rs]}GPR[rs]31∣∣GPR[rs] 表⽰将 rs 号寄存器的 bit31 和 rs 号寄存器原本的值进⾏拼接,组合成⼀个 33 位的数值。进⼀步地,\tt{temp ← (GPR[rs]31||GPR[rs])+
(GPR[rt]31||GPR[rt])}temp←(GPR[rs]31∣∣GPR[rs])+(GPR[rt]31∣∣GPR[rt]) 表⽰将这两个 33 位的数值相加,存⼊⼀个临时变量 temp 中。接着,判断这个临时变量的 bit32 和 bit31 进⾏⽐较,若不相等,则说明出现了溢出,那么就会引发整数溢出异常(关于异常的知识会在以后学到);如果没有发⽣溢出,则会将 temp 的低 32 位存⼊ rd 号寄存器中,作为最终的结果。
⽰例:汇编指令书写样例。这⾥的 rs、rt 和 rd 已经替换为了具体的寄存器。
其他:补充说明。这⾥提到如果不考虑溢出,则 add 与另⼀个指令:addu 等价,这便是指出了 add 的特性:add 会检验计算的结果是否会溢出。在⼀些C语⾔编译器中,加法的计算并不会引发异常。例如计算
\tt{2000000000+2000000000}2000000000+2000000000,得到的结果 -2094967296,说明计算出现了溢出,但并未引发异常。这说明该编译器使⽤的是 addu。
下图更加直观地表⽰了汇编指令转变为机器码指令的过程:

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。