MIPS汇编语言编程

MIPS下各个寄存器编号及描述

寄存器编号 寄存器名 寄存器用途
0 zero 永远返回0
1 $at 汇编保留寄存器(不可用作其他用途)
2-3 $v0-$v1 (value的简写)存储表达式或者函数的返回值
4-7 $a0-$a3 (Argument简写)存储子程序的前4个参数,在子程序调用过程中释放
8-15 $t0-$t7 (temp简写)临时变量,同上调用时不保存
16-23 $s0-$s7 (Save or Static简写?)静态变量?调用时保存
24-25 $t8-$t9 (Temp简写)算是前面$0-$7的继续,属性同$t0-$t7
26-27 $k0-$k1 (break off简写?)中断函数返回值,不可做其他用途
28 gp\ (GlobalPointer简写)指向64k(gp\ (GlobalPointer简写)指向64k(2^{16}$)大小的静态数据块的中间地址
29 $sp (Stack Pointer简写)栈指针,指向栈顶
30 $s8/$fp (Save / Frame Pointer)帧指针
31 $ra 返回地址,目测不可用作其他用途

程序结构

数据声明 + 普通文本 + 程序编码(文件后缀是 .s 或 .asm)

数据声明在代码段之后(在之前也没什么问题)

数据

  • 数据段以 .data 为开始标志
  • 声明变量后,即在主存中分配空间

代码

  • 代码段以 .text 为开始标志
  • 各项指令操作
  • 程序入口标志:main:
  • 程序结束标志

注释

同C系语言。

基本模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
# Comment giving name of program and description of function
# ...
# Template.s
.data # variable declarations follow this line
# 数据变量声明
# ...
.text # instructions follow this line
# 代码段
main: # indicates start of code (first instruction to execute)
# 主程序
# ...

# End of program, leave a blank line

数据声明

声明的格式:

1
变量名: 数据类型 变量值

冒号不可缺少。

通常给变量赋一个初始值,对于 .space,要指明需要多少大小的空间(bytes)。

加载/保存 指令集

  • 如果要访问内存,只能使用 load/store 指令
  • 其他的只能是寄存器操作

load

lw

lw register_dest, RAM_src

从内存中复制RAM_src的内容到对应的寄存器中(w即word,一个字长,4个字节,因此该数据大小为4个字节)

lb

lb register_dest, RAM_src

同上,lb为load byte.

store

sw

sw register_src, RAM_dest

指将指定寄存器中的数据写入到特定的内存中。

sb

sb register_src, RAM_dest

同上,但数据大小为1字节。

load imm

li

li register_dest, value

加载立即数。

立即与间接寻址

la

Load address 直接给地址

例如,la $t0, var1 将 var1 (表示的是内存地址)放到 $t0 中。

如果变量var1不是内存地址,就成 li 指令了。

间接寻址

lw: 寄存器中存储的是内存地址,需要访问该内存地址得到数据再放到目的寄存器中。

lw $t2,($t0) load word at RAM address contained in $t0 into $t2
sw $t2,($t0) store word in register $t2 into RAM at address contained in $t0

加偏移量

lw $t2,4($t0) load word at RAM address ($t0+4) into register $t2, ”4” gives offset from address in register $t0
sw $t2,-12($t0) store word in register $t2 into RAM at address ($t0 - 12),negative offsets are fine

算术指令集

  • 最多3个操作数
  • 操作数只能是寄存器,不允许出现地址
  • 所有指令统一是32位
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
add $t0,$t1,$t2 # $t0 = $t1 + $t2; add as signed (2's complement) integers
sub $t2,$t3,$t4 # $t2 = $t3 - $t4
addi $t2,$t3, 5 # $t2 = $t3 + 5; "add immediate" (no sub immediate)
addu $t1,$t6,$t7 # $t1 = $t6 + $t7; add as unsigned integers
subu $t1,$t6,$t7 # $t1 = $t6 - $t7; subtract as unsigned integers
mult $t3,$t4 # multiply 32-bit quantities in $t3 and $t4, and store 64-bit
# result in special registers Lo and Hi: (Hi,Lo) = $t3 * $t4
            #             运算结果存储在hi,lo(hi高位数据, lo低位数据)
div $t5,$t6 # Lo = $t5 / $t6 (integer quotient)
# Hi = $t5 mod $t6 (remainder)
            #             商数存放在 lo, 余数存放在 hi
mfhi $t0 # move quantity in special register Hi to $t0: $t0 = Hi
        #                 不能直接获取 hi 或 lo中的值, 需要mfhi, mflo指令传值给寄存器
mflo $t1 # move quantity in special register Lo to $t1: $t1 = Lo
# used to get at result of product or quotient

move $t2,$t3 # $t2 = $t3

控制流

分支(if else系列)

1
2
3
4
5
6
7
b   target      #  unconditional branch to program label target
beq $t0,$t1,target # branch to target if $t0 = $t1
blt $t0,$t1,target # branch to target if $t0 < $t1
ble $t0,$t1,target # branch to target if $t0 <= $t1
bgt $t0,$t1,target # branch to target if $t0 > $t1
bge $t0,$t1,target # branch to target if $t0 >= $t1
bne $t0,$t1,target # branch to target if $t0 <> $t1

跳转(while, for, goto系列)

1
2
3
4
j   target      #  unconditional jump to program label target
# 看到就跳, 不用考虑任何条件
jr $t3 # jump to address contained in $t3 ("jump register")
      # 类似相对寻址,跳到该寄存器给出的地址处

子程序调用

1
jal sun_label   # "jump and link"

将当前程序计数器保存在 $ra 中,通过上面保存在 $ra 中的计数器返回到调用前。

如果调用的子程序中有调用了其他的子程序,如此往复,则返回地址的标记就用栈来存储。

系统调用与输入/输出(主要针对SPIM模拟器)

  • 使用syscall,以下指令应该是通用的。
  • 参数使用的寄存器:$v0, $a0, $a1.
  • 返回值使用:$v0.
Service Code in $v0 对应功能的调用码 Arguemnts 所需参数 Results 返回值
打印一个整型 $v0=1 将要打印的整型赋值给$a0
打印一个浮点数 $v0=2 将要打印的浮点数赋值给$f12
打印双精度浮点数 $v0=3 将要打印的双精度浮点数赋值给$f12
打印字符串 $v0=4 将要打印的字符串的地址赋值给$a0
读取一个整型 $v0=5 将读取的整型赋值给$v0
读取浮点数 $v0=6 将读取的浮点数赋值给$v0
读取双精度浮点数 $v0=7 将读取的双精度浮点数赋值给$v0
读取字符串 $v0=8 将读取字符串地址赋值给$a0,将读取字符串的长度赋值给$a1
sbrk(应该同C中的sbrk()函数一样) 动态分配内存 $v0=9 $a0=amount 需要分配的空间大小,单位目测是字节 将分配好的空间首地址给$a0
退出 \v0=10 退出
  • 打印的字符串应该有一个终止符(‘\0’),声明字符串为 .asciiz 类型即可。
  • 对于读取整型,浮点数,双精度浮点数等数据操作,系统会读取一整行(以’\n’为结束)
  • 读取字符串时,输入过长就截短,短了不补,最后会加上终止符
  • The sbrk service returns the address to a block of memory containing n additional bytes. This would be used for dynamic memory allocation.

在MIPS程序中,指令一般从地址0x00400000开始存储。MIPS存储器地址是字节寻址,所以32位(4字节)指令地址每次增加4字节而不是1字节。

机器代码就是指令。汇编代码是汇编代码。

处理器从存储器中顺序地取出指令。数字电路硬件解码和执行这些指令。当前指令的地址存储在程序计数器(一个32位寄存器)中。

操作系统将PC的值设为地址0x00400000。处理器将位于这个地址的指令读出并执行机器代码。然后,处理器将PC增加4,变为0x00400000,接着取出该地址的指令并执行,以此类推。

微处理器的体系结构状态(architectural state)保存程序的状态。对于MIPS,体系结构状态由寄存器文件和PC组成。

分支

为了顺序执行指令,程序计数器执行一条指令后增4。分支(branch)指令改变程序计数器的值,跳过某段代码或返回到执行先前的代码。

Author

preccrep

Posted on

2021-06-13

Updated on

2021-06-21

Licensed under

You need to set install_url to use ShareThis. Please set it in _config.yml.
You forgot to set the business or currency_code for Paypal. Please set it in _config.yml.

Comments

You forgot to set the shortname for Disqus. Please set it in _config.yml.