MIPS小总结
MIPS
读入输出
字符串
输出
.ascii
与.asciiz
.ascii
不会在字符串后加上'\0'
,而.asciiz
会在字符串加'\0'
。两者均以字节为单位存储数据,这会对我们带来一些小麻烦,.asciiz
之后分配的空间首地址有可能无法字对齐,因此我们在定义.ascii
与.asciiz
时尽量写在最后面
#正确写法
.data
array_int: .space 28
space: .asciiz " "
#错误示范
.data
space: .asciiz " "
array_int: .space 28
#由于.data后面的变量声明在内存中是紧密有序存储的,所以后面获取array的地址时会报错“fetch address not aligned on word boundary 0x00000002”
syscall($v0=4)
以$a0寄存器所存地址为首地址输出,直到遇上'\0'
停止
#输出一个空格并换行
.data
space: .asciiz " "
enter: .asciiz "\n"
.text
la $a0,space #将space的首地址传给$a0
li $v0,4
syscall
la $a0,enter #将enter的首地址传给$a0
li $v0,4
syscall
读入
syscall($v0=8)
读入一个字符串,其中$a0表示读入的首地址,$a1表示读入的字符数n,与fget类似,会在读入的字符串最后加'\n'
,因此实际上最多读入n-1个字符
syscall($v0=12)
读入一个字符,将读入的字符存在$v0中。
.data
str: .space 20
.text
#$t0 = i
li $v0,12
syscall
sb $v0,str($t0) # 将读入的字符存在str[i]中(sb指令仅将寄存器的低8位保存)
整数
读入
使用syscall($v0=5),将读入的整数存在$v0中
输出
使用syscall($v0=5),将$a0中的整数输出
条件语句
单条件
相等条件if-else
if(i==j){
THEN语句块
}else{
ELSE语句块
}
- 用beq
#t0=i,t1=j
beq $t0,$t1,then #若i==j,那么跳到THEN语句块,不相等则进行运行下一条语句,即ELSE语句块
ELSE语句块
j end #不跳转到end的话将继续运行THEN语句块
then:
THEN语句块
end:
- 用bne
该写法与C的THEN与ELSE块顺序一样,所以我一般都是将if中的条件取反后用转移指令,这样就保持了与c语言差不多的写法(老菜鸡行为)。
#t0=i,t1=j
bne $t0,$t1,else #若i!=j,那么跳到ELSE语句块,相等则进行运行下一条语句,即THEN语句块
THEN语句块
j end #不跳转到end的话将继续运行ELSE语句块
else:
ELSE语句块
end:
与0比较的if-else
使用bxxx rs,label。(同理我为了保持与c语言一样的写法,将条件取反后再找指令)
if(a<=0){
THEN块
}else{
ELSE块
}
#$t0=a
bgtz $t0,else #当a>0时跳转
THEN块
j end
else:
ELSE块
end:
非0值比较的if-else
使用slt使其转化为与0比较的if-else,若条件中含=号,则将条件取反(如条件为i<=j,那么slt判断的为j<i)。下面列表表示($t0=i,$t1=j,$t2为保存slt结果的寄存器)
初始条件 | slt | $t2所代表的含义 | beq/bne |
---|---|---|---|
i<j | slt $t2,$t0,$t1 | 0:初始条件为假 1:初始条件为真 | beq $t2,$0,else |
i>j | slt $t2,$t1,$t0 | 0:初始条件为假 1:初始条件为真 | beq $t2,$0,else |
i<=j | slt $t2,$t1,$t0 | 0:初始条件为真 1:初始条件为假 | bne $t2,$0,else |
i>=j | slt $t2,$t0,$t1 | 0:初始条件为真 1:初始条件为假 | bne $t2,$0,else |
(注:均为了保持与c语言顺序一致,只写了该写法的beq/bne,事实上i<j的beq/bne也可以写为bne $t2,$0,then,但是这种写法的THEN块与ELSE块与c语言的顺序相反)
eg:i<=j,将上表格的对应的slt和beq/bne复制即可
if(i<=j){
THEN块
}else{
ELSE块
}
#$t0=i,$t1=j
slt $t2,$t1,$t0
bne $t2,$0,else
THEN块
j end
else:
ELSE块
end:
多条件
&&
可以先判断第一个条件,若不成立直接跳至else,否则判断第2个条件。
if(a<b&&i<j){
THEN块
}else{
ELSE块
}
# $t0=a,$t1=b,$t2=i,$t3=j
slt $t4,$t0,$t1
beq $t4,$0,else#判断条件1
slt $t4,$t3,$t2
beq $t4,$0,else#判断条件2
THEN块
j end
else:
ELSE块
end:
||
不能判断了第1个条件就跳转,应该将两个条件得出的结果做一次或运算,再判断是否跳转。
循环语句
c语言: MIPS:
for(i=0;i<n;i++) li $t0,0 # 赋值i=0
{ for_loop:
beq $t0,$s0,end_loop # $s0=n
loop语句块 loop语句块
addi $t0,$t0,1
} j for_loop
end_loop:
更多层的就把loop语句块换成下一层的循环即可。
for(i=0;i<n;i++)
{
for(j=0;j<m;j++)
{
loop块
}
}
# 为了把层次看的更清楚,这里采用了不同的缩进代表不同的循环
li $t0,0 #i=0
for_loopi:
beq $t0,$s0,end_loopi
li $t1,0 #j=0
for_loopj:
beq $t1,$s1,end_loopj
loop语句块
addi $t1,$t1,1
j for_loopj
end_loopj:
addi $t0,$t0,1
j for_loopi
end_loopi:
一维数组的使用
字符数组
声明
[name]: .space [n]
eg:str: .space 20
使用
- set
li $v0,12
syscall
sb $v0,str($t0) # 读入一个字符并存到str[i]
- get
lb $t2,str($t0) #将str[i]读入寄存器$t2
整型数组
声明
[name]: .space [n]
其中n应为4*数组大小
eg:a .space 200
相当于int a[50]
使用
- set
sll $t1,$t0,2 #一定记得地址是i*4
sw $v0,a($t1) # 读入一个字符并存到a[i]
- get
sll $t1,$t0,2
lb $t2,a($t1) #将a[i]读入寄存器$t2
二维数组的使用
声明
.data
a: .space 256 # int a[8][8]
使用
#使用宏来简化
.macro getindex(%ans,%i,%j)
sll %ans,%i,3 # %ans=%i*8,若不是8*8的二维数组,如是10*10的,那么这条指令应改为mul %ans,%i,10
add %ans,%ans,%j # %ans=%ans+%j
sll %ans,%ans,2 # %ans=%ans*4
.end_macro
.text
#$t0=i,$t1=j
#存数组操作:
li $v0,5
syscall
getindex($t2,$t0,$t1)
sw $v0,a($t2) #将读入的整数存入a[i][j]
#读数组操作:
getindex($t2,$t0,$t1)
lw $s0,a($t2) #将a[i][j]的值存至$s0
递归函数
按c语言一步一步翻译就可以,递归调用的时候把$ra和函数的参数压栈即可。如c语言中func_name(n+1);
这一条语句就对应MIPS里的
sw $ra,0($sp) #存$ra
subi $sp,$sp,4
sw $t0,0($sp) #存这一层函数的参数
subi $sp,$sp,4
addi $t1,$t0,1 #将n+1存入$t1
move $a0,$t1 #传值
jal factorial #下一层函数的参数便是n+1了,当下一层函数运行到return(jr $31)时将回到这一层
addi $sp,$sp,4
lw $t0,0($sp) #读回这一层的参数
addi $sp,$sp,4
lw $ra,0($sp) #读回这一层的$ra
下面看一个求阶乘的递归问题。
int factorial(int n)
{
if(n==1) return 1;
else return n*factorial(n-1);
}
int main()
{
int n;
scanf("%d",&n);
printf("%d",factorial(n));
return 0;
}
main: #int main()
li $v0, 5
syscall
move $s0,$v0 #scanf("%d",&n);
move $a0,$s0 #让$a0=n,传入参数
jal factorial #factorial(n);
move $a0,$v0
li $v0,1
syscall #printf();
li $v0,10
syscall #return 0;
factorial: #int factorial (int n)
bne $a0,1,else #if(n==1){
li $v0,1 #return 1;
jr $31 #}
else: #else{
move $t0,$a0
###########
sw $ra,0($sp)
subi $sp,$sp,4
sw $t0,0($sp)
subi $sp,$sp,4 #要压入栈的东西:$ra和递归函数的参数
subi $t1,$t0,1
move $a0,$t1 #这一大段等价于factorial(n-1)
jal factorial
addi $sp,$sp,4
lw $t0,0($sp)
addi $sp,$sp,4
lw $ra,0($sp) #将$ra和这层的参数读回
############
mult $t0,$v0
mflo $v0
jr $31 #return n*factorial(n-1)