在阅读本指导书前,需要你先下载 MARS 软件并配置 java 运行环境,具体方式可以查看 PDF 版指导书《MARS 使用教程》。
如果你对于 MIPS 汇编是完完全全的零基础,那么,欢迎阅读本指导书,尽力使用最为朴实的语言、最为细致的样例,突出方法,尽可能少的涉及原理,手把手教你写汇编程序。
入门篇
MIPS 的指令非常多,指导书当中也只会列举出完成实验常用的指令。
在课件下载区新增了《MIPS32-Quick Reference》,是常用 MIPS 指令的快速查询手册。

MIPS 汇编编写的主要的一个难点就是,一次只能做一个运算,但是会用到好几个寄存器,稍微复杂一点就乱了。我的建议是对每一个使用到的寄存器,都做上注释防止出现混乱。
另外,希望大家在写 MIPS 汇编的时候,多多参考 MARS Help 当中的内容。

赋值
在 C 语言中,我们最先用到的便是给变量赋值,这个在 MIPS 中该如何实现呢?
下面这条指令的意思,就是将 t1 寄存器的值赋值为 100,其实际含义是给 $t1 赋值为 100 和 0 进行按位或运算后得到的结果。
ori $t1, 100
当然了,我们也可以使用扩展指令(伪指令)li(load immediate),下面这条指令的意思,便是直接将 $t1 寄存器赋值为 100,该指令与上一条指令的效果完全一致。
基础篇
一次简单的 if 条件判断
在 C 语言中,常用 if 结构,在 MIPS 中如何实现呢?
考虑下面的代码:
if (a > 5) {
// do A;
}
// do B;
我们不得不提一个叫做 ble 的指令,如下图,其含义是若 $t1 寄存器的值小于等于 5,则跳转到 label 标签处;否则,执行 ble 的下一条指令。
ble $t1, 5, label
所以我们需要给源程序打上标签:
if (a > 5) {
// do A;
}
// label:
// do B;
经过这样打标签之后,程序的逻辑就变成了:
如果 a 的值小于等于 5,就跳转到 label 的位置,执行 B 部分
敲门篇
MIPS 体系结构中的寄存器
MIPS 结构体系当中定义了 32 个寄存器
通用寄存器标志由 $ 符号开头,通常有两种使用的方式:
直接使用对应的寄存器的名称,例如 $t0、$t1、$sp 等。
直接使用对应的寄存器的编号,例如 $0、$1、$31 等。
每个寄存器都有其特定的作用,有相应的规范,如下表所示。
寄存器编号 助记符 用途 0 zero 值总是为 0 1 at (汇编暂存寄存器)一般由汇编器作为临时寄存器使用。我们编写 MIPS 汇编不使用。 2 - 3 v0 - v1 用于存放表达式的值,或函数的整型、指针型的返回值 4 - 7 a0 - a3 用于函数传参。其值在函数调用的过程中不会被保存。若函数参数较多,多出来的部分通常会使用栈进行传递。 8 - 15 t0 - t7 用于存放表达式的值的临时寄存器。其值在函数调用的过程中不会被保存。 16 - 23 s0 - s7 保存寄存器。这些寄存器中的值在经过函数调用之后不会改变。 24 - 25 t8 - t9 用于存放表达式的值的临时寄存器。其值在函数调用的过程中不会被保存。当调用位置无关函数时,25 号寄存器必须存放被调用函数的地址。 26 - 27 k0 - k1 仅被操作系统使用。 28 gp 全局指针和内容指针。 29 sp 栈指针。 30 fp 或 s8 保存寄存器(同 s0 - s7)。也可用作帧指针。 31 ra 函数返回地址。
进阶篇
函数
虽然前面有宏定义,但是 MIPS 本身还是有函数的使用的。
宏定义和函数的关系就像是 C 语言当中 #define 关键字 和 函数 的关系一样。
首先,我们来学习 jal(jump and link)指令。
jal 指令的意思是跳转到对应标签的位置,并将当前执行的指令位置存储在 $ra 寄存器中。
前面说过 $a0 - $a3 寄存器的作用是函数传参,意味着如果在 jal 指令调用之前,往 $a0 - $a3 寄存器当中存放了数据,那么 jal 指令调用之后,这几个寄存器的值是可以直接拿出来用的。
既然 $ra 寄存器当中存放了调用之前的执行的位置,当函数调用结束后可使用 jr(jump register)指令:jr $ra ,返回(return)这个函数,函数的返回值存储在 $v0 和 $v1 寄存器中。(MIPS 甚至可以有两个返回值)
高级篇
前面我们已经学习了 MIPS 汇编的基本使用。
下面我们在此基础上回顾一下 MIPS 汇编,同时讲一些高级一点的用法。
宏定义
有的同学可能会说,这个输入输出每次都要写好几条指令才能做到,但是每次写起来都是一样的,有没有更方便的方法去完成这件事呢?
当然有,我们隆重向大家介绍宏定义。
宏定义需要使用 .macro 和 .end_macro 进行包裹。
看下面的例子:
.macro RI(%i)
li $v0, 5
syscall
move %i, $v0
.end_macro
首先阅读一下中间三行 MIPS 代码:
$v0 寄存器的值设置成 5,然后 syscall 系统调用,这个时候程序会进行读入整数的操作。
然后,将读到 $v0 寄存器的值移动到 %i。