要计算机服从指挥, 就必须用计算机的语言。计算机语言中的基本单词称为指令,一台计算机的全部指令称为该计算机的指令集

  • 精简指令集(RISC)
  • 复杂指令集(CISC)
MIPS汇编不同于x86汇编,属于精简指令集,常见于路由器等一些嵌入式设备中。MIPS汇编中保留了32个通用寄存器。
之前的文章中的汇编语言是基于8086的,在计算机组成与设计这个分类将会学习MIPS

运算操作

两个来源和一个目的地,所有的算术运算都有这种形式

指令示例含义注释
加法add $s1,$s2,$s3$s1=$s2+$s3三个寄存器操作数
减法sub $s1,$s2,$s3$s1=$s2-$s3三个寄存器操作数
立即数加法add $s1,$s2,20$s1=$s2+20用于加常数数据

符合硬件简单性的设计原则,说明了硬件设计三条基本原则的第一条:设计原则1:简单源于规整

  • 简单性倾向于规则性
    -规则性使实现更简单

    • 简洁性使性能更高,成本更低

例子

C code:

f = (g + h) - (i + j);

Compiled MIPS code:

add t0, g, h   # temp t0 = g + h
add t1, i, j   # temp t1 = i + j
sub f, t0, t1  # f = t0 - t1 =(g + h) – (i + j)

寄存器操作数 add sub

与高级语言不同,MIPS算术运算指令的操作数是很严格的,它们必须来自寄存器。
MIPS体系结构中寄存器大小为32位,由于32位为一组的情况经常出现,因此在MIPS体系结构中将其称为字(word)

字: 计算机中的基本访问单位,通常是32位一组,在MIPS体系结构中与寄存器大小相同。

MIPS约定书写指令时用一个$符号后面跟两个字符来表示一个寄存器。

  • 用于经常访问的数据
  • 32位一组,编号为0到31
  • 名称
    $t0, $t1, ..., $t9 用于临时寄存器
    $s0, $s1, ..., $s7用于存储变量

寄存器个数限制为32个的理由可以表示为硬件设计三条基本原则中的第二条:设计原则2:越小越快

例子

C code:

f = (g + h) - (i + j);

将变量f、g、h、i依次分配给寄存器$s0、$s1、$s2、$s3和$s4

Compiled MIPS code:

add $t0, $s1, $s2
add $t1, $s3, $s4
sub $s0, $t0, $t1

存储器操作数 lw sw

在实际应用中除了有上面例子的一两个简单变量,也可能有像数组或结构那样的复杂数据结构。
这些数据元素可能远多于计算机中寄存器的个数。
因此,这类数据是存放在存储器中的。

MIPS的算术运算指令只对寄存器进行操作,因此MIPS必须包含在存储器和寄存器之间传送数据的指令。这些指令叫做 数据传送指令

为了访问存储器中的一个字,指令必须给出存储器地址。
存储器就是一个很大的下标从0开始的一维数组,地址就相当于数组的下标


如图所示,第三个数据元素的地址为2,存放的数据为10

取数 lw

将数据从存储器复制到寄存器的数据传送指令叫取数(load)
取数指令的格式是操作码后接着目标寄存器,再后面是用来访问存储器的常数和寄存器。
常数和第二个寄存器中的值相加即得存储器地址
实际的MIPS取数指令助记符为lw,为load word的缩写

例题


设A是一个含有100个字的数组,编译器仍然将寄存器$s1、$s2依次分配给变量g、h
设数组A的起始地址(或称基址)存放在寄存器$s3中
编译下面的c赋值语句

C code:

g = h + A[8];

因为其中一个操作数在存储器中,所以首先必须将A[8]传送到寄存器。其地址是$s3中的基址加上该元素序号8。
取回的数据应存放在一个临时寄存器中
第一行是一种简化版的描述实际上应该是第二行(4*8=32)

lw  $t0, 8($s3)    # 简化版描述
lw  $t0, 32($s3)    # load word

现在A[8]已取到寄存器$t0中,下一条指令就可对$t0进行操作

add $s1, $s2, $t0

表示:将s2(题目中s2分配给了h)和t0相加,结果给s1

存数 sw

存数指令:将数据从寄存器复制到存储器。
存数指令的格式和取数指令相似:首先是操作码,接着是包含待存储数据的寄存器,然后是数组元素的偏移量,最后是基址寄存器。
实际的MIPS的存数指令为sw,即store word的缩写

例题

假设变量h存放在寄存器$s2中,数组A的基址放在$s3中。试编译下面的C赋值语句:

A[12] = h + A[8];

这个例题中有两个操作数在里面,我们需要先把A[8]取出来,然后把h($s2)和它相加
最后再把结果存储回A[12]

lw  $t0, 32($s3)    # load word
add $t0, $s2, $t0
sw  $t0, 48($s3)    # store word

寄存器 vs. 内存

寄存器的访问速度比内存快
对内存数据进行操作需要加载和存储
需要执行更多的指令
编译器必须尽可能多地使用寄存器中的变量
只有不常用的变量才会溢出到内存中。
寄存器优化很重要!

常数和立即数操作数 addi

程序中经常会在某个操作中使用到常数---例如:将数组的下标加1,用以指向下一个数组元素。

从目前已经介绍的指令来看,如果要使用常数需要先将其从存储器中取出来。
避免使用取数指令的另一种方法,提供其中一个操作数是常数的运算指令。这种有一个常数操作数的快速加法指令叫做加立即数,写成addi
例如:

addi $s3, $s3, 4    # $s3 = $s3 + 4

由于MIPS支持负常数,所以MIPS中没有减立即数的命令。只需使用一个负常数 例如:

addi $s2, $s1, -1

常数0

当你想把一个寄存器里的值移到另一个寄存器里,就要用到$zero
例如:

add $t2, $s1, $zero

可以把这行代码理解为 t2=s1+0

最后修改:2022 年 04 月 03 日
如果觉得我的文章对你有用,请随意赞赏