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)

读入一个字符串,其中 a 0 表 示 读 入 的 首 地 址 , a0表示读入的首地址, a0a1表示读入的字符数n,与fget相似,会在读入的字符串最后加'\n',所以实际上最多读入n-1个字符oop

syscall($v0=12)

读入一个字符,将读入的字符存在$v0中。fetch

.data 
	str: .space 20
.text
	#$t0 = i
	li $v0,12
	syscall
	sb $v0,str($t0) # 将读入的字符存在str[i]中(sb指令仅将寄存器的低8位保存)

整数

读入

使用syscall( v 0 = 5 ) , 将 读 入 的 整 数 存 在 v0=5),将读入的整数存在 v0=5)v0中spa

输出

使用syscall( v 0 = 5 ) , 将 v0=5),将 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语言差很少的写法(老菜鸡行为)。code

#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)。下面列表表示( t 0 = i , t0=i, t0=i,t1=j,$t2为保存slt结果的寄存器)内存

初始条件 slt $t2所表明的含义 beq/bne
i<j slt t 2 , t2, t2,t0,$t1 0:初始条件为假 1:初始条件为真 beq $t2,$0,else
i>j slt t 2 , t2, t2,t1,$t0 0:初始条件为假 1:初始条件为真 beq $t2,$0,else
i<=j slt t 2 , t2, t2,t1,$t0 0:初始条件为真 1:初始条件为假 bne $t2,$0,else
i>=j slt t 2 , t2, t2,t0,$t1 0:初始条件为真 1:初始条件为假 bne $t2,$0,else

(注:均为了保持与c语言顺序一致,只写了该写法的beq/bne,事实上i<j的beq/bne也能够写为bne $t2,$0,then,可是这种写法的THEN块与ELSE块与c语言的顺序相反)ci

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)