BUAA计组p2_Mips_复习tips

p2 MARS


汇编程序回顾

重点:运行之前确保(Mars-Settings-Memory Configuration)为Compact,Data at Address 0

由于汇编语言的主体代码中并没有变量名、函数名、各种运算符号和语句块。我们仅能通过有限的32个寄存器以及众多的汇编指令和标签来实现我们之前所学的C语言所能够实现的功能。

  1. 伪指令

    • .data 定义程序数据段的初始地址

    • .text 定义程序的代码段

    • .space

      • name: .space n
      • name的地址是由.data段的初始地址加上前面所申请的数据大小计算得出的
      • 申请空间时尽量让n为4的倍数
      • 字节为单位申请空间,1byte,一个int占4个字节
    • .word

      • name: word 0:n
      • 在内存数据段以为单位连续存储数据,32bit
    • .asciiz

      • name: .asciiz:"string"
      • 字节为单位存储字符串,末尾以NULL结尾
      • 由于按字节存储,尽量放在字节分配空间之后
  2. 宏的使用

    • .macro 实现代码复用,不允许有嵌套宏指令
    .macro macro_name(%parameter1, %parameter2, ...)
    //代码段
    .end_macro
    
    • .eqv 增强代码可读性
    .eqv EQV_NAME string
    
  3. MIPS汇编程序设计

  • 条件语句

     slt $t3, $t1, $t2      # if(t1<t2) t3=1;
     beq $t3, $0, if_1_else
     # do something
     j if_1_end
    
     if_1_else:
     # do something else
    
     if_1_end:
    
  • 循环语句

     for_begin1:
     slt $t3, $t2, $t1;
     beq $t3, $0, for_end1
     # do something
     addi $t2, $t2, 1
     j for_begin1
     for_end1:
    

矩阵

matrix_multi

  1. 矩阵读入
    • 使用栈读入矩阵,保留三个矩阵的起始位置,计算元素在矩阵中的偏移量
  2. 矩阵乘法
    • 计算偏移量,计算结果,并存入目标地址
  3. 输出结果
    • for循环 + 计算偏移量 + 输出结果

常用汇总

  1. .word申请存储空间时,为.word 0:n, n为实际字节数

    matrix: .word 0:100       # 10\*10\*1word 即申请出10*10的int型数据二维矩阵
    
  2. 将题目中的固定输出赋予label写在.data下

    space: .asciiz " "
    
  3. sw, lw 对word的操作

    • sw即store word
    sw $t3, array($t4)		# store contents of $t3 into array[index]
    
    • lw即load word
    lw $t3, symbol($t4)     # load contents of $t3 into symbol[i]
    
    • 同理,lb和sb为对byte的操作
  4. li, la

    • la即load address
    • li即load immediate
  5. 常用的syscall命令

    读入整数
    li $v0, 5     # read an integer into $v0
    syscall
    
    输出字符串
    la $a0, space   # $a0 = address of null-terminated string to print
    li $v0, 4       
    syscall
    
    输出整数
    move $a0, out    # $a0 = integer to print
    li $v0, 1
    syscall
    
    读入字符
    li $v0, 12      # $v0 contains character read
    syscall
    
    结束程序
    li $v0, 10
    syscall
    
  6. 除法:商存在lo中,余数存在hi中; 乘法:低四位存在lo中,高四位存在hi中


递归

首先需要理解递归本身的性质或运行流程,在此基础上进行mips编写
mips代码示例为全排列一题

  • 递归流程:判断是否终止递归、进入终止输出并回溯 或 进入包含递归模块的核心部分

    • 递归模块重点:递归终止条件、递归进入变量存储、递归回溯
    1. 递归终止条件
    • 即递归终止的情况
    1. 递归进入变量存储
    • 递归中参数的存储
      • 进入新一轮递归前的参数
    • 每个递归跳转指令的下一指令的地址的存储
      • $ra中存储着jal recursion指令的下一指令,即本次递归结束时的下一条指令地址
        sw $ra, 0($sp)		# store address
        addi $sp, $sp, -4
        sw index, 0($sp)
        addi $sp, $sp, -4   # store index 
        sw i, 0($sp)
        addi $sp, $sp, -4	#store i
        
    1. 递归回溯
      • 按照存储顺序的逆序将本递归的参数取回来继续完成本次递归函数
        sw $ra, 0($sp)		# store address
        addi $sp, $sp, -4
        sw index, 0($sp)
        addi $sp, $sp, -4   # store index 
        sw i, 0($sp)
        addi $sp, $sp, -4	#store i
        
      • 递归回溯后的操作:
            bne $t4, 8, nxt
            li $s1, 0
        nxt:
            add $s2, $s1, $s0
            sw $s2, fib_array($t4) 
            move $s1, $s0		#前前一个
            move $s0, $s2		# $s0 前一个
            jr $ra
        

tips

  1. 由于寄存器数量问题,经常会有反复使用同一寄存器的操作,为避免复用了不该改动的寄存器,.eqv从$s7或者$t7开始由大到小使用
  2. 寄存器名字经常会造成写着写着迷糊的情况,建议勤用.eqv,命名直观最重要,如果缺寄存器则一定要多加注释帮助自己理清思路!
  3. 做题逻辑
    3.1 先理清题目的模块顺序,比如约瑟夫环的题目:先读入n,再循环结构得到所需结果的数组,最后输出模块
    3.2 将核心模块的具体流程落实写出来,可以是伪代码的形式,也可以是直接写出C,然后将寄存器与核心变量一一对应
    3.3 逐层对照翻译,或者是按照汇编的思路写完,注意初始化、结尾跳转、寄存器使用情况
    3.4 最好是一遍写对,写完之后,如果是偏简单的题目加简单注释,稍复杂的务必加详细的注释,然后再去debug

课上测试

  1. 上机调试好环境之后不要再随意按键盘上的键了,机房电脑重启很烧时间。

  2. 遇到Bug优先考虑局部修改,而不是全盘推倒重写。一是不一定有或不一定想得到更好的算法,而是重写耗时颇高。

    • 此类汇编题目有限制步数要求时,如果TLE了,优先考虑删减掉大部分情况下不必要的步骤(而不是傻乎乎的想不出来别的做法自闭)。
  3. 翻译C的mips汇编题目时,可以稍微调整判断语句的顺序以更适用于汇编的书写。

  4. 如果担心数据数量过多,直接压栈操作,不过务必记得存储栈顶地址。

  5. 程序末尾务必加上结束程序的指令(li $v0, 10 \n syscall),否则如果习惯将跳转执行模块label写在末尾的话,程序可能会死循环/玄学bug,该点可以通过调试发现问题,如果是边操作边输出,此类情况输出应正确。

posted @ 2020-11-14 12:59  Frida_h  阅读(1248)  评论(1编辑  收藏  举报