高级篇
高级篇
前面我们已经学习了 MIPS 汇编的基本使用。
下面我们在此基础上回顾一下 MIPS 汇编,同时讲一些高级一点的用法。
宏定义
有的同学可能会说,这个输入输出每次都要写好几条指令才能做到,但是每次写起来都是一样的,有没有更方便的方法去完成这件事呢?
当然有,我们隆重向大家介绍宏定义。
宏定义需要使用 .macro 和 .end_macro 进行包裹。
看下面的例子:
.macro RI(%i)
li $v0, 5
syscall
move %i, $v0
.end_macro
首先阅读一下中间三行 MIPS 代码:
$v0 寄存器的值设置成 5,然后 syscall 系统调用,这个时候程序会进行读入整数的操作。
然后,将读到 $v0 寄存器的值移动到 %i。
坏了,%i 是什么没听说过啊?往上找找 %i 在哪出现,哦,是第一行出现过,跟函数定义的参数类似的东西。
是不是传进来什么寄存器,这里的 %i 就会被替换成什么寄存器。
这下懂了,类比 C 语言,这就像是 #define 关键词,进行一整个的替换。使用的时候,只需要像下面这样使用:
.macro RI(%i)
li $v0, 5
syscall
move %i, $v0
.end_macro
.text
RI($s0)
就可以直接读入一个整数,存放到 $s0 寄存器当中。
类似的,如果使用RI($t0),就可以读到 $t0 寄存器当中。想读到哪就读到哪,非常的方便。
而且宏定义,想传几个参数就传几个参数,还可以嵌套使用,非常的方便,可以大大简化 MIPS 汇编代码的编写。
下面列举一些,好用的输入输出宏定义:
#read int .macro RI(%i) li $v0, 5 syscall move %i, $v0 .end_macro # print int .macro PI(%i) li $v0, 1 move $a0, %i syscall .end_macro # print int + newline .macro PIL(%n) PI(%n) li $a0, 10 li $v0, 11 syscall .end_macro # read string .macro RString(%addr, %maxlen) li $v0, 8 la $a0, %addr li $a1, %maxlen syscall .end_macro # print string .macro PString(%i) li $v0, 4 la $a0, %i # 如果是这种方法,使用的时候只需要使用 PString(数组名称) # la $a0, (%i) # 如果是这种方法,需要先使用 la 指令,然后 PString(寄存器) syscall .end_macro
全局数据段的使用
在 MIPS 汇编语言中,全局数据段使用 .data 伪指令来定义。
对于每个全局使用的变量,都应该编写对应的指令。指令的格式应该根据变量的类型和初始化情况来确定。例如,对于 int 类型的变量,可以使用 .word 伪指令来分配 4 字节的空间。对于全局字符串,可以使用 .asciiz 来分配空间。
例如对于下面的 C 语言代码:
int a = 2;
int b[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int c;
int d[10];
char *str1 = "abcdefg";
char *str2 = "hijklmn";
可以转换成下面的 MIPS 汇编语句。
.data
a: .word 2
b: .word 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
c: .word 0
d: .space 400
str1: .asciiz "abcdefg"
str2: .asciiz "hijklmn"
.ascii 和 .asciiz 的不同之处在于,.asciiz 后面的字符串默认会帮你在字符串的结尾添加好 '\0',而 .ascii 不会。
有兴趣的同学可以试试如果把上面这个例子中的 .asciiz 改成 .ascii,输出 str1 字符串会发生什么。
通过全局数据段,我们可以在程序运行之前为所有的全局变量分配内存空间,并在必要时将它们初始化为指定的值。这样,在程序的执行过程中,全局变量的值就可以被保存在内存中,并随时被读取和修改。
当然使用的时候需要使用 la 指令、lw 指令来完成内存当中数据的读取。
综合的例子如下:
.data
a: .word 2
b: .word 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
c: .word 0
d: .space 400
str1: .asciiz "abcdefg"
str2: .asciiz "hjiklmn"
.macro PI(%n)
move $a0, %n
li $v0, 1
syscall
.end_macro
.macro endl()
li $a0, 10
li $v0, 11
syscall
.end_macro
.macro PString(%i)
li $v0, 4
la $a0, %i
syscall
.end_macro
.text
# print a
la $s0, a
lw $s1, ($s0)
PI($s1)
endl()
# print b
la $s0, b
li $t0, 0
loop:
beq $t0, 10, endB
lw $t1, ($s0)
PI($t1)
addi $t0, $t0, 1
addi $s0, $s0, 4
j loop
endB:
endl()
# print str1
PString(str1)
endl()
这个样例的输出为:
2
12345678910
abcdefg