[Mars基本用法参考](https://jingyan.baidu.com/article/ea24bc39c04b76da62b3313c.html#:~:text=方法%2F步骤 1%2F11 分步阅读 首先在官网下载Mars 4.5。,是一个4MB的Jar可执行文件。 如果电脑上已经装有Java运行库就可以直接运行了。 2%2F11 打开MARS,点击左上角的File菜单,选择New或者Open (一个asm格式文件).如图我们新建了一个。)

赋值

  • ori $t2,100将t2寄存器的值赋值为100,其实是”100或上0“的结果给t2寄存器
  • li $t1,100将t1寄存器的值赋值为100,li是伪指令,表示立即加载

传值

  • move $t1,$t2,将t2寄存器的值传递给t1寄存器,同时t2寄存器中的值不改变

运算

  • 加法:add $t1,$t2,$t3,将t2寄存器的值和t3寄存器的值相加给t1寄存器,这里公式里面可以包含数字,比如add $t1,$t2,100,100也可以换成负数
  • 减法:sub $t1,$t2,$t3
  • 乘法:mul $t1,$t2,$t3
  • 除法:div rs, rt,商放在LO寄存器中,余数放在HI寄存器中,如果rt寄存器的值为0,那么结果不可预料
  • 使用mfhi和mflo来分别读取HI和LO寄存器
1
2
3
4
5
li $t1,5		#被除数5
li $t2,2 #除数2
div $t1,$t2 #计算5/2,商在LO中,余数在HI中
mfhi $t3 #将HI寄存器的值取出放在t3寄存器中,为1
mflo $t4 #将LO寄存器的值取出放在t4寄存器中,为2

输入输出(scanf与printf)

v0寄存器的值与syscall(系统调用)是紧密联系的,也就是v0的值决定了syscall是输入还是输出,输入/输出是字符串还是数字

1
2
li $v0,5
syscall

这段代码表面含义:把v0寄存器的值赋值为5,然后进行系统调用。

实际含义:读入一个整数,整数的值存储于v0中。也就是v0寄存器的值为5,决定了syscall为读入一个整数,并将读入的整数存储在v0寄存器中。具体对应值参考mars的help

第一个程序

1
2
3
4
5
6
7
8
li $v0,5                                                
syscall #读入一个整数

addi $v0,$v0,1 #加1
move $a0,$v0 #把v0寄存器的值移到a0

li $v0,1
syscall #输出a0寄存器的值

判断

跳转指令ble

ble $t1,6,lable1如果t1寄存器的值小于等于6,则跳转到lable1处,否则,执行ble下一条指令

1
2
3
4
5
6
7
8
9
10
li $t1,5                                                                          
li $v0,1 #确立syscall为输出a0的值
ble $t1,5,lable1

li $a0,5 #若t1不是小于等于5,继续执行该语句
syscall #输出5

lable1: #若t1小于等于5,跳转至此处
li $a0,6
syscall #输出6
1
2
3
4
5
6
7
8
9
10
11
li $t1,5                                                                          
li $v0,1 #确立syscall为输出a0的值
ble $t1,3,lable1

li $a0,5 #若t1不是小于等于5,继续执行该语句
syscall #输出5

lable1: #若t1小于等于5,跳转至此处
li $a0,6
syscall #输出6
最终输出56
1
2
3
4
5
6
7
8
9
10
11
12
li $t1,5
li $v0,1
ble $t1,3,lable1

li $a0,5
syscall #输出5
li $v0,10
syscall #程序结束代码

lable1:
li $a0,6
syscall

循环

1
2
3
4
5
6
7
8
li $t1,0
li $a0,0
loop:
addi $a0,$a0,1
addi $t1,$t1,1
ble $t1,3,loop
li $v0,1
syscall

使用判断语句和loop即可实现循环

数组

mips代表一般有两部分,.data和.text,前面的内容都是.text部分,对于数组的使用需要使用.data段。

在.data定义一个起始地址为array,空间为400的数组,然后再.text写代码

1
2
3
4
5
.data
array:.space 400

.text
xxxx

使用数组之前,需要将数组的首地址赋值给一个寄存器,用la指令

la $t1,array

int占4个字节,以下是赋值方式,在此表示方法下,需要sw(存)lw(取)来赋值和取值

1
2
3
4
5
li $t2,3
sw $t2,0($t1) #array[0]=3

li $t2,4
sw $t2,4($t1) #array[1]=4

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.data
array:.space 400

.text
la $t1,array

li $t2,3
sw $t2,0($t1)
li $t2,4
sw $t2,4($t2)

li $v0,1
lw $a0,0($t1)
syscall
lw $a0,4($t1)
syscall

在.data里加一句`a:.asciiz","

想要输出逗号的地方加一句

1
2
3
la $a0,a
li $v0,4
syscall

数组的赋值还有别的写法,这种写法适合循环赋值

1
2
3
4
5
6
7
8
.data
array:.space 400

.text
la $t1,array
li $t2,4
addi $t3,$t1,4
sw $t2,0($t3) #array[1]=4

.marco开始.end_macro结束

1
2
3
4
5
.macro RI(%i)
li $v0,5
syscall
move %i,$v0
.end_macro

递归

jal指令,跳转到相应位置,并将现在的指令位置存储在ra寄存器中,当跳转结束后可由jr $ra指令返回,返回值存储在v0寄存器中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
li $a0,1

jal lable

li $v0,1
syscall
li $v0 10
syscall

label:
addi $a0,$a0,1
jr $ra

#最后输出2

sp指针,sp指针可以分配空间

栈通常由高地址向低地址生长,$sp指向栈顶,也就是最高地址

  • 数据入栈时,让sp减小
  • 数据出栈时,sp增大
1
addi $sp,$sp,-8

可以理解为分配了8个单位的空间,可以用来存储数据

1
2
sw $t1,0($sp)
sw $t2,4($sp)

无论如何修改寄存器t1,t2的值,只要如下操作,t1,t2就会恢复原本的值

1
2
lw $t1,0($sp)
lw $t2,4($sp)

最后别忘了加回来指针

1
addi $sp,$sp,8

下面时一份递归代码,输出f(3)

1
2
3
4
int f(x){
if(x==1)return 1;
return f(x-1)+1;
}

其对应的汇编代码为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
li $a1,3
jal label

move $a0,$v0 #输出
li $v0,1
syscall
li $v0,10
syscall

label:
beq $a1,1,ret #如果x==1,则xxx
addi $sp,$sp,-8
sw $a1,0($sp)
sw $ra,4($sp) #将返回值和x的地址存储起来
addi $a1,$a1,-1 #x-1
jal label #执行f(x-1)
lw $a1,0($sp)
lw $ra,4($sp)
addi $sp,$sp,8
move $s1,$v0
addi $v0,$s1,1 #f(x-1)+1
jr $ra

ret:
li $v0,1 #返回值存储在v0中
jr $ra

mips总结

寄存器表,记录常见寄存器的用途与编号规则

Name Reg.Num Usage
zero 0 恒为0
at 1 为汇编程序保留
v0~v1 2~3 (2的1次幂) 过程调用返回值
a0~a3 4~7 (2的2次幂) 过程调用参数(函数参数)
t0~t7 8~15 (2的3次幂) 临时变量
s0~s7 16~23 (2的4次幂) 保存
t8~t9 24~25 临时变量
k0~k1 26~27 OS保留
gp 28 global pointer
sp 29 stack pointer
fp 30 frame pointer
ra 31 return address