1.汇编语言基本结构
MIPS规定编写汇编程序时要用".data"和".text"两个关键字来区分程序的数据部分和代码部分。
2.主存变量声明
由于MIPS只有32个寄存器,因此大量的变量必须存储到主存中,待需要使用时,再将其值加载到某个寄存器中。
MIPS汇编程序声明一个全局变量:变量名+存储类型+初值
例如:
str : .asciiz "1234+4321"
".asciiz"为字节为单位字符串变量的关键字,并在结尾放置一个'\0'
注意:.space是字节数,而不是字数。
3.读存储器
1.读字节:lb(Load Byte)与lbu(Load Byte Unsigned)
从主存中读取一个字节并且将该字节写入指定的寄存器
格式:
lb rt,offset(base)
2.读半字: lh(Load Halfword)与lhu(Load Halfword Unsigned)
3.读字:lw(Load Word)
读字时从主存读出的数据是32位,而要写入寄存器的数据宽度也是32,没有符号拓展的需求,所以并不存在lwu
4.写寄存器
为读寄存器的反向操作,把数据从寄存器写入主存单元
有sb(Store Byte)、sh(Store Half)和sw(Store Word)
格式:
sb rt,offset(base)
5.寄存器加载立即数位
格式:
lui rt,immediate
这种情况下,MIPS规定立即数的长度为16位无符号数,lui执行后,指令的十六位立即数被复制到了寄存器的高16位,而寄存器的低16位则被置0
由于lui只能置高16位,因此很少被单独使用,往往与ori配对
ori格式:
ori rt,rs,imm16
rs寄存器"OR"立即数imm16,将结果写入rt寄存器
lui $s7,0x55AA
ori $s7,$s7,0x1234
$s7的值为55AA_1234
6.算术运算
6.1 加法和减法:add sub addu subu addi
add和sub会检查算术溢出,而带u则不会
MIPS规定addi的立即数是16位符号数,在编写程序时,addi的立即数范围为-32768~32767,在执行这条指令的时候,CPU首先将16位的立即数进行符号拓展成为32位
MIPS还规定了addiu等指令
6.2 乘法和除法:mult multu div divu mfhi mflo mthi mtlo
设计思想:两个32位数进行乘法运算,计算结果的最大绝对值需要64位才能表示,而一个寄存器只有32位,因此显然要用两个特定寄存器来存储高位(hi)和低位(lo)的运算结果
由于hi(HIgh)和lo(Low)不属于32个通用寄存器,因此MIPS定义了mfhi指令和mflo指令,格式如下:
mfhi rd
mflo rd
在除法中,lo用来表示商,hi则用来表示余数
示例:
mfhi $s7 #将乘法(除法)结果的高位(余数)传递到$s7寄存器
mthi 和mtlo两条指令不太常用,故不过多赘述
实际上还有指令mul,格式类似于add,但只能存储乘法结果的低32位
7.逻辑运算
and/andi指令效果完全相同,区别在于andi要将第二个是立即数的运算数按0拓展方式拓展为32位立即数。
zero_ext():零拓展
sign_ext():符号拓展
8.分支指令与if-else及循环结构
8.1 基本分支指令
beq rs,rt,label
bne rs,rt,label
j label
条件分支指令:
beq: 相等则执行label(CP的值变为label后续的指令)
bne:不相等则执行
无条件分支指令:
j: 直接跳转至label处的语句
8.2 相等条件下的if-else构造方法
为了防止THEN语句即使在条件不成立的时候也执行,ELSE语句块后的j指令是必须的。
8.3 与0值比较的条件分支指令
blez:小于等于0转移
bgtz:大于0转移
bltz:小于0转移
bgez:大于等于0转移
格式:
blez rs,label
8.4 两个非0值的不等条件的if-else构造方法
出于性能方面的考虑,MIPS中没有提供可“通用”的指令,而是提供了slt这一比较大小的指令
a<b 直接用slt比较
a>b 用slt判断b<a
a<=b 对b<a的结果进行取反
a>=b 对a<b的结果进行取反
slt将判断后的结果储存在rd寄存器中,再运用beq和bne指令结合$0就可以实现对应的条件转移
8.5 while循环
首尾两行定义while的边界,通过组合b类指令以及slt类指令实现当循环条件不满足时跳出循环
8.6 for循环
for类似于while,需要在循环体被执行前测试条件,因此b类指令必须部署在循环体之前,循环体后通常放置循环变量赋值语句,并在循环外先初始化循环变量。
9.伪指令
9.1 move: 寄存器间赋值
move $dst,$src
将src寄存器赋值给dst寄存器
等效于
add $s0,$s1,$0
9.2 li:加载立即数到寄存器
(16位)
li $t0,-100
等效于
addiu $t0,$0,-100
32位立即数时编译器通过lui和ori两条指令实现
9.3 la:加载地址到寄存器
la 寄存器,内存变量名
10.移位指令
方向:向左还是向右移位
性质:逻辑移位还是算术移位(带不带符号)
移位量:(1)由五位的立即数表示移位量
(2)由寄存器的后五位表示移位量
11.系统调用与输入输出
此部分内容可参考(27条消息) MIPS编程入门_子曰小玖的博客-CSDN博客_mips编程
- 通过系统调用实现终端的输入输出,以及声明程序结束
- 学会使用 syscall
- 参数所使用的寄存器:$v0, $a0, $a1
- 返回值使用: $v0
下表给出了系统调用中对应功能,代码,参数机返回值
Service | Code 对应功能的调用码 | Arguments 所需参数 | Results 返回值 |
---|---|---|---|
print_int 打印一个整型 | $v0 = 1 | $a0 = integer to be printed 将要打印的整型赋值给 $a0 | |
print_float 打印一个浮点 | $v0 = 2 | $f12 = float to be printed 将要打印的浮点赋值给 $f12 | |
print_double 打印双精度 | $v0 = 3 | $f12 = double to be printed 将要打印的双精度赋值给 $f12 | |
print_string | $v0 = 4 | $a0 = address of string in memory 将要打印的字符串的地址赋值给 $a0 | |
read_int | $v0 = 5 | integer returned in $v0 将读取的整型赋值给 $v0 | |
read_float 读取浮点 | $v0 = 6 | float returned in $v0 将读取的浮点赋值给 $v0 | |
read_double 读取双精度 | $v0 = 7 | double returned in $v0 将读取的双精度赋值给 $v0 | |
read_string 读取字符串 | $v0 = 8 | $a0 = memory address of string input buffer 将读取的字符串地址赋值给 $a0 将读取的字符串长度赋值给 $a1 | |
sbrk 应该同C中的sbrk()函数 动态分配内存 | $v0 = 9 | $a0 = amount 需要分配的空间大小(单位目测是字节 bytes) | address in $v0 将分配好的空间首地址给 $v0 |
exit 退出 | $v0 =10 |
打印一个存储在寄存器$s2的整型:
li $v0,1 #将要打印$a0
move $a0,$s2 #将$s2的值传递给$a0
syscall #系统执行IO操作
读取一个数,并储存在$s0中:
li $v0,5 #将要进行读取
syscall #系统执行IO操作(返回值就存储在$v0中)
move $v0,$s0 #把$v0的值传递给$s0