「MIPS」前置知识
数据大小
通常情况下,对32位操作系统,
- 一个字节 (Byte) 等于8位
- 一个字(Word)等于4字节
根据SI标准,1kB=1000B(字节, Byte),而根据IEC标准 ,1kiB=1024B [ 1KB=1024B ]。
硬盘生产商是以GB(十进制,即10的3次方=1000,如1MB=1000KB)计算的,而电脑(操作系统)是以GiB(2进制,即2的10次方, 如1MiB=1024KiB)计算的,但是国内用户一般理解为1MiB=1M=1024 KB, 所以为了便于中文化的理解,翻译MiB为MB也是可以的。
数据传输速率中,1Kb/s=1000b/s。因为1024是理想标准的,但是实际情况很难做到理想标准。
存储方式
计算机中的数据存储主要有两种方式:大端存储和小端存储。
大端模式,是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中。
小端模式,是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中,这种存储模式将地址的高低和数据位权有效地结合起来,高地址部分权值高,低地址部分权值低。
例如,将一个32位的整数0x12345678
存放到一个整型变量(int)中:
寄存器
简介
寄存器是CPU的组成部分之一,是一种高速存储器(甚至是CPU可以使用的最高速的一种存储器),可以用来暂存指令,数据,地址等。由于寄存器的成本较高,一般的CPU中只有数量很有限的寄存器可供使用。
在MIPS体系结构中,CPU对数据的操作均是基于寄存器的。内存中的数据需要先使用读取(load)类指令保存到寄存器才可使用。操作完成的数据也不能直接保存到内存中,需要使用装载(store)类指令保存至内存中。
在一般的MIPS汇编程序中,比较常用的是32个通用寄存器。所谓通用寄存器,代表它没有明确规定的用途。
这32个通用寄存器有两种命名方式:
$t0 – 01000
$t1 – 01001
$0一般不能用于赋值,但这不代表你对它赋值就是错的。在实际中,你可以对$0进行赋值,但它的值会始终保持为0。也就是说对$0赋值并不违反语法,但没有实际效果。
$1保留给汇编器,一般不使用此寄存器(使用该寄存器可能会导致难以发现的bug,具体原因请在编写MIPS汇编时注意扩展指令是如何翻译成普通指令的)
调用寄存器
关于通用寄存器的使用,实际上是一种约定,为了方便程序员之间的沟通而制定的规则。
对于s寄存器而言,被调用者需要保证寄存器中的值在调用前后不能发生改变——对应到实际操作中,如果你想要编写一个子函数,那么在这个子函数中使用的所有s寄存器,都必须要在函数的开头入栈,在函数的结尾出栈,以确保其值在这个函数被调用前后不会发生变化。
对于t寄存器而言则刚好相反,你编写的子函数中用到t寄存器的地方无需做任何保存,随意使用即可——因为维护t寄存器是上层函数的任务。这也就是所谓的“s寄存器由被调用者维护,t寄存器由调用者维护”。
需要注意的是,这仅仅是一个约定而已。你当然可以不遵守这种规范,但代价就是程序的合作和维护将变得举步维艰。在同学们编写mips代码的时候,尤其是涉及到子函数的编写时,我们推荐严格按照mips的规范进行,确保一个函数在被调用前后,其中所用到的s寄存器的值不会被改变。
一个调用者(即父函数)并不能预知其将要调用的子函数(即被调用者)会使用到哪些t寄存器,但可能在调用时并不想失去自己正在使用的某个t寄存器中的数据。因此就将自己希望保留的t寄存器压入栈中,函数调用结束之后再弹回来。
总而言之,调用者维护t寄存器,被调用者维护s寄存器的意义,就在于让代码更易于模块化。在这种约定下,调用者不需要去考虑被调用者的具体细节,被调用者也不需要去考虑自己被调用的方式。这使得mips代码可以以函数为单位进行模块化开发。
特殊寄存器
PC(Program Counter):用于存储当前CPU正在执行的指令在内存中的地址。需要注意的是,这个寄存器的值不能用常规的指令进行取值和赋值,但这并不意味着不能做到,只是麻烦一些。
HI:这个寄存器用于乘除法。它被用来存放每次乘法结果的高32位,也被用来存放除法结果的余数。
LO:HI的孪生兄弟。它被用来存放每次乘法结果的低32位,也被用来存放除法结果的商。
CP0寄存器
CP0是一个系统控制协处理器,而CP0寄存器则是该协处理器工作时需要用到的一些寄存器。
SR:用于系统控制,决定是否允许异常和中断
Cause:记录异常和中断的类型EPC:保存异常或中断发生时的PC值,也就是发送异常或中断时CPU正在执行的那条指令的地址。当处理完成之后,CPU会根据这个地址返回到正常程序中继续往下执行。
PRId:处理器ID,用于实现个性的寄存器。