这是我参与2022首次更文挑战的第6天,活动详情查看:2022首次更文挑战。
大家好,我是程栩,一个即将入职鹅厂的校招新人。本系列文章将会以一个演进的视角介绍计算机组成原理,并以一个采取Mips指令集的CPU作为范例。
Mips指令集
今天开始我们会以实际的指令集、CPU结构、指令运行过程等来进行讲述。
寄存器
首先我们介绍的是在目前我们的架构里所使用到的寄存器以及他们的作用。在我们的架构里,使用的有32个32位寄存器,但是并不是所有的寄存器都可以自由使用,有一些寄存器有他们专门的作用。
| 寄存器 | 别名 | 用途 |
|---|---|---|
| $0 | $zero | 常量0(constant value 0) |
| $1 | $at | 保留给汇编器(Reserved for assembler) |
| 3 | v1 | 函数调用返回值(values for results and expression evaluation) |
| 7 | a3 | 函数调用参数(arguments) |
| 15 | t7 | 暂时的(或随便用的) |
| 23 | s7 | 保存的(或如果用,需要SAVE/RESTORE的)(saved) |
| 25 | t9 | 暂时的(或随便用的) |
| $28 | $gp | 全局指针(Global Pointer) |
| $29 | $sp | 堆栈指针(Stack Pointer) |
| $30 | $fp | 帧指针(Frame Pointer) |
| $31 | $ra | 返回地址(return address) |
在这里,寄存器名$0表示的是0号寄存器,而别名则是在汇编中呈现出来的寄存器。
从用途上看,0号寄存器是一个基础量,我们可以通过对0号寄存器进行加减或者其他运算来获取某些运算数。
而像$v0、$v1这样的寄存器则是在运行过程中需要使用到的,比如当我们需要调用一个函数的时候,我们可以将参数放到寄存器中传参,跳转到对应的地址进行运行,然后在调用函数return的时候进行返回。
也许你曾经听说过程序的运行和栈有关,那么是如何相关联的呢?我们明天会进行相关的说明。
指令分类
从指令分类上,我们可以将指令分为以下三类,分别为R指令、I指令和J指令。
如下图所示:
这是一个R指令的结构图,其中op就是命令,rs、rt、rd分别用五位二进制来表示操作的寄存器,因为我们有32个寄存器,所以指令这里也需要有五位才能表示32个寄存器。这也是指令集作为软硬件接口的体验,其和硬件是强耦合的。
至于shamt和funct,我们到后面会根据具体的指令来进行讲解。我们可以理解成一些指令的附加信息,不是所有指令都需要用到他们两,不用的时候值是多少其实都是无所谓的。因为我们的Control会通过信号屏蔽掉这些值对运行的影响。
我们再来看I指令:
I指令的结构和R指令不同的是,其中有一个16的immediate,可以译作立即数,也就是二进制数,这也是为什么叫I指令的由来。比如有这么一条指令:addi,该指令就是从寄存器中取出一个数然后加上一个常数后再放回某个寄存器中,例如:
addi $t0,$0,10000
这个指令就是将0号寄存器中的值加上10000后放回到$t0中,而我们知道0号寄存器的值总是0,所以$t0寄存器的值就确定了,这也就是为什么会有0号寄存器的原因。
除此之外,I指令还和数据的存储强相关,因为可以用后面的立即数来表示偏移量从而进行寻址。
到这里,我们暂时不介绍J指令,等我们结合架构介绍的时候再说明J指令,其和架构是强相关的。我们目前只需要知道J指令可以分成无条件跳转和有条件跳转两种,前者可以理解成goto,而后者可以理解成if。
我们明天再见。