MIPS寄存器约定

对于在一个CPU上进行开发,掌握其工作的CPU的寄存器约定是非常重要的。

MIPS体系结构提供了32GPR(GENERAL PURPOSE REGISTER)。这32个寄存器的用法大致如下:

REGISTER   NAME          USAGE
$0                   $zero           常量0(constant value 0)
$2-$3             $v0-$v1        函数调用返回值(values for results and expression evaluation)
$4-$7             $a0-$a3        函数调用的前几个参数(arguments)
$8-$15           $t0-$t7          临时变量。子程序使用时无需保存,从上层函数来看,函数调用前后,寄存器的值可能会发生变化。
$16-$23         $s0-$s7       需要保存的变量。子程序使用时,必须保存原始值,并在返回前恢复,从上层函数来看,这些寄存器的值没有变化。
$24-$25        $t8-$t9           临时变量
$26-$27        $k0-$k1         保留给中断或自陷处理程序使用;因而值可能随时发生变化   
$28                $gp                全局指针(Global Pointer) 。利用gp做基址,对于gp指针前后32K范围的数据存取,只需要一条指令就可以完成。通常的做法是将一些小的全局数据(static extern)等,放在一起,用gp作指针。
$29                $sp                 堆栈指针(Stack Pointer) 。MIPS通常只在多重调用时,子程序出口和入口才调整堆栈指针,这个过程由子程序负责完成。
$30                $fp                  帧指针(Frame Pointer) (BNNfp is stale acutally, and can be simply used as $t8)
$31                $ra                  返回地址(return address)

对一个CPU的寄存器约定的正确用法是非常重要的。当然对C语言开发者不需要关心,因为COMPILERTAKE CARE。但对于KERNEL的开发或DRIVER开发的人就**必须**清楚。

一般来讲,你通过objdump -d可以清醒的看到寄存器的用法。

下面通过我刚才写的一个简单例子来讲解:

~/ vi Hello.c
"Hello.c" [New file]
/* Example to illustrate mips register convention
* -Author: BNN
* 11/29/2001
*/

int addFunc(int,int);
int subFunc(int);

void main()
{

int x,y,z;
x= 1;
y=2;
z = addFunc(x,y);
}


int addFunc(int x,int y)
{
int value1 = 5;
int value2;

value2 = subFunc(value1);
return (x+y+value2);

}

int subFunc(int value)
{
return value--;
}

上面是一个C程序,main()函数调用一个加法的子函数。让我们来看看编译器是如何产生代码的。

~/bnn:74> /bin/mips-elf-gcc -c Hello.o Hello.c -mips3 -mcpu=r4000 -mgp32 -mfp32 -O1

~/bnn:75> /bin/mips64-elf-objdump -d Hello.o
Hello.o: file format elf32-bigmips
Disassembly of section .text:

/* main Function */
0000000000000000 :
/*create a stack frame by moving the stack pointer 8
*bytes down and meantime update the sp value
*/
0: 27bdfff8 addiu $sp,$sp,-8
/* Save the return address to the current sp position.*/
4: afbf0000 sw $ra,0($sp)
8: 0c000000 jal 0
/* nop is for the delay slot */
c: 00000000 nop
/* Fill the argument a0 with the value 1 */
10: 24040001 li $a0,1
/* Jump the addFunc */
14: 0c00000a jal 28
/* NOTE HERE: Why we fill the second argument
*behind the addFunc function call?
* This is all about the "-O1" compilation optimizaiton.
* With mips architecture, the instruciton after jump
* will also be fetched into the pipline and get
* exectuted. Therefore, we can promise that the
* second argument will be filled with the value of
* integer 2.
*/
18: 24050002 li $a1,2
/*Load the return address from the stack pointer
* Note here that the result v0 contains the result of
* addFunc function call
*/
1c: 8fbf0000 lw $ra,0($sp)
/* Return */
20: 03e00008 jr $ra
/* Restore the stack frame */
24: 27bd0008 addiu $sp,$sp,8

/* addFunc Function */
0000000000000028 :
/* Create a stack frame by allocating 16 bytes or 4
* words size
*/
28: 27bdfff0 addiu $sp,$sp,-16
/* Save the return address into the stack with 8 bytes
* offset. Please note that compiler does not save the
* ra to 0($sp).
*Think of why, in contrast of the previous PowerPC
* EABI convention
*/
2c: afbf0008 sw $ra,8($sp)
/* We save the s1 reg. value into the stack
* because we will use s1 in this function
* Note that the 4,5,6,7($sp) positions will then
* be occupied by this 32 bits size register
*/
30: afb10004 sw $s1,4($sp)
/* Withe same reason, save s0 reg. */
34: afb00000 sw $s0,0($sp)
/* Retrieve the argument 0 into s0 reg. */
38: 0080802d move $s0,$a0
/* Retrieve the argument 1 into s1 reg. */
3c: 00a0882d move $s1,$a1
/* Call the subFunc with a0 with 5 */
40: 0c000019 jal 64
/* In the delay slot, we load the 5 into argument a0 reg
*for subFunc call.
*/
44: 24040005 li $a0,5
/* s0 = s0+s1; note that s0 and s1 holds the values of
* x,y, respectively
*/
48: 02118021 addu $s0,$s0,$s1
/* v0 = s0+v0; v0 holds the return results of subFunc
*call; And we let v0 hold the final results
*/
4c: 02021021 addu $v0,$s0,$v0
/*Retrieve the ra value from stack */
50: 8fbf0008 lw $ra,8($sp)
/*!!!!restore the s1 reg. value */
54: 8fb10004 lw $s1,4($sp)
/*!!!! restore the s0 reg. value */
58: 8fb00000 lw $s0,0($sp)
/* Return back to main func */
5c: 03e00008 jr $ra
/* Update/restore the stack pointer/frame */
60: 27bd0010 addiu $sp,$sp,16

/* subFunc Function */
0000000000000064 :
/* return back to addFunc function */
64: 03e00008 jr $ra
/* Taking advantage of the mips delay slot, filling the
* result reg v0 by simply assigning the v0 as the value
*of a0. This is a bug from my c source
* codes--"value--". I should write my codes
* like "--value", instead.
68: 0080102d move $v0,$a0


希望大家静下心来把上面的代码看懂。一定要注意编译器为什么在使用s0s1之前要先把她们SAVE起来,然后再RESTORE,虽然在这个例子中虽然main 函数没用s0s1


另外的一点是:由于我们加了“-O1”优化,编译器利用了“delay slot"来执行那些必须执行的指令,而不是简单的塞一个”nop"指令在那里。非常的漂亮。

最后,考大家一个问题,为了使得大家更加理解寄存器的用法:

*在写一个核心调度context switch()例程时,我们需要SAVE/RESTORE$t0-$t7吗?如果不,为什么?

*在写一个时钟中断处理例程时,我们需要SAVE/RESTORE$t0-$t7吗?如果是,为什么?
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MIPS寄存器文件是MIPS处理器中的一个重要组成部分,主要用于存储MIPS指令的操作数和运算结果。MIPS寄存器文件的设计原理可以分为以下几个方面: 1. 寄存器文件的大小:MIPS寄存器文件的大小通常为32位,即可以存储32位的数据。在MIPS处理器中,寄存器文件的大小是固定的,不可以改变。 2. 寄存器文件的寄存器数量:MIPS寄存器文件中总共有32个寄存器,每个寄存器都可以存储32位的数据。这些寄存器可以用于存储MIPS指令的操作数和运算结果。 3. 寄存器文件的访问方式:MIPS寄存器文件的访问方式是通过寄存器编号进行访问的。每个寄存器都有一个唯一的编号,可以通过这个编号访问对应的寄存器。在MIPS汇编语言中,寄存器的编号通常用$符号表示,例如$0表示寄存器0,$1表示寄存器1,以此类推。 4. 寄存器文件的寄存器内容:MIPS寄存器文件中的每个寄存器都有一个特定的用途。例如,$zero寄存器始终为0,$sp寄存器用于存储栈指针,$ra寄存器用于存储返回地址等。 5. 寄存器文件的读写方式:MIPS寄存器文件的读写方式是同步的,即在每个时钟周期中,寄存器文件只能读取或写入一个寄存器的数据。此外,寄存器文件中的数据可以同时读取,但是不能同时写入。 总的来说,MIPS寄存器文件的设计原理主要包括寄存器文件的大小、寄存器数量、访问方式、寄存器内容和读写方式等方面。这些设计原理可以保证MIPS处理器的高效运行,并且方便编写MIPS汇编语言程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值