关于MIPS汇编的二三事
字数 2960 2025-08-25 22:58:56
MIPS汇编语言全面教程
1. MIPS架构概述
MIPS(Microprocessor without Interlocked Pipeline Stages)是一种精简指令集(RISC)处理器架构,由MIPS科技公司于1981年开发。特点包括:
- 最早版本为32位,现已发展到64位
- 广泛应用于电子产品、网络设备和个人娱乐设备
- 采用无互锁流水级设计
2. 机器周期基础
MIPS处理器执行指令的基本步骤:
- 取指令:从内存读取指令(PC寄存器保存当前指令地址)
- 更新PC:PC = PC + 4(指向下一条指令)
- 执行指令:执行取到的指令
3. 寄存器系统
MIPS有32个通用寄存器,使用$符号开头表示:
3.1 寄存器分类
| 寄存器 | 名称 | 用途 |
|---|---|---|
| $0 | $zero | 恒为零值 |
| $1 | $at | 汇编器保留,用于处理大常数 |
| \(2-\)3 | \(v0-\)v1 | 函数返回值 |
| \(4-\)7 | \(a0-\)a3 | 函数参数传递 |
| \(8-\)15 | \(t0-\)t7 | 临时寄存器(调用者保存) |
| \(16-\)23 | \(s0-\)s7 | 保存寄存器(被调用者保存) |
| \(24-\)25 | \(t8-\)t9 | 更多临时寄存器 |
| \(26-\)27 | \(k0-\)k1 | 操作系统/异常处理保留 |
| $28 | $gp | 全局指针 |
| $29 | $sp | 栈指针 |
| $30 | $fp | 帧指针 |
| $31 | $ra | 返回地址 |
3.2 特殊寄存器
- HI/LO:乘除法专用寄存器
- 乘法:HI存高位,LO存低位
- 除法:LO存商,HI存余数
- 访问指令:mfhi/mflo
4. 程序结构
基本汇编程序框架:
.data # 数据声明段
# 变量声明
.text # 代码段
main: # 程序入口
# 指令代码
# 程序结束
5. 数据声明与内存操作
5.1 数据声明语法
变量名: 存储类型 值
常用数据类型:
.word:32位整数.byte:8位字符.space n:分配n字节未初始化空间.asciiz:以null结尾的字符串
示例:
var1: .word 3 # 32位整数,初始值3
array1: .byte 'a','b' # 2字节字符数组
array2: .space 40 # 40字节未初始化空间
5.2 内存访问指令
| 指令 | 描述 | 示例 |
|---|---|---|
| lw | 加载字(4字节) | lw $t0, var1 |
| lb | 加载字节 | lb $t0, var1 |
| sw | 存储字 | sw $t1, var1 |
| sb | 存储字节 | sb $t1, var1 |
| li | 加载立即数 | li $t0, 5 |
| la | 加载地址 | la $t0, var1 |
5.3 寻址模式
-
直接寻址:
lw $t0, var1 -
间接寻址:
lw $t2, ($t0) -
基址偏移寻址:
lw $t2, 4($t0) # $t0 + 4地址处加载
6. 算术指令
| 指令 | 描述 | 示例 |
|---|---|---|
| add | 有符号加法 | add $t0,$t1,$t2 |
| sub | 有符号减法 | sub $t2,$t3,$t4 |
| addi | 立即数加法 | addi $t2,$t3,5 |
| addu | 无符号加法 | addu $t1,$t6,$t7 |
| subu | 无符号减法 | subu $t1,$t6,$t7 |
| mult | 乘法 | mult $t3,$t4 |
| div | 除法 | div $t5,$t6 |
| mfhi | 从HI寄存器移动 | mfhi $t0 |
| mflo | 从LO寄存器移动 | mflo $t1 |
| move | 寄存器间移动 | move $t2,$t3 |
7. 控制流指令
7.1 分支指令
| 指令 | 描述 | 示例 |
|---|---|---|
| b | 无条件分支 | b target |
| beq | 等于分支 | beq $t0,$t1,target |
| bne | 不等于分支 | bne $t0,$t1,target |
| blt | 小于分支 | blt $t0,$t1,target |
| ble | 小于等于分支 | ble $t0,$t1,target |
| bgt | 大于分支 | bgt $t0,$t1,target |
| bge | 大于等于分支 | bge $t0,$t1,target |
7.2 跳转指令
| 指令 | 描述 | 示例 |
|---|---|---|
| j | 无条件跳转 | j target |
| jr | 寄存器跳转 | jr $t3 |
| jal | 跳转并链接 | jal sub_label |
7.3 子程序调用
# 调用子程序
jal sub_label # 保存返回地址到$ra,跳转到子程序
# 子程序返回
jr $ra # 跳转回$ra保存的地址
8. 系统调用与I/O
使用syscall指令进行系统调用,参数通过寄存器传递:
| 服务 | $v0值 | 参数 | 返回值 |
|---|---|---|---|
| print_int | 1 | $a0=要打印的整数 | - |
| print_float | 2 | $f12=要打印的浮点数 | - |
| print_double | 3 | $f12=要打印的双精度 | - |
| print_string | 4 | $a0=字符串地址 | - |
| read_int | 5 | - | $v0=读取的整数 |
| read_float | 6 | - | $v0=读取的浮点数 |
| read_double | 7 | - | $v0=读取的双精度 |
| read_string | 8 | \(a0=缓冲区地址,\)a1=缓冲区长度 | - |
| sbrk | 9 | $a0=要分配的字节数 | $v0=分配的内存地址 |
| exit | 10 | - | - |
示例:
# 打印整数
li $v0, 1
li $a0, 123
syscall
# 打印字符串
.data
msg: .asciiz "Hello"
.text
li $v0, 4
la $a0, msg
syscall
# 读取整数
li $v0, 5
syscall
move $t0, $v0 # 将输入保存到$t0
# 程序退出
li $v0, 10
syscall
9. 栈操作
MIPS栈特点:
- 从高地址向低地址增长
- $sp寄存器指向栈顶
基本操作:
# 压栈(分配空间)
addi $sp, $sp, -4
sw $t0, 0($sp)
# 弹栈(释放空间)
lw $t0, 0($sp)
addi $sp, $sp, 4
10. 数组处理
数组定义与访问:
.data
array: .space 20 # 5个字的数组
.text
# 初始化数组
li $t0, 0 # 索引
li $t1, 1 # 值
sw $t1, array($t0) # array[0] = 1
addi $t0, $t0, 4 # 索引+4
li $t1, 2
sw $t1, array($t0) # array[1] = 2
# 访问数组元素
la $s1, array # 基地址
li $a0, 2 # 索引
mul $a0, $a0, 4 # 偏移量
add $s1, $s1, $a0 # 计算元素地址
lw $a0, 0($s1) # 加载元素
11. 宏定义与使用
11.1 宏匹配
.macro 宏名(参数)
# 指令
.end_macro
# 示例:打印整数宏
.macro print_int(%param)
li $v0, 1
li $a0, %param
syscall
.end_macro
# 使用
.text
print_int(1)
print_int(2)
11.2 宏定义
.eqv 别名 值/寄存器/指令
# 示例
.eqv LIMIT 20
.eqv CTR $t2
.eqv CLEAR_CTR add CTR, $zero, 0
.text
li $t0, LIMIT
CLEAR_CTR
12. 多文件处理
使用.include指令包含其他文件:
.text
jal fun
.include "A.asm" # 包含A.asm文件
13. 浮点操作
13.1 浮点寄存器
- 32个浮点寄存器(包含16个双精度寄存器)
13.2 浮点指令
# 单精度
lwc1 $f2, f1 # 加载单精度
swc1 $f2, 0x10010000 # 存储单精度
# 双精度
ldc1 $f2, d1 # 加载双精度(使用两个寄存器$f2+$f3)
sdc1 $f2, 0x10010000 # 存储双精度
14. 内存布局
典型MIPS内存布局:
- 代码段(.text):存放程序指令
- 数据段(.data):初始化的全局/静态变量
- 堆(heap):动态分配的内存
- 栈(stack):函数调用、局部变量,从高地址向低地址增长
15. 完整示例
15.1 条件判断
.data
msg_yes: .asciiz "YES"
msg_no: .asciiz "NO"
.text
main:
# 读取a
li $v0, 5
syscall
move $t0, $v0
# 读取b
li $v0, 5
syscall
move $t1, $v0
# 比较a > b
bgt $t0, $t1, a_greater
# 输出NO
li $v0, 4
la $a0, msg_no
syscall
j exit
a_greater:
# 输出YES
li $v0, 4
la $a0, msg_yes
syscall
exit:
li $v0, 10
syscall
15.2 循环求和
.text
main:
li $t0, 1 # i = 1
li $t1, 0 # sum = 0
loop:
add $t1, $t1, $t0 # sum += i
addi $t0, $t0, 1 # i++
ble $t0, 100, loop # if i <= 100, continue
# 输出结果
move $a0, $t1
li $v0, 1
syscall
# 退出
li $v0, 10
syscall
16. 最佳实践
- 使用有意义的标签名
- 合理使用宏减少重复代码
- 遵循寄存器使用约定
- 注意栈平衡(函数调用前后保持$sp一致)
- 为关键代码添加注释
- 使用.include组织大型项目
17. 参考资源
- MIPS官方文档
- 《计算机组成与设计:硬件/软件接口》
- MARS模拟器帮助文档
- 在线MIPS汇编教程和参考手册