从MIPS的发展史开始学MIPS
起源
当初和ARM比肩的MIPS,由于决策的问题和命途多舛,MIPS架构开始逐渐没落,曾经的辉煌也慢慢暗淡,但世间还残留着MIPS架构的余光,在目前的游戏机,打印机,路由器还有很多是MIPS架构的CPU,所以MIPS的学习并不能落下,而且学习各种CPU架构的设计思路,对于理解计算机也有着很大的帮助,虽然MIPS没落了,但RISC-V会带着它的余光继续前进…
MIPS各寄存器介绍与对比
与ARM和相比,MIPS也有很多特殊的特性,这些特性都旨在帮助CPU更好的完成工作
$zero寄存器时刻保存着常量0,至于为什么这么做,答:还是为了效率
零寄存器始终保持常量 0,除了 0 恰好是一个非常有用的常量这一事实之外,它并没有什么特别之处。如此有用以至于 MIPS 设计者专门使用一个寄存器来保存其值。(这样你就不必浪费另一个寄存器或任何内存来保存值。)
$1:即$at,该寄存器为汇编保留,由于I型指令的立即数字段只有16位,在加载大常数时,编译器或汇编程序需要
把大常数拆开,然后重新组合到寄存器里。比如加载一个32位立即数需要 lui(装入高位立即数)和addi两条
指令。像MIPS程序拆散和重装大常数由汇编程序来完成,汇编程序必需一个临时寄存器来重组大常数,这
也是为汇编 保留$at的原因之一v0
v1是函数调用返回之后的值,v也就是values的意思,a0a3就是函数的参数,如果有更多的参数,则通过栈来传递,t0t9都是Temporary寄存器,可以理解为随便使用,但需要注意的是t7和t8,t9并不是挨在一起的,中间还搁着s0s7,着8个寄存器那就不是随便使用的了,如果要使用他们必须将他们保存,否则程序可能会出现非预期的错误,k0~k1是给操作系统使用的,暂不考虑剩下的就是有特殊用途的寄存器,对应的英文也标识了,很容易理解,这里强调一下$ra寄存器,当调用的函数属于叶子函数的时候,会直接把返回值放到$ra寄存器里面,若是非叶子函数的话,就会把返回地址放到栈上,稍微解释一下叶子函数:此函数自身不再调用别的函数就称之为叶子函数
寄存器编号 | 别名 | 用途 |
---|---|---|
$0 | $zero | 常量0(constant value 0) |
$1 | $at | 保留给汇编器(Reserved for assembler) |
$2-$3 | $v0-$v1 | 函数调用返回值(values for results and expression evaluation) |
$4-$7 | $a0-$a3 | 函数调用参数(arguments) |
$8-$15 | $t0-$t7 | 暂时的(Temporary) |
$16-$23 | $s0-$s7 | 保存的(或如果用,需要SAVE/RESTORE的) |
$24-$25 | $t8-$t9 | 暂时的(Temporary) |
$26-$27 | $k0-$k1 | 内核的(kernel) |
$28 | $gp | 全局指针(Global Pointer) |
$29 | $sp | 堆栈指针(Stack Pointer) |
$30 | $fp/$s8 | 栈帧指针(Frame Pointer) |
$31 | $ra | 返回地址(return address) |
MIPS寄存器经典表格,但是我看到之后就有点疑惑,为啥MIPS的寄存器有个别名的东西,奇了怪了,还真没见过,那它为啥要有别名呢?网上的解释是”注记符”,但是它既然好记为啥不直接拿它命名呢?在ARM上也是以r开头命名的,并没有这么易懂的别名
在和常见的X86和X64对比下更容易明白他们之间的差别和各自的好处:
架构 | x86 | X64 | ARM | MIPS |
---|---|---|---|---|
函数返回值 | eax | rax | r0 | v0~v1 |
函数调用参数 | 栈 | rdi-rsi-rdx-rcx-r8-r9;栈 | r0~r3;栈 | a0~a3;栈 |
栈指针 | ebp;esp | rbp;rsp | fp(r11);sp(r13) | fp/s8;sp |
返回地址 | 进入子函数时会将返回地址压入栈中 | 进入子函数时会将返回地址压入栈中 | lr(r14)寄存器保存着子程序的返回地址 | ra寄存器保留程序的返回地址(叶子函数) |
MIPS汇编指令
IDA上有个很好用的选项,Options => General => Auto comments ,当你把它勾选上的时候,IDA会在一些指令的旁边显示注释帮助你理解这条汇编指令大致在做什么事情: