MIPS 汇编程序设计

MIPS 汇编程序设计

editor:xingjiancui


在学习 MIPS 汇编程序设计之前,必须知道一些关于 MIPS 架构的特性

  • MIPS 一个 Word 占 4 Byte

  • MIPS 结构的每条指令长度都是 1 Word = 32 bit

寄存器

MIPS 寄存器文件包含 32 个通用寄存器,32 个协处理器1(浮点寄存器)。每个寄存器都是 32 位宽。

通用寄存器

这些寄存器可以用于存储数据和地址。

  1. $0$zero
    • 始终为 0。
    • 不能被修改。
    • 常用于常量 0 的表示。
  2. $1$at
    • 保留给汇编器使用。
    • 通常用于汇编器临时变量。
  3. $2$3$v0$v1
    • 用于存储函数调用的返回值。
    • $v0 通常用于返回整数值。
    • $v1 用于返回其他类型的值。
    • $v0 用于存储系统调用号
    • 在用户交互中,$v0 用于存储返回的整数类型
  4. $4$7$a0$a3
    • 用于传递函数的前 4 个参数。
    • $a0$a3 分别对应第 1 到第 4 个参数。
    • $a0 通常用于打印整数、字符、字符串
    • 在用户交互中,$a0 用于指定读取的字符串存储到的内存地址,并用 a1 告诉操作系统允许的存储空间的最大值
  5. $8$15$t0$t7
    • 临时寄存器。
    • 用于存储临时数据。
    • 在函数调用时不会被保存,因此函数调用后可能会被覆盖。
    • 调用者负责保存这些寄存器的内容,如果需要在函数调用后恢复。
  6. $16$23$s0$s7
    • 保存寄存器。
    • 用于存储函数中的局部变量。
    • 在函数调用时需要保存和恢复。
    • 被调用者负责保存这些寄存器的内容,并在函数返回时恢复。
  7. $24$25$t8$t9
    • 临时寄存器。
    • 用于存储临时数据。
    • 在函数调用时不会被保存,因此函数调用后可能会被覆盖。
  8. $26$27$k0$k1
    • 保留给操作系统内核使用。
    • 通常用于中断处理。
  9. $28$gp
    • 全局指针。
    • 指向全局数据区的中间位置。
  10. $29$sp
    • 栈指针。
    • 指向当前栈顶。
  11. $30$fp
    • 帧指针。
    • 指向当前栈帧的基址。
  12. $31$ra
    • 返回地址寄存器。
    • 存储函数调用的返回地址。

  • $t$s的使用约定

    何时使用 $t 寄存器

    • 临时数据:当需要存储临时数据时,使用 $t 寄存器。这些数据通常在函数内部使用,不需要在函数调用时保存。
    • 计算中间结果:在进行计算时,使用 $t 寄存器存储中间结果。
    • 不需要跨函数调用保存的数据:如果数据不需要在函数调用后恢复,使用 $t 寄存器。

    何时使用 $s 寄存器

    • 局部变量:当需要存储函数中的局部变量时,使用 $s 寄存器。这些变量需要在函数调用时保存。
    • 状态信息:当需要存储函数的状态信息时,使用 $s 寄存器。这些信息需要在函数调用时保存。
    • 需要跨函数调用保存的数据:如果数据需要在函数调用后恢复,使用 $s 寄存器。

协处理器1(浮点寄存器)

MIPS 架构的浮点寄存器可以用于存储单精度浮点数(32 位)或双精度浮点数(64 位)。其中,单精度浮点数的存储需要一个协处理器1,双精度浮点数的存储需要两个协处理器1

  1. $f0$f1

    • 用于存储浮点运算的结果。
    • $f0 通常用于存储单精度结果。
    • $f1 通常用于存储双精度结果。
    • 在用户交互中,$f0 用于存储返回的 float 类型,$f0 : f1 用于存储返回的 double 类型
  2. $f2$f3

    • 用于存储浮点运算的临时结果。
  3. $f4$f11

    • 用于存储浮点运算的参数。
    • 通常用于传递函数调用的参数。
  4. $f12$f15

    • 用于存储浮点运算的参数。
    • 通常用于传递函数调用的参数。
    • $f12 通常用于打印单精度和双精度浮点数
  5. $f16$f19

    • 用于存储浮点运算的临时结果。
  6. $f20$f31

    • 用于存储浮点运算的临时结果或保存寄存器。
    • 在函数调用时需要保存和恢复。

特殊寄存器

  1. HI 和 LO
    • 用于存储乘法和除法的结果。
    • HI 存储乘法的高 32 位或除法的余数。
    • LO 存储乘法的低 32 位或除法的商。

数据类型

在MIPS汇编语言中,数据类型主要用于定义和存储数据。以下是对几种数据类型的详细介绍,这几乎涵盖了所有的数据类型:

  1. byte

    • 定义:用于存储8位(1字节)的整数。也可用来存储单个字符
    • 示例
      .byte 0x41    # 存储字节值0x41
      .byte 65      # 存储字节值65
      
    • 用途:常用于存储单个字符或需要较小数据存储的情况。
  2. half

    • 定义:用于存储16位(2字节)的整数。
    • 示例
      .half 0x1234  # 存储半字值0x1234
      .half 4660    # 存储半字值4660
      
    • 用途:用于需要中等大小数据存储的情况。
  3. word

    • 定义:用于存储32位(4字节)的整数。
    • 示例
      .word 0x12345678  # 存储字值0x12345678
      .word 305419896   # 存储字值305419896
      
    • 用途:这是MIPS中最常用的数据类型,用于存储大多数整数数据。
  4. ascii

    • 定义:用于存储ASCII字符序列
    • 示例
      .ascii "Hello, World!"  # 存储字符串"Hello, World!"
      
    • 用途:用于存储字符数据,但不以NULL字符结尾。
    • 注意:被打印的字符序列必须用 " " 包围
  5. asciiz

    • 定义:用于存储以NULL字符(0x00)结尾的ASCII字符串。
    • 示例
      .asciiz "Hello, World!"  # 存储字符串"Hello, World!"并以NULL字符结尾
      
    • 用途:用于存储C风格字符串,即以NULL字符结尾的字符串。
  6. float

    • 定义:用于存储32位(4字节)的单精度浮点数。
    • 示例
      .float 3.14159  # 存储单精度浮点数3.14159
      
    • 用途:用于存储单精度浮点数,遵循IEEE 754标准。
  7. double

    • 定义:用于存储64位(8字节)的双精度浮点数。由于每个浮点寄存器都是32位,每个双精度浮点数需要用两个浮点寄存器来存储
    • 示例
      .double 3.141592653589793  # 存储双精度浮点数3.141592653589793
      
    • 用途:用于存储双精度浮点数,遵循IEEE 754标准。
  8. space + num(伪指令):

    • 定义:用于在数据段中预留一定数量的字节

    • 示例

      myString:  .space 20	# 存储最大长度为20得字符串
      myArray;   .space 12    # 存储3个整数
      

系统调用

  • 系统调用号都存在 $v0 寄存器中
  • 用于打印的整数、字符、字符串存在 $a0 寄存器中
  • 用于打印的单精度浮点数、双精度浮点数存在 $f12 寄存器中

系统调用的基本流程:

  1. 系统调用号:用户程序将系统调用号加载到特定的寄存器中(如 MIPS 中的 $v0 寄存器)。

  2. 参数传递:用户程序将系统调用的参数加载到特定的寄存器中(如 MIPS 中的 $a0$a3 寄存器)。

  3. syscall 指令:执行 syscall 指令,触发操作系统内核的系统调用处理程序。

  4. 内核处理:操作系统内核根据系统调用号执行相应的服务,并将结果返回给用户程序


系统调用号

  1. 打印整数 (print_int)

    • 系统调用号:1
    • 示例
      li $v0, 1       # 系统调用号1,表示打印整数
      li $a0, 42      # 要打印的整数
      syscall         # 执行系统调用
      
  2. 打印浮点数 (print_float)

    • 系统调用号:2
    • 示例
      li $v0, 2       # 系统调用号2,表示打印浮点数
      l.s $f12, float_value  # 加载浮点数到$f12寄存器
      syscall         # 执行系统调用
      
  3. 打印双精度浮点数 (print_double)

    • 系统调用号:3
    • 示例
      li $v0, 3       # 系统调用号3,表示打印双精度浮点数
      l.d $f12, double_value  # 加载双精度浮点数到$f12寄存器
      syscall         # 执行系统调用
      
  4. 打印字符串 (print_string)

    • 系统调用号:4
    • 示例
      li $v0, 4       # 系统调用号4,表示打印字符串
      la $a0, message # 加载字符串地址到$a0寄存器
      syscall         # 执行系统调用
      
  5. 打印字符 (print_char)

    • 系统调用号:11
    • 示例
      li $v0, 11      # 系统调用号11,表示打印字符
      li $a0, 'A'     # 要打印的字符
      syscall         # 执行系统调用
      
  6. 读取整数 (read_int)

    • 系统调用号:5
    • 存储:返回值存储到 $v0
    • 示例
      li $v0, 5       # 系统调用号5,表示读取整数
      syscall         # 执行系统调用,返回值存储到$v0
      move $t0, $v0   # 将读取的整数保存到$t0寄存器
      
  7. 读取浮点数 (read_float)

    • 系统调用号:6
    • 存储:返回值存储到 $f0
    • 示例
      li $v0, 6       # 系统调用号6,表示读取浮点数
      syscall         # 执行系统调用,返回值存储到$f0
      
  8. 读取双精度浮点数 (read_double)

    • 系统调用号:7
    • 示例
      li $v0, 7       # 系统调用号7,表示读取双精度浮点数
      syscall         # 执行系统调用
      mov.d $f2, $f0  # 将读取的双精度浮点数保存到$f2寄存器
      
  9. 读取字符串 (read_string)

    • 系统调用号:8

    • 示例

      li $v0, 8       # 系统调用号8,表示读取字符串
      la $a0, buffer  # 加载缓冲区地址到$a0寄存器
      li $a1, 20      # 设置缓冲区大小
      syscall         # 执行系统调用
      
  10. 读取字符 (read_char)

    • 系统调用号:12

    • 示例

      li $v0, 12      # 系统调用号12,表示读取字符
      syscall         # 执行系统调用
      move $t0, $v0   # 将读取的字符保存到$t0寄存器
      
  11. 退出程序 (exit)

    • 系统调用号:10

    • 示例

      li $v0, 10      # 系统调用号10,表示退出程序
      syscall         # 执行系统调用
      

运算指令

  • move:用于两个寄存器之间的数据移动

加法

  1. add 指令

    • 全称: Add
    • 功能: 将两个寄存器的值相加,并将结果存储在目标寄存器中。

    • 操作数: 3个寄存器(目标寄存器、源寄存器1、源寄存器2)。

    • 示例:

      add $t0, $t1, $t2  # $t0 = $t1 + $t2
      
    • 溢出检测: add 指令会检测溢出(overflow),如果发生溢出,会触发异常。

  2. addu 指令

    • 全称: Add Unsigned

    • 功能: 将两个寄存器的值相加,并将结果存储在目标寄存器中。

    • 操作数: 3个寄存器(目标寄存器、源寄存器1、源寄存器2)。

    • 示例:

      addu $t0, $t1, $t2  # $t0 = $t1 + $t2
      
    • 溢出检测: addu 指令不会检测溢出,即使发生溢出也不会触发异常。

  3. addi 指令

    • 全称: Add Immediate

    • 功能: 将一个寄存器的值与一个立即数相加,并将结果存储在目标寄存器中。

    • 操作数: 2个寄存器(目标寄存器、源寄存器)和一个立即数。

    • 示例:

      addi $t0, $t1, 100  # $t0 = $t1 + 100
      
    • 溢出检测: addi 指令会检测溢出,如果发生溢出,会触发异常。

  4. addiu 指令

    • 全称: Add Immediate Unsigned

    • 功能: 将一个寄存器的值与一个立即数相加,并将结果存储在目标寄存器中。

    • 操作数: 2个寄存器(目标寄存器、源寄存器)和一个立即数。

    • 示例:

      addiu $t0, $t1, 100  # $t0 = $t1 + 100
      
    • 溢出检测: addiu 指令不会检测溢出,即使发生溢出也不会触发异常。

  5. add.s 指令

    • 全称: Add Single-Precision
    • 功能: 将两个单精度浮点数的值相加,并将结果存储在目标寄存器中。
    • 操作数: 3个浮点寄存器(目标寄存器、源寄存器1、源寄存器2)。
    • 示例:
      add.s $f0, $f1, $f2  # $f0 = $f1 + $f2 (单精度浮点加法)
      
    • 解释: 将 $f1$f2 中的单精度浮点数相加,结果存储在 $f0 中。
  6. add.d 指令

    • 全称: Add Double-Precision
    • 功能: 将两个双精度浮点数的值相加,并将结果存储在目标寄存器中。
    • 操作数: 3个浮点寄存器(目标寄存器、源寄存器1、源寄存器2)。
    • 示例:
      add.d $f0, $f2, $f4  # $f0 = $f2 + $f4 (双精度浮点加法)
      
    • 解释: 将 $f2$f4 中的双精度浮点数相加,结果存储在 $f0 中。

总结

  • add: 用于有符号数的加法,检测溢出。
  • addu: 用于无符号数的加法,不检测溢出。
  • addi: 用于有符号数的立即数加法,检测溢出。
  • addiu: 用于无符号数的立即数加法,不检测溢出。
  • add.s: 单精度浮点加法,将两个单精度浮点数相加。
  • add.d: 双精度浮点加法,将两个双精度浮点数相加。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值