P2主要是要掌握把C语言程序转化成MIPS的过程,推荐自己写一些宏定义,实现scanfprintfreturn_0等功能,最后程序会非常像一个C语言程序,清晰易读。

还有一个很好用的方法是.eqv,可以自己学一下

.data 
s: .space 100

.macro return_0
li $v0,10
syscall
.end_macro

.macro scanf_s(%n)
la $a0,s
move $a1,%n
li $v0,8
syscall
.end_macro

.macro printf_c(%i)
lb $a0,s(%i)
li $v0,11
syscall
.end_macro

.macro scanf_d(%ans)
li $v0,5
syscall
move %ans,$v0
.end_macro

.eqv i $t0
.eqv n $t1
.eqv N $t2
.eqv k $t3
.eqv l $t4

.text
main:
scanf_d(n)
addi n,n,1
scanf_d(N)
scanf_s(n)

li,i,0
for_i:
beq i,N,for_i_end
#
li,k,0
for_k:
beq k,n,for_k_end
#
lb $t8,s(k)
addi $t7,k,1
lb $t9,s($t7)
bgt $t8,$t9,if_ak_bigger
nop
j if_ak_bigger_end
if_ak_bigger:
#
move l,k
for_l:
beq l,n,for_l_end
addi $t9 l 1
lb $t8,s($t9)
sb $t8,s(l)
addi l,l,1
j for_l
for_l_end:
addi n,n,-1
j for_k_end
#
if_ak_bigger_end:
#
addi k,k,1
j for_k
for_k_end:
#
addi i,i,1
j for_i
for_i_end:

li i,0

while:
lb $t8,s(i)
bnez $t8,while_end
addi $t9,n,-2
beq i,$t9,while_end

addi i,i,1
j while
while_end:

for:
beq i,n,for_end
printf_c(i)
addi,i,i,1
j for
for_end:

return_0
一个小知识点

1字 == 4 字节 == 32 位

if…else

if (a == b)
{
//do something.
}
else if (a > b)
{
//do something..
}
else
{
//do something...
}

对于上述的一个C语言程序的if...else模块,我们将其翻译成MIPS语言如下:

# 为了让程序更像c,我习惯给所有常量都用.eqv声明
.eqv a,$t0
.eqv b,$t1
# 下面是if else语句模块
beq a,b,if_a_is_b
nop
bgt a,b,else_if_a_greater_b
nop
j else

if_a_is_b:
#do something.
j else_end
else_if_a_greater_b:
#do something..
j else_end
else:
#do something...
else_end:

for

for (int i = 0; i < n; i++)
{
// do something...
}

我会用#符号来表示这里是for语句块的开始,即#前后分别是C语言中{}的内容

.eqv i,$t0
.eqv n,$t1

li i,0
for_i:
beq i,n,for_i_end
nop
#
# do something...
#
addi i,i,1
j for_i
for_i_end:

do-while

这种形式比较少见,主要是要和for循环做个区分,别写错了


递归

#include <stdio.h>
void dfs(int n);
int n;
int main()
{
scanf("%d", &n);
dfs(n);
return 0;
}
void dfs(int n)
{
if (n == 1)
{
// do something...
return;
}
dfs(n - 1);
return;
}
.macro return_0
li $v0 10
syscall
.end_macro
.macro scanf_d(%ans)
li $v0 5
syscall
move %ans $v0
.end_macro

.eqv n $t0

.text
main: # main函数

scanf_d(n)

move $s0,n # 传参
jal dfs # dfs(n);
nop

return_0

dfs:
push($ra)
...
push(n)

move n,$s0 # 一般采用$a0-$a3来传参,但在程序中可能有打印模块会改变$a0的值,所以我统一用$s0-$s7来传参数
...

beq n,1,if_n_is_1 # if (n == 1)
nop
j if_n_is_1_end

if_n_is_1:
# do something...
j dfs_end # return;
if_n_is_1_end:

addi $s0,n,-1 # $s0 = n-1 # 传参
jal dfs # dfs(n - 1);
nop

dfs_end:
pop(n)
...
pop($ra)
jr $ra

6.取array[i][j]的值到%d,矩阵的列数为%n(不要搞成行数)

.macro getNum(%d,%i,%j,%n)
mul %d,%i,%n
add %d,%d,%j
sll %d,%d,2
lw %d,array(%d)
.end_macro

常用的(好用的)扩展指令

不需要专门背诵和记忆,在知道他们的英文全称,也就知道了他们的功能。

分支类

beq $t0,-100,label   #beq 第二个数可以是立即数
bne $t0,-100,label #bne 第二个数可以是立即数
bge $t0,$t1,label #if t0 >= t1
bgt $t0,$t1,label #if t0 > t1
ble $t0,$t1,label #if t0 <= t1
blt $t0,$t1,label #if t0 < t1
beqz $t0,label #if t0 == 0
bnez $t0,label #if t0 != 0
#bge,bgt,ble,blt 第二个操作数可以是立即数

运算类

not $t1,$t2       #t1 is not t2
neg $t1,$t2 #t1 is negation of t2
rem $t1,$t2,$t3 #t1 is the reminder of t2/t3 $t3可以换为立即数
div $t1,$t2,$t3 #t1 is t2/t3 (integer division)

存储类

move $t1,$t2      #t1 is t2
li $t1,-100 #t1 is -100
la $t1,label #t1 is address of label
sle $t1,$t2,$t3 #t1 is (t2 <= t3)?1:0
sgt $t1,$t2,$t3 #t1 is (t2 > t3)?1:0
sge $t1,$t2,$t3 #t1 is (t2 >= t3)?1:0
seq $t1,$t2,$t3 #t1 is (t2 == t3)?1:0
# sle,sgt,sge,seq $t3可以换成立即数