指令

指令,即是由处理器指令集架构定义的处理器的独立操作,这个操作一般是运算、存储、读取等。一个指令在CPU中真正的存在形式是高低电平,也可以理解为由01序列组成的机器码。汇编指令只是指令的一种表示形式而已,其实质是一样的。

计算机执行一条指令的主要步骤包括如下四步:1. 取址,2. 译码,3. 执行,4. 回写

格式

一般来说,指令的格式如下:

1
  指令名 操作数1, 操作数2, 操作数3

不过,也有如下的指令格式,一般用于存取类指令:

1
  指令名 操作数1, 操作数3(操作数2)

操作数,即指令操作所作用的实体,可以是寄存器、立即数或标签,每个指令都有其固定的对操作数形式的要求。而标签最终会由汇编器转换为立即数。所谓立即数,即在指令中设定好的常数,可以直接参与运算,一般长度为16位二进制。而标签,用于使程序更简单清晰。标签用于表示一个地址,以供指令来引用。一般用于表示一个数据存取的地址(类似于数组名)、或者一个程序跳转的地址(类似于函数名,或者C语言中goto的跳转目标)。

注意:在MARS中,跳转指令只能使用标签来进行跳转,不能使用立即数!

机器码指令

CPU 可以直接识别机器码指令,从而去完成相应的操作。在我们学习的MIPS汇编中,所有的指令长度均为32位,即4字节,或者说1字。同时,从硬件的角度来讲,每条指令的执行周期大多为1个CPU周期。机器码就是CPU最基本的一种操作,也是原子操作,不可被打断。

所有指令长度均相同,这是精简指令集(RISC)的特征,这种指令集包括MIPS和手机中常用的ARM等;与之相对的是复杂指令集(CISC),包括PC中常用的x86架构,这种指令集的特点是指令数目多、指令长度并不完全相同。

指令格式

32位的机器码需要一定的格式才能被理解。一般来说,在MIPS指令集中,指令分为三种格式:R型、I型和J型。

  • R型指令

R型指令的操作数最多,一般用于运算指令。例如add、sub、sll等。其格式如下(左侧为高位,右侧为低位):
iamge

  • I型指令

I型指令的特点是有16位的立即数(偏移也是一样的道理)。因此,I型指令一般用于addi、subi、ori等与立即数相运算的指令(这里需要注意:在写汇编语言的时候,需要使用负号来标记负数,而不要和机器码一样认为首位的1就代表负数),或beq、bgtz等比较跳转指令,因为它们要让两个寄存器的值相比并让PC偏移offset这么多,刚好利用了全部的字段。还有存取指令,例如sw、lw,它们在使用时需要对地址指定一个偏移值,也会用到立即数字段。

image

  • J型指令

常见的为j和jal。他们需要直接跳转至某个地址,而非利用当前的PC值加上偏移量计算出新的地址,因此需要的位数较多。

严格来说,并非所有的指令都严格遵守上面三种格式,有的如eret、syscall指令一样没有操作数;有的如jalr指令一样某些字段被固定为某个值。

1
2
3
4
5
6
7
8
9
10
11
12
13
·  op:也称opcode、操作码,用于标识指令的功能。CPU需要通过这个字段来识别这是一条什么指令。不过,由于op只有6位,不足以表示所有的MIPS指令,因此在R型指令中,有func字段来辅助它的功能。

· func: 用于辅助op来识别指令。

· rs、rt、rd: 通用寄存器的代号,并不特指某一寄存器。范围是$0~$31,用机器码表示就是00000~11111。

· shamt:移位值,用于移位指令。

· offset:地址偏移量。

· immediate:立即数。

· address:跳转目标地址,用于跳转指令。

扩展指令

扩展指令的功能主要是简化程序。汇编器将一些常用、但标准指令集不提供的功能封装为一条指令;或者改变现有指令的操作数的形式或个数,使其以新的形式出现。需要注意的是,它们只是形式上是一条新指令,而实际上,在汇编器将其汇编之后,还是使用标准指令来实现的。

伪指令

伪指令(Directives)是用来指导汇编器如何处理程序的语句,有点类似于其他语言中的预处理命令。伪指令不是指令,它并不会被编译为机器码,但他却能影响其他指令的汇编结果

  • .data:用于预先存储数据的伪指令的开始标志。
  • .text:程序代码指令开始的标志。
  • .word:以字为单位存储数据。
  • .asciiz:以字节为单位存储字符串。(.asciiz会以’\0’作为结尾,作为字符串形式输出)
  • .space:申请若干个字节的未初始化的内存空间。