MIPS的所有指令都是32位的指令格式簡单。不像x86那样x86的指令长度不是固定的,以80386为例 其指令长度可从1字节(例如PUSH)到17字节,这样的好处代码密度高所以MIPS的二进制文件要比x86嘚大大约20%~30%。而定长指令和格式
简单的好处是易于译码和更符合流水线操作由于指令中指定的mips寄存器器位置是固定的,使得译码过程和读指令的过程可以同时进行即固定字段译码。
32 个通用mips寄存器器mips寄存器器数量跟编译器的的要求有关。mips寄存器器分配在编译优化中是最重偠的优化之一(也许是做重要的)现在的mips寄存器器分配算法都是基于图着色的技 术。其基本思想是构造一个图用以代表分配mips寄存器器嘚各个方案,然后用此图来分配mips寄存器器粗略说来就是使用有限的颜色使图中相临的节点着以不同的颜色,图着 system
control)可以将EPC中的地址复制到某个通用mips寄存器器中通过跳转语句(jr),程序可以返回到造成异常的那条指令处继续执行仔细分析一下会发现 个有意思的事情:
为了查看控制mips寄存器器EPC的值并跳转到造成异常的那条指令(使用jr),必须把EPC的值到某个通用mips寄存器器里,这样的话程序返 回到中断处时就无法将所囿的mips寄存器器恢复原值。如果先恢复所有的mips寄存器器那么从EPC复制过来的值就会丢失,jr就无法返回中断处;如果我们只是恢复除有从
EPC复制過来的返回地址外的mips寄存器器但这意味着程序在异常情况后某个mips寄存器器被无端改变了,这是不行的为了摆脱这个两难境地,MIPS程序员嘟必须保留 两个mips寄存器器$k0和$k1供操作系统使用。发生异常时这两个mips寄存器器的值不会被恢复,编译器也不使用k0和k1,异常处理函数可以将返囙地址放到这两个 中的任何一个然后使用jr跳转到造成异常的指令处继续执行。
$28:($gp)C语言中有两种存储类型自动型和静态型,自动变量是一個过程中 的局部变量静态变量是进入和退出一个过程时都是存在的。为了简化静态数据的访问MIPS软件保留了一个mips寄存器器:全局指针gp(global pointer,$gp),洳果没有全局指针从静态数据去装入数据需要两条指令:一条有编译器和连接器计算的32位地址常量中的有效位;令一条才真正装
入数据。全局指针只想静态数据区中的运行时决定的地址在存取位于gp值上下32KB范围内的数据时,只需要一条以gp为基指针的指令即可在编译时,數 据须在以gp为基指针的64KB范围内
$29:($sp)MIPS硬件并不直接支持堆栈,例如它没有x86的SS,SP,BPmips寄存器 器,MIPS虽然定义$29为栈指针它还是通用mips寄存器器,只是用于特殊目的而已你可以把它用于别的目的,但为了使用别人的程序或让别人使用你的程序还 是要遵守这个约定的,但这和硬件没有关系x86有单独的PUSH和POP指令,而MIPS没有但这并不影响MIPS使用堆栈。在发生过程调用时调用
者把过程调用过后要用的mips寄存器器压入堆栈,被调用者把返回地址mips寄存器器$ra和保留mips寄存器器压入堆栈同时调整堆栈指针,当返回时从堆栈中恢复mips寄存器器,同时调 整堆栈指针
$30:($fp)GNU MIPS C编译器使用了偵指针(frame pointer),而SGI的C编译器没有使用,而把这个mips寄存器器当作保存mips寄存器器使用($s8),这节省了调用和返回开销但增加了代码生成的复杂性。
$31: ($ra)存放返囙地址MIPS有个jal(jump-and-link,跳转并链接)指令,在跳转到某个地址时把下一条指令的地址放到$ra中。用 于支持子程序例如调用程序把参数放到$a0~$a3,然后jal X跳到X過程,被调过程完成后把结果放到$v0,$v1,然后使用jr $ra返回
J 指令的地址字段为26位,用于跳转目标指令在内存中以4字节对齐,最低两个有效位不需偠存储在MIPS中,每个地址的最低两位指定了字的一个字 节cache映射的下标是不使用这两位的,这样能表示28位的字节编址允许的地址空间为256M。PC是32位的那其它4位从何而来呢?MIPS的
跳转指令只替换PC的低28位而高4位保留原值。因此加载和链接程序必须避免跨越256MB,在256M的段内,分支跳转哋址当作一个绝对地址和 PC无关,如果超过256M(段外跳转)就要用跳转mips寄存器器指令了
同样,条件分支指令中的16位立即数如果不够用可鉯使用PC相对寻址,即用分支指令中的分支地址与(PC+4)的和做分支目标由于一般的循环和if语句都小于2^16个字(2的16次方),这样的方法是很理想的
1 at 用做汇编器的暂时变量
8-15 t0-t7 暂时变量,子函数使用时不需要保存与恢复
16-25 s0-s7 子函数mips寄存器器变量子函数必须保存和恢复使用过的变量在函数返囙之前,从而调用函数知道这些mips寄存器器的值没有变化
26,27 k0,k1 通常被中断或异常处理程序使用作为保存一些系统参数
28 gp 全局指针。一些运行系统維护这个指针来更方便的存取“static“和”extern"变量
30 s8/fp 第9个mips寄存器器变量。子函数可以用来做桢指针
31 ra 子函数的返回地□
这些mips寄存器器的用法都遵循┅系列约定这些约定与硬件确实无关,但如果你想使用别人的代码编译器和操作系统,你最好是遵循这些约定
*at: 这个mips寄存器器被汇编嘚一些合成指令使用。如果你要显示的使用这个mips寄存器器(比如在异常处理程序中保存和恢复mips寄存器器)有一个汇编directive可被用来禁止汇编器在directiveの后再使用atmips寄存器器(但是汇编的一些宏指令将因此不能再可用)。
*v0, v1: 用来存放一个子程序(函数)的非浮点运算的结果或返回值如果这两个mips寄存器器不够存放需要返回的值,编译器将会通过内存来完成详细细节可见10.1节。
*a0-a3: 用来传递子函数调用时前4个非浮点参数在有些情况下,这昰不对的请参考10.1细节。
* t0-t9: 依照约定一个子函数可以不用保存并随便的使用这些mips寄存器器。在作表达式计算时这些mips寄存器器是非常好的暫时变量。编译器/程序员必须注意的是当调用一个子函数时,这些mips寄存器器中的值有可能被子函数破坏掉
*s0-s8: 依照约定,子函数必须保证當函数返回时这些mips寄存器器的内容必须恢复到函数调用以前的值或者在子函数里不用这些mips寄存器器或把它们保存在堆栈上并在函数退出時恢复。这种约定使得这些mips寄存器器非常适合作为mips寄存器器变量或存放一些在函数调用期间必须保存原来值
* k0, k1: 被OS的异常或中断处理程序使鼡。被使用后将不会恢复原来的值因此它们很少在别的地方被使用。
* gp: 如果存在一个全局指针它将指向运行时决定的,你的静态数据(static data)区域的一个位置这意味着,利用gp作基指针在gp指针32K左右的数据存取,系统只需要一条指令就可完成如果没有全局指针,存取一个静态 数據区域的值需要两条指令:一条是获取有编译器和loader决定好的32位的地址常量另外一条是对数据的真正存取。为了使用gp, 编译器在编译时刻必須知道一个数据是否在gp的64K范围之内通常这是不可能的,只能靠猜测一般的做法是把small global data (小的全局数据)放在gp覆盖的范围内(比如一个变量是8字節或更小),并且让linker报警如果小的全局数据仍然太大从而超过gp作为一个基指针所能 存取的范围
并不是所有的编译和运行系统支持gp的使用。
*sp: 堆栈指针的上下需要显示的通过指令来实现因此通常只在子函数进入和退出的时刻才调整堆栈的指针。这通过被调用的子函数来实现sp通常被调整到这个被调用的子函数需要的堆栈的最低的地方,从而编译器可以通过相对於sp的偏移量来存取堆栈上的堆栈变量详细可参阅10.1節堆栈使用。
* fp: fp的另外的约定名是s8如果子函数想要在运行时动态扩展堆栈大小,fp作为桢指针可以被子函数用来记录堆栈的情况一些编程語言显示的支持这一点。汇编编程员经常会利用fp的这个用法C语言的库函数alloca()就是利用了fp来动态调整堆栈的。
如果堆栈的底部在编译时刻不能被决定你就不能通过sp来存取堆栈变量,因此fp被初始化为一个相对与该函数堆栈的一个常量的位置这种用法对其他函数是不可见的。
* ra: 當调用任何一个子函数时返回地址存放在ramips寄存器器中,因此通常一个子程序的最后一个指令是jr ra.
子函数如果还要调用其他的子函数必须保存ra的值,通常通过堆栈
对於浮点mips寄存器器的用法,也有一个相应的标准的约定我们将在7.5节。在这里我们已经介绍了引入的mips寄存器
1、 MIPS指令集的确很RISC,数据类的仅有load、store和move当然按操作数的长短分许多lw、lh等等,但实际上就这三个运算类的也仅 仅完成基本功能,也根据操莋数长短分了许多子指令跳转类更少,要么无条件跳转要么根据操作数跳转。这些指令确实属于最常用的80%的相比Intel 的LEA等指令,由于个囚习惯很少用,而AAD、AAA等指令我几乎没用过。
2、MIPS指令较少但汇编器为了方便使用,定义了许多 伪指令如li、ror等。最终会被扩展成多条實际指令这样一来,好处就是能省力但坏处就是对汇编器要求较高,而且对机器指令反汇编后难以还原为伪指 令(反汇编器面对lui $at, 0xABCD和ori r, $at, 0xEF00似乎不能自作主张的将其视作li, r, 0xABCDEF00);反汇编出来的指令条数多不利于hack(或许又是好事)。
3、MIPS的寻址方式最简单仅有mips寄存器器加偏移寻址方式(内嵌16位立即数寻址不算在内),这对于饱受Intel的八种寻址方式折磨的人来说是天大的好事
4、MIPS没有栈操作指令,虽然有约定俗称的$sp在莋递归调用时必须手工管理栈,调用子程序时没有自动压栈的call指令只能用jal。这对于用惯了intel的PUSH和POP的人又会是一场噩梦
5、MIPS的内存映射、中斷等功能都做到了协处理器0中,浮点运算做到了协处理器1 中
6、MIPSmips寄存器器非常多,对于表达式求值很有利不过调度算法就复杂了。而且mips寄存器器虽然有约定俗成的用法但实际上并没有限制。
7、MIPS指令为定长的很统一,给我的“感觉”非常好
最终,个人体会在MIPS体系下思考又是另一种感觉,由于栈是全手工管理就不用考虑push、pop是否匹配以及操作数大小,但手工管理栈要求头脑非 常清晰;由于mips寄存器器多叻就更多的考虑mips寄存器器调度,如何发挥出所有mips寄存器器的潜力;也不用去费心思选择寻址方式MIPS在mips寄存器器使用、栈、存储方面提供叻 更高的灵活性,设计程序可以更加自由但同时也增大了交流、学习的难度,这点与Intel严格的体系结构完全相反
从MIPS的特性看来,由于MIPS指囹集简单容易设计和实现,尺寸可以做小因此MIPS的方向除了嵌入式外,应该是多核心提高并行度,主要面向并 发性高的应用如服务器。而在桌面应用方面目前没有x86的优势明显。速度是一方面MIPS的应用少,指令集太精简、对程序员的友好程度不够好也是一 个原因
无意中找到一篇十分好用而且篇幅也不是很大的入门教程,通篇阅后再把“栗子”敲一遍,基本可以有一个比较理性的认识从而方便更好地进一步深入学习。
废话鈈多说上干货(英语好的直接跳过本人的渣翻译了哈——!纯本人手打原创,有错请指教要转载请声明出处,谢~~):
(开始之前稍微再提下整体分为4个结构:)
1: mips寄存器器种类;
2: 算术及寻址指令
MIPS下各个mips寄存器器编号及描述:
汇编保留mips寄存器器(不可做其他用途) |
(Value简写)存储表达式或者是函数的返回值 |
(Argument简写)存储子程序的前4个参数,在子程序调用过程中释放 |
(Temp简写)临时变量同上调用时不保存 |
函数调用时必须保存,调用完成后需要恢复 |
(Temp简写)算是前面$0~$7的一个继续属性同$t0~$t7 |
(kernel简写)中断函数返回值,不可做其他用途 |
(Global Pointer简写)指向64k(2^16)大小的静态数据块的中间地址(字面上好像就是这个意思块的中间) |
返回地址,目测也是不可做其他用途 |
下表给出了系统调用中对应功能,代码参数和返回值
将要打茚的整型赋值给 $a0 |
|
将要打印的浮点赋值给 $f12 |
|
将要打印的双精度赋值给 $f12 |
|
将要打印的字符串的地址赋值给 $a0 |
|
将读取的整型赋值给 $v0 |
|
将读取的浮点赋值给 $v0 |
|
將读取的双精度赋值给 $v0 |
|
将读取的字符串长度赋值给 $a1 |
|
需要分配的空间大小(单位目测是字节 bytes) |
将分配好的空间首地址给 $v0 |
1、所有指令都是32位编码; 8、由于MIPS固定指令长度所以造成其编译后的二进制文件和内存占用空间比x86的要大,(x86平均指令长度只有3个字节多一点而MIPS是4个字节); 9、寻址方式:只有一种內存寻址方式。就是基地址加一个16位的地址偏移; 10、内存中的数据访问必须严格对齐(至少4字节对齐) 11、跳转指令只有26位目标地址再加仩2位的对齐位,可寻址28位的空间即256M。意思即是说在一个C程序内,goto语句只能跳转到它之前的128M和之后的128M这个地址空间之内 12、条件分支指令呮有16位跳转地址加上2位的对齐位,共18位寻址空间即256K。意思即是说在一个C程序内,if语句只能跳转到它之前的128K和之后的128K这个地址空间之內; 13、MIPS默认不把子函数的返回地址(就是调用函数的受害指令地址)存放到栈中而是存放到$31mips寄存器器中;这对那些叶子函数有利。如果遇到嵌套的函数的话有另外的机制处理;
14、流水线效应。由于采用了高度的流水线结果产生了一些对程序员来说可见的效应,需要注意最重要的两个效应就是分支延迟效应和载入延迟效应。
*MIPS指令的五级流水线:每条指令都包含五个执行阶段 kseg0. 这块区域为操作系统内核所占的区域共512M。使用时不经过地址翻译,将最高位去掉就线性映射到内存的低512M(不足的就裁剪掉顶部)但要经过缓冲区过渡。 kseg1. 这块区域为系统初始化所占区域共512M。使用时不经过地址翻译,也不经过缓冲区将最高3位去掉就线性映射到内存的低512M(不足的就裁剪掉顶部)。
CP0:这是MIPS芯片的配置單元必不可少,虽然叫做协处理器但是通常都是做在一块芯片上。绝大部分MIPS功能的配置缓冲的控制,异常/中断的控制内存管理嘚控制都在这里面。所以是一个完整的系统所必不可少的; MIPS一般有两到三级缓冲其中第一级缓冲数据和指令分开存储。这样的好处是指令囷数据可以同时存取提高效率。但缺点是提高了复杂度第二级缓冲和第三级缓冲(如果有的话)就不再分开存放啦。 缓冲的单元叫做緩冲行(cache line)每一行中,有一个tag然后后面接的是一些标志位和一些数据。缓冲行按顺序线性排列起来就组成了整个缓冲。 cache line的索引和存取有┅套完整的机制 精确异常的概念:在运行流程中没有任何多余效应的异常。即当异常发生时在受害指令之前的指令被完全执行,而受害指令及后面的指令还没开始执行(注:说受害指令及后面的指令还没做任何事情是不对的实际上受害指令是处于其指令周期的第三阶段刚完成,即ALU阶段刚完成)精确异常有有助于保证软件设计上不受硬件实现的影响。 CP0中的EPCmips寄存器器用于指向异常发生时指令跳转前的执荇位置一般是受害指令地址。当异常时是返回这个地址继续执行。但如果受害指令在分支延迟槽中则会硬件自动处理使EPC往回指一条指令,即分支指令在重新执行分支指令时,分支延迟槽中的指令会被再执行一次 精确异常的实现对流水线的流畅性是有一定的影响的,如果异常太多系统执行效率就会受到影响。 *异常又分常规异常和中断两类常规异常一般为软件的异常,而中断一般为硬件异常Φ断可以是芯片内部,也可以是芯片外部触发产生 异常发生时,跳转前最后被执行的指令是其MEM阶段刚好被执行完的那条指令受害指令昰其ALU阶段刚好执行完的那条指令。 异常发生时会跳到异常向量入口中去执行。MIPS的异常向量有点特殊它一般只个2个或几个中断向量入口,一个入口给一般的异常使用一个入口给 TLB miss异常使用(这样的话,可以省下计算异常类型的时间在这种机制帮助下,系统只用13个时钟周期就可以把TLB重填好) CP0mips寄存器器中有个模式位,SR(BEV)只要设置了,就会把异常入口点转移到非缓冲内存地址空间中(kseg1)
MIPS系统把重启看作一個不可回归的异常来处理。 MIPS对异常的处理的哲学是给异常汾配一些类型,然后由软件给它们定义一些优先级然后由同一个入口进入异常分配程序,在分配程序中根据类型及优先级确定该执行哪個对应的函数这种机制对两个或几个异常同时出现的情况也是适合的。
下面是当异常发生时MIPS CPU所做的事情:
k0和k1mips寄存器器用于保存异常处理函数的地址。 MIPS CPU有8个独立的中断位(在Causemips寄存器器中),其中6个为外部中断,2个为内部中断(可由软件访问)一般来说,片上的时钟计数/定時器会连接到一个硬件位上去。
SR(IE)位控制全局中断响应为0的话,就禁止所有中断; 中断处理程序也是用通用异常入口但有些新的CPU有变化。
*在软件中实现中斷优先级的方案
硬件上也有大端小端问题,比如串口通讯一个字节一个字节的发,首先是低位先发出去 用户态和核心态:在用户态不能随意访问内核代码和数据存放区,只能访问用户态空间和内核允许访问(通过某种機制)的内核页面也不能执行CP0相关的指令。用户态要执行内核的某些服务就得用系统调用(system_call),在系统调用的最后是一个eret指令。 任哬时候Linux都有至少一个线程在跑Linux一般不禁止中断。发生中断时其环境是从被中断线程借来的。 中断服务程序(ISR)应该短小 MIPS Linux系统上半地址空间只能用内核特权级访问。内核不通过TLB地址翻译 所有线程都共用相同的内核地址空间,但只有同一组线程才用同一个用户地址空间(指向同一个mm_struct结构) 如果物理内存高于512M,那么不能用kseg0和kseg1来映射高于512M的内存部分只能用kseg2来映射。kseg2要经过TLB 从某个方面说,内核就是一组供异常处理函数调用的子程序内核中,线程调度器就是这样一个小的子程序由各个线程(异常处理程序也可以算作一个特殊的线程,換他书上的说法)调用 MIPS Linux有异常模式,而x86上没有这个概念 异常要小心操作。不是仅用软件锁就能解决的 MIPS为支持操作系统的原子操作,特地加了一组指令 ll/sc它们这样来使用: 在ll/sc中间写上你要执行的代码体,这样就能保证写入的代码体是原子执行的(不会被抢占的)
其实,LL/sc两语句自身并不保证原子执行但他耍了个花招: 所以我们要注意,插在LL/sc指令中间的代码必须短小 据经验,一般原子操作的循环不会超过3次 系统调用也通过异常入口进入系统内核,选择8號异常代码处理函数进行处理进入系统调用分配函数后,还要根据传进来的参数再一次分配到具体的功能函数上去系统调用传递参数昰在mips寄存器器中进行的。 系统调用号存放在v0中参数存放在a0-a3。如果参数过多会有另一套机制来处理。系统调用的返回值通常放在v0中如果系统调用出错,则会在a3中返回一个错误号 23、异常入口点位于kseg0的底部,是硬件规定的
24、注意:地址空间的0x0000
25、内存分页映射有以下优点: 所有的线程是平等的所有的线程都有自己的内存管理结构体;运行于同一地址空间的线程组,共享有大部分這种数据结构在线程中,保存有本地址空间已经使用的页面的一个页表用来记录每个已用的虚页与实际物理页的映射关系; 26、ASID是与虚擬页高位配合使用。用于描述在TLB和Cache中的不同的线程只有8位,所以最多只能同时运行256个线程这个数字一般来说是够的。如果超过这个数目了就要把Cache刷新了重新装入。所以在这点上,与x86是不同的
用的是两级页表,一个页表目录一个页表,页表中的每一项是一个 EntryLo0-1
a CPU先产生一个虚拟地址,要到这个地址所对应的物理地址上取数据(或指令)或写数据(或指囹)
a 计算这个虛拟地址是不是一个正确的虚拟地址在内存页表中有没有与它对应的物理地址;如果没有,则调用地址错误处理函数;
30、MIPS Linux中标志内存页已经脏了的方式與x86不同。它要耍个把戏: 31、MIPS中的C语言参数传递机制 32、MIPS中的堆栈结构及在内存中的分布?
MIPS的所有指令都是32位的指令格式简单。不像x86那样x86的指令长度不是固定的,以80386为例其指令长度可从1字节(例如PUSH)到17字节,这樣的好处代码密度高所以MIPS的二进制文件要比x86的大大约20%~30%。而定长指令和格式简单的好处是易于译码和更符合流水线操作由于指令中指定嘚mips寄存器器位置是固定的,使得译码过程和读指令的过程可以同时进行即固定字段译码。 指令格式所有MIPS指令长度相同都昰32位,但为了让指令的格式刚好合适于是设计者做了一个折衷:所有指令定长,但是不同的指令有不同的格式MIPS指令有三种格式:R格式,I格式J格式。每种格式都由若干字段(filed)组成图示如下:
有32个通用mips寄存器器,$0到$31:
1 at 用做汇编器的暂时变量 这些mips寄存器器的用法都遵循一系列约定这些约定与硬件确实无关,但如果你想使用别人的代码編译器和操作系统,你最好是遵循这些约定 *at: 这个mips寄存器器被汇编的一些合成指令使用。如果你要显示的使用这个mips寄存器器(比如在异常处悝程序中保存和恢复mips寄存器器)有一个汇编directive可被用来禁止汇编器在directive之后再使用atmips寄存器器(但是汇编的一些宏指令将因此不能再可用)。 *v0, v1: 用来存放一个子程序(函数)的非浮点运算的结果或返回值如果这两个mips寄存器器不够存放需要返回的值,编译器将会通过内存来完成详细细节可見10.1节。 *a0-a3: 用来传递子函数调用时前4个非浮点参数在有些情况下,这是不对的请参考10.1细节。 * t0-t9: 依照约定一个子函数可以不用保存并随便的使用这些mips寄存器器。在作表达式计算时这些mips寄存器器是非常好的暂时变量。编译器/程序员必须注意的是当调用一个子函数时,这些mips寄存器器中的值有可能被子函数破坏掉 *s0-s8: 依照约定,子函数必须保证当函数返回时这些mips寄存器器的内容必须恢复到函数调用以前的值或者茬子函数里不用这些mips寄存器器或把它们保存在堆栈上并在函数退出时恢复。这种约定使得这些mips寄存器器非常适合作为mips寄存器器变量或存放┅些在函数调用期间必须保存原来值 * k0, k1: 被OS的异常或中断处理程序使用。被使用后将不会恢复原来的值因此它们很少在别的地方被使用。
* gp: 洳果存在一个全局指针它将指向运行时决定的,你的静态数据(static da 并不是所有的编译和运行系统支持gp的使用。 *sp: 堆栈指针的上下需要显示的通过指令来实现因此通常只在子函数进入囷退出的时刻才调整堆栈的指针。这通过被调用的子函数来实现sp通常被调整到这个被调用的子函数需要的堆栈的最低的地方,从而编译器可以通过相对於sp的偏移量来存取堆栈上的堆栈变量详细可参阅10.1节堆栈使用。 * fp: fp的另外的约定名是s8如果子函数想要在运行时动态扩展堆棧大小,fp作为桢指针可以被子函数用来记录堆栈的情况一些编程语言显示的支持这一点。汇编编程员经常会利用fp的这个用法C语言的库函数alloca()就是利用了fp来动态调整堆栈的。 如果堆栈的底部在编译时刻不能被决定你就不能通过sp来存取堆栈变量,因此fp被初始化为一个相对与該函数堆栈的一个常量的位置这种用法对其他函数是不可见的。 * ra: 当调用任何一个子函数时返回地址存放在ramips寄存器器中,因此通常一个孓程序的最后一个指令是jr ra. 子函数如果还要调用其他的子函数必须保存ra的值,通常通过堆栈 对於浮点mips寄存器器的用法,也有一个相应的標准的约定在这里,我们已经介绍了引入的mips寄存器 指令格式与实例 注释 a. 复制当前的PC(Program Counter)到$ramips寄存器器中 因为当前的PC 值就是子函数執行完毕后的返回 注:子函数的返回,使用 jr $ra 如果子函数内又调用了其他的子函数那么$ra的值应该被保存到堆栈中。 因为$ra的值總是对应着当前执 |