P2

Coooookie hahah

P2_Study

MIPS教程&大黑书学习

常用寄存器

  1. 寄存器使用规范

1

  1. 注意
    在输入输出的系统调用时,会涉及对于$a0$v0寄存器的值的改变,尤其是对于$a0寄存器
    要么在主程序中避免使用(比如从$a1开始保存参数)
    要么在系统调用时对于$a0寄存器进行保护
    1
    2
    3
    4
    5
    6
    7
    8
    # 保护示例
    .macro ptintInt(%src)
    push($a0) # push
    add $a0,%src,$zero
    li $v0,1
    syscall
    pop($a0) # pop
    .end_macro

常用指令

  1. 算术指令
    addi add subi sub
    mult(低32位存入LO寄存器,高32位存入HI寄存器)
    div(商存入LO寄存器,余数存入HI寄存器)
    sll (逻辑左移) srl(逻辑右移) sra(算术右移)
  2. 分支指令
    ① 条件分支
    b开头的一系列指令(可以根据Mars的提示来使用)
    需要注意的是,跳转过去之后不会返回原地址的下一条,直接跑了
    ② 跳转指令
    j 无条件跳转到相应标号,不返回
    jal 跳转与链接,跳转到相应标号并保存返回地址(可与jr $ra配合使用)
    jr 跳转寄存器指令,跳转到寄存器所保存的地址
  3. 存取指令
    swlw指令,以字为单位(4Byte)进行存取,每次存取32位
    sblb指令,以字节为单位(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:
    # ......

数组

  1. 新建数组
    注意.word与.space的区别,这里直接上评论区同学的总结
    1
    1
  2. 数组访问
    注意以字为单位(int)每次移位4,以字节为单位(char)每次移位1
    利用lb/lbu sb lw sw 等指令实现对数组的访问和改变

函数调用

大黑书P200-P206讲得很清楚

  1. 函数调用与返回
    MIPS使用jal来调用一个函数,使用jr指令从函数返回
    jal完成两种功能:
    ①将下一条指令(jal后面的那条指令)的地址存储到寄存器$ra中
    ②跳转到目标指令
    jr:跳转到寄存器保存的地址
    来点栗子:
    不涉及传参的函数
    1
    带有传参和函数返回值的函数
    1
  2. 寄存器保护
    函数调用前后要注意保存和维护寄存器,避免被调用函数改变寄存器的值而对调用者产生影响
    $s0$s7 保存局部变量时,要保护(就是调用者用到了而且调用函数可能改变的寄存器都要保护)
    $ra 要保护,这样才能知道函数返回哪里
    $v0
    $v1 不用保护,因为函数要将返回结果(如果有的话)存入这些寄存器中
    利用栈来保护寄存器,使用push和pop操作
  3. 递归函数调用
    保护好寄存器!!!
    1
  4. 栈的使用
    1

板子

  1. print
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    .macro  printInt(%src)
    add $a0,%src,$zero
    li $v0,1
    syscall
    .end_macro

    .macro printStr(%src)
    la $a0,%src # load label
    li $v0,4
    syscall
    .end_macro
  2. get
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    .macro getInt(%des)
    li $v0,5
    syscall
    add %des,$v0,$zero
    .end_macro

    .macro getChar(%des)
    li $v0,12
    syscall
    add %des,$v0,$zero
    .end_macro

    .macro getStr(%des) # contains '\n'
    li $v0,8
    syscall
    add %des,$a0,$zero
    .end_macro
  3. load from array
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    .macro loadChar(%i,%array,%des)
    lb %des,%array(%i)
    .end_macro

    .macro loadInt(%i,%j,%n,%array,%des)
    mult %i,%n
    mflo $t9
    add $t9,$t9,%j
    sll $t9,$t9,2
    lw %ans,%array($t9)
    .end_macro
  4. store to array
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    .macro storeInt(%i,%j,%n,%array,%src) # n is rows 
    mult %i,%n
    mflo $t9
    add $t9,$t9,%j
    sll $t9,$t9,2
    sw %src,%array($t9)
    .end_macro

    .macro storeChar(%i,%array,%src)
    sb %src,%array(%i)
    .end_macro
  5. push
    1
    2
    3
    4
    .macro push(%src)
    sw %src,0($sp)
    subi $sp,$sp,4
    .end_macro
  6. pop
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
       .macro pop(%src)
    addi $sp,$sp,4
    lw %src,0($sp)
    .end_macro
    7. end
    ```MIPS
    .macro end
    li $v0,10
    syscall
    .end_macro
  7. if-else
    利用b类指令实现if-else的跳转,注意执行完每个块最后也要跳转,否则顺序执行
  8. for
    设置循环变量,利用b类指令实现跳转,一定不要忘记i++和最后跳回去

易错点

  1. 写循环的时候写成死循环——忘记判断跳出循环的指令(一般放在loop开头的b类指令);
    忘记最后循环变量的自增操作
  2. 寄存器写错,比如$s1写成$1
    寄存器对应错误(注意自己每个寄存器保存什么变量以及注意提示C代码中的变量与实际程序中的变量可能有区别)

P2教程

矩阵乘法

考察循环使用,就是多个循环实现读取和计算

回文串判断

简单的c程序翻译就好,需要注意的是利用$v0 = 12来读入字符($v0 = 8会读入行尾换行符,不知道为啥,就这样吧)

卷积运算

矩阵乘法plus版,临时寄存器的使用有点多,注意勿混

全排列

先上C代码

1
注意 递归调用一定要保护好递归前后的变量!!!

1
2
3
4
5
6
7
8
9
10
11
12
   #
push($ra) # can also at the beginning
push($a1)
push($t0)
addi $a1,$a1,1 # index = index+1
jal FullArray ###
pop($t0)
pop($a1)
pop($ra) # can also at the return
#
return:
jr $ra

迷宫

  1. 不确定是否要保护的寄存器,只要递归前后涉及改变就push和pop
  2. 注意递归前后某个数组变量的置0和置1

后记

  1. 多写注释,利用好宏
  2. 水仙花数,汉诺塔,约瑟夫环等等,多练习
  3. 认真认真!

P2上机加油!冲!

P2_Review

匹配子串

  1. 读取字符串
    1
    2
    3
    4
    la $a0,array # the addr of the str
    li $a1,200 # the maxsize of the str is n-1
    li $v0,8
    syscall
    需要注意的是,这里的$a1存的是字符串的最长长度,读到\n停止
    想要得到字符串的实际长度,需要手搓strlen
  2. 判断是否匹配完子串,因为这里未知子串长度,所以用最后s2[j]是否为’\n’来判断比较好(但不知道为什么T了一个点,最后又设置了一个比较字符串上限的限制就过了)

冒泡排序

  1. 翻译c代码,没啥好说的
  2. 小错误:比如,$t5写成$5,$s3写成$a3等等
  3. 需要注意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.