「MIPS」汇编程序设计
数据的声明与分配
数据储存空间,指的是内存中的空间
寄存器虽然也可以存储数据,但更重要的功能是临时存储以供计算。
控制流
if-else 结构
1 |
|
for 结构
1 |
|
输入输出
1 |
|
子过程调用
PC需要进行跳转
jal跳入子过程,jr返回父过程
栈的使用
- 过程自身需满足栈的结构
- 过程调用子过程时需满足展的结构
- 子过程执行前后需要移动栈指针
$sp
(分配与释放栈)
具体使用
- 计算好栈帧大小
- 栈指针始终指向栈顶
- 过程开始时分配栈空间
(addiu $sp, $sp, -32)
- 过程结束时回收栈空间
(addiu $sp, $sp, 32)
- 以栈指针为基址进行栈的存取
(sw $to, 24($sp))
递归程序解析
C语言实现阶乘
汇编实现阶乘
factorial部分。最开始的三句,其作用等价于我们在C语言factorial函数中的第一句,即递归终止条件。如果此时$a0
的值为1,那么会将值1写在$v0
寄存器里。意味着1的阶乘值为1。如果$a0
的值不为1,我们跳转到work部分。这部分主要实现的是递归调用。第一句,我们将$t0
的值设置为$a0
,即当前n的值。接下来我们要将当前这个函数中用到的值存入栈中。有用的值有两个,一个是当前n的值,因为我们要利用它进行计算。另一个是当前的$ra
寄存器的值,因为此时的$ra
的值可能在后面被jal
指令再次覆盖,以至于无法跳转到正确的位置。这里需要注意下栈的增长方向,应该是从高到低。所以我们存入一个数之后要将当前$sp
的值减去4,一遍下一个数的存放。接着我们要设置传递给下一次函数的参数。具体来说就是n-1的值。我们将其存入$a0
中。做好所有这些准备工作,我们就可以进行递归了,通过jal
指令再次调用factorial函数。在调用结束后,因为我们巧妙的设计,下一层函数结束后将通过jr
指令跳转这里,并接着执行下一条的内容。之前我们将$t0
和$ra
的值存入到了栈中,相应的,我们这里需要将这些值重新写入。并且恢复栈指针的位置。之后我们计算n与下一层函数返回值的乘积,并且当做新的返回值存入$v0
之中。最后我们通过jr
指令,返回到上一次调用它的地方。
对比下C语言的实现和MIPS汇编的实现。两者的差距主要在于两个部分。第一,C语言中,函数内的临时变量是不需要程序员来通过栈维护的。我们可以理解为,每一层的变量相互之间都是独立的。但是在mips汇编中,我们需要维护这些变量。第二,我们需要维护PC值的变化。C语言中函数结束会自动返回到调用它的位置。但是在mips汇编中,我们需要利用jal
和jr
来实现这个功能。