MIP指南(一)

.eqv 为寄存器命名

主要是为了提高代码的可读性
习惯了C语言的函数命名,现在看一堆寄存器的名字就很不习惯

1
2
.eqv n,$s0
li n,10

寄存器功能约定

这个其实我在上一篇也提到了这一点,但我当时的想法只是针对每一个题,规定不同的寄存器的用途,是一个比较初步的想法。

而学长的做法就相对比较成熟了,算是直接将这个想法抽象出来,直接规定哪几个寄存器是做什么的,这样以后无脑用就很方便。

  • for循环变量:$t0,$t1
    • 这里我们特别强调一下,函数调用时要注意这两个寄存器的入栈和出栈,防止发生冲突

  • 验证真值:$t2
  • 恒1变量:$t3
  • 数组地址位移:$t4
  • 数组的数:$t5

宏定义

宏定义的简单使用

这里其实我在第一篇有关COpre的文章里就提到过,但是那个没有这个全面,我也正好试一下学习的新板块功能

宏定义大致分为以下5类:

  • int数字的输入(5)和输出(1)
  • char字符的输入(12)和str字符串的输出(4)
  • 数组位移偏移量的获取
  • 函数的寄存器压栈[push(-4),pop(4)]
  • 结束程序(10)
1
2
3
4
5
6
7
8
9
10
11
.macro getInt(%des)
li $v0,5
syscall
move %des,$v0
.end_macro

.macro printInt(%src)
move $a0,%src
li $v0 ,1
syscall
.end_macro
1
2
3
4
5
6
7
8
9
10
11
.macro getChar(%des)
li $v0,12
syscall
move %des,$v0
.end_macro

.macro printStr(%src)
la $a0, %src
li $v0, 4
syscall
.end_macro
1
2
3
4
5
6
7
8
9
.macro push(%src)
addi $sp,$sp,-4
sw %src,0($sp)
.end_macro

.macro pop(%des)
lw %des,0($sp)
addi $sp,$sp,4
.end_macro
1
2
3
4
5
6
7
8
9
10
11
.macro One_Array(%ans,%i)
sll %ans,%i,2
.end_macro

//Array[M][N] 元素Array[i][j]
.macro Two_Array(%ans,%i,%j,%m)
mult %i,%m
mflo %ans
add %ans,%ans,%j
sll %ans,%ans,2
.end_macro
1
2
3
4
.macro end
li $v0,10
syscall
.end_macro

到这里我们对宏的使用就结束了吗?
当然没有!!!

宏具备以下特性,使其非常适合封装函数

  • 独立的段:可以有自己的.data.text
  • 重载支持:同名宏可以根据参数个数不同实现重载

因此!我们考虑将麻烦的输出、for循环等等封装成宏!

宏定义的进阶使用

print函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
.macro print(%str)
.data
_str:.asiiz %str
.text
la $a0, _str
li $v0,4
syscall
.end_macro

.macro print(%int,%sep)
.data
_str:.asiiz %sep
.text
li $v0,1
move $a0,%int
syscall
la $a0, _str
li $v0,4
syscall
.end_macro
1
2
print("hello")
print($t0,"is $t0")

for循环

通过宏封装,不仅可以实现嵌套循环,而且代码的可读性也会得到显著提高.
原理是将标签作为参数传入宏,从而实现循环的嵌套.

1
2
3
4
5
6
7
8
9
10
11
.macro for(%i,%n,%startLabel,%endLabel)
li %i,0
%startLabel:
bge %i,%n,%endLabel
.end_macro

.macro for_end(%i,%startLabel,%endLabel)
add %reg,%reg,1
j %startLabel
%endLabel:
.end_macro
1
2
3
4
5
for($t0,n,begin1,end1)
for($t1,m,begin2,end2)
#一些操作内容
for_end($t1,begin2,end2)
for_end($t0,begin1,end1)

递归函数调用过程

这里我们直接以Hamilton回路为例

1
2
3
4
5
6
7
8
9
10
.macro dfs(%int)
move $a0,%int#传参
push($t0)
push($t1)
push($ra)
jal dfs_function
pop($ra)
pop($t1)
pop($t0)
.end_macro
1
dfs($0)
参考1 参考2