P2
P2_Study
MIPS教程&大黑书学习
常用寄存器
- 寄存器使用规范
- 注意
在输入输出的系统调用时,会涉及对于$a0
和$v0
寄存器的值的改变,尤其是对于$a0
寄存器
要么在主程序中避免使用(比如从$a1
开始保存参数)
要么在系统调用时对于$a0
寄存器进行保护1
2
3
4
5
6
7
8# 保护示例
ptintInt(%src)
push($a0) # push
add $a0,%src,$zero
li $v0,1
syscall
pop($a0) # pop
.end_macro
常用指令
- 算术指令
①addi add subi sub
②mult
(低32位存入LO寄存器,高32位存入HI寄存器)div
(商存入LO寄存器,余数存入HI寄存器)
③sll
(逻辑左移)srl
(逻辑右移)sra
(算术右移) - 分支指令
① 条件分支
b开头的一系列指令(可以根据Mars的提示来使用)
需要注意的是,跳转过去之后不会返回原地址的下一条,直接跑了
② 跳转指令j
无条件跳转到相应标号,不返回jal
跳转与链接,跳转到相应标号并保存返回地址(可与jr $ra配合使用)jr
跳转寄存器指令,跳转到寄存器所保存的地址 - 存取指令
①sw
与lw
指令,以字为单位(4Byte)进行存取,每次存取32位
②sb
与lb
指令,以字节为单位(1Byte)进行存取lbu
:将相应地址的字节(8位)装入寄存器的最低有效字节,并用0填充寄存器的剩下字节lb
:同lbu,不过最后是使用符号扩展来填充寄存器的剩下字节sb
:将寄存器的最低有效字节存储到指定地址字节
因为MIPS本就是以字节为单位操作的,所以每次对于字节操作,移位只用以1为单位即可
来点栗子:1
2
3
4
5
6
7
8
9
10
11
12
13# $s0 = base addr of chararray $s1 = i
addi $s1,$zero,0 # i = 0
addi $t0,$zero,10 # t0 = 10
loop:
beq $s1,$t0,done # i = 10,exit loop
add $t1,$s0,$s1 # t1 = index = addr0 + i
lb $t2,0($t1) # t2 = array[i]
addi $t2,$t2,-32 # xiaoxie -> daxie
sb $t2,0($t1) # store the new value
addi $s1,$s1,1 # i++
j loop
done:
# ......
数组
- 新建数组
注意.word与.space的区别,这里直接上评论区同学的总结 - 数组访问
注意以字为单位(int)每次移位4,以字节为单位(char)每次移位1
利用lb/lbu sb lw sw 等指令实现对数组的访问和改变
函数调用
大黑书P200-P206讲得很清楚
- 函数调用与返回
MIPS使用jal来调用一个函数,使用jr指令从函数返回
jal完成两种功能:
①将下一条指令(jal后面的那条指令)的地址存储到寄存器$ra中
②跳转到目标指令
jr:跳转到寄存器保存的地址
来点栗子:
不涉及传参的函数
带有传参和函数返回值的函数 - 寄存器保护
函数调用前后要注意保存和维护寄存器,避免被调用函数改变寄存器的值而对调用者产生影响
①$s0
$s7
保存局部变量时,要保护(就是调用者用到了而且调用函数可能改变的寄存器都要保护)
②$ra
要保护,这样才能知道函数返回哪里
③$v0
$v1
不用保护,因为函数要将返回结果(如果有的话)存入这些寄存器中
利用栈来保护寄存器,使用push和pop操作 - 递归函数调用
保护好寄存器!!! - 栈的使用
板子
- print
1
2
3
4
5
6
7
8
9
10
11printInt(%src)
add $a0,%src,$zero
li $v0,1
syscall
.end_macro
printStr(%src)
la $a0,%src # load label
li $v0,4
syscall
.end_macro - get
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17getInt(%des)
li $v0,5
syscall
add %des,$v0,$zero
.end_macro
getChar(%des)
li $v0,12
syscall
add %des,$v0,$zero
.end_macro
# contains '\n' getStr(%des)
li $v0,8
syscall
add %des,$a0,$zero
.end_macro - load from array
1
2
3
4
5
6
7
8
9
10
11loadChar(%i,%array,%des)
lb %des,%array(%i)
.end_macro
j,%n,%array,%des) loadInt(%i,%
mult %i,%n
mflo $t9
add $t9,$t9,%j
sll $t9,$t9,2
lw %ans,%array($t9)
.end_macro - store to array
1
2
3
4
5
6
7
8
9
10
11j,%n,%array,%src) # n is rows storeInt(%i,%
mult %i,%n
mflo $t9
add $t9,$t9,%j
sll $t9,$t9,2
sw %src,%array($t9)
.end_macro
storeChar(%i,%array,%src)
sb %src,%array(%i)
.end_macro - push
1
2
3
4push(%src)
sw %src,0($sp)
subi $sp,$sp,4
.end_macro - pop
1
2
3
4
5
6
7
8
9
10pop(%src)
addi $sp,$sp,4
lw %src,0($sp)
.end_macro
7. end
```MIPS
end
li $v0,10
syscall
.end_macro - if-else
利用b类指令实现if-else的跳转,注意执行完每个块最后也要跳转,否则顺序执行 - for
设置循环变量,利用b类指令实现跳转,一定不要忘记i++和最后跳回去
易错点
- 写循环的时候写成死循环——忘记判断跳出循环的指令(一般放在loop开头的b类指令);
忘记最后循环变量的自增操作 - 寄存器写错,比如
$s1
写成$1
;
寄存器对应错误(注意自己每个寄存器保存什么变量以及注意提示C代码中的变量与实际程序中的变量可能有区别)
P2教程
矩阵乘法
考察循环使用,就是多个循环实现读取和计算
回文串判断
简单的c程序翻译就好,需要注意的是利用$v0 = 12来读入字符($v0 = 8会读入行尾换行符,不知道为啥,就这样吧)
卷积运算
矩阵乘法plus版,临时寄存器的使用有点多,注意勿混
全排列
先上C代码
注意 递归调用一定要保护好递归前后的变量!!!
1 | # |
迷宫
- 不确定是否要保护的寄存器,只要递归前后涉及改变就push和pop
- 注意递归前后某个数组变量的置0和置1
后记
- 多写注释,利用好宏
- 水仙花数,汉诺塔,约瑟夫环等等,多练习
- 认真认真!
P2上机加油!冲!
P2_Review
匹配子串
- 读取字符串 需要注意的是,这里的$a1存的是字符串的最长长度,读到\n停止
1
2
3
4la $a0,array # the addr of the str
li $a1,200 # the maxsize of the str is n-1
li $v0,8
syscall
想要得到字符串的实际长度,需要手搓strlen - 判断是否匹配完子串,因为这里未知子串长度,所以用最后s2[j]是否为’\n’来判断比较好(但不知道为什么T了一个点,最后又设置了一个比较字符串上限的限制就过了)
冒泡排序
- 翻译c代码,没啥好说的
- 小错误:比如,$t5写成$5,$s3写成$a3等等
- 需要注意n可能为0,这里直接结束程序,示例c代码并没有给出相应处理,不过T的是第一个点,自己试一下也就知道了
P2结束啦!再接再厉!
- Post title:P2
- Post author:Coooookie
- Create time:2023-01-02 20:29:45
- Post link:https://coooookie0913.github.io/2023/01/02/P2/
- Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.