Chinaunix首页 | 论坛 | 博客
  • 博客访问: 7068269
  • 博文数量: 702
  • 博客积分: 10821
  • 博客等级: 上将
  • 技术积分: 12031
  • 用 户 组: 普通用户
  • 注册时间: 2005-12-02 10:41
个人简介

中科院云平台架构师,专注于数字化、智能化,技术方向:云、Linux内核、AI、MES/ERP/CRM/OA、物联网、传感器、大数据、ML、微服务。

文章分类

全部博文(702)

分类: 云计算

2020-02-28 13:20:52

一、MIPS寄存器

  MIPS的系统结构及设计理念比较先进,其指令系统经过通用处理器指令体系MIPS I、MIPS II、MIPS III、MIPS IV、MIPS V,以及嵌入式指令体系MIPS16、MIPS32到MIPS64的发展。

  MIPS32的架构是一种基于固定长度的定期编码指令集,并采用导入/存储(load/store)数据模型。经改进,这种架构可支持高级语言的优化执行。在路由器中,经常使用的一种MIPS架构就是MIPS32。

  RISC的一个显著特点就是大量使用寄存器。因为寄存器的存取可以在一个时钟周期内完成,同时简化了寻找方式,所以,MIPS32的指令中除了加载/存储指令以外,都使用寄存器或者立即数作为操作数,以便让编译器通过保持对寄存器内数据的频繁存取进一步优化代码的生成性能。

  MIPS32寄存器分为两类:通用寄存器(GPR)和特殊寄存器。

1、通用寄存器(GPR)

  在MIPS体系结构中有32个通用寄存器,在汇编程序中可以用编号$0~$31表示,也可以用寄存器的名字表示,如$sp、$t1、$ta等,如图,堆栈是从内存的高地址方向向低地址方向增长的。

编号 寄存器名称 寄存器描述
0  zero 第0号寄存器,其值始终为0
1 $at 保留寄存器
2~3 $v0~v1 values, 保存表达式或函数返回结果
4-7 $a0~a3 aruments, 作为函数的前4个参数
8~15 $t0~$t7 temporaries,供汇编程序使用的临时寄存器
16~23 $s0~$s7 saved values,子函数使用时需要先保存原寄存器的值
24~25 $t8~t9 temporaries, 供汇编程序的临时寄存器,补充$t0~t7
26~27 $k0~$k1 保留,中断处理函数使用
28 $gp global pointer,全局指针
29 $sp stack pointer, 堆栈指针,指向堆栈的栈顶
30 $fp frame pointer, 保存栈指针
31 $ra return address, 返回地址
  • $0:即$zero,该寄存器总是返回0,为0这个有用常数提供了一个简洁的编码形式。在MIPS处理器的通用寄存器中,没有任何帮助运算判断的标志寄存器,要实现相应的功能时,都是通过测试两个寄存器是否相等完成的。MIPS编译器常常会使用slt、beq、bne等指令和由寄存器$0获得0值产生比较所有的比较条件,如相等、不等、小于等于、大于、大于等于。还可以用add指令创建move伪指令,如"move $t0, $t1; $t0=$t1"实际为“add $t0,$0,$t1; $t0= $t1 + 0"。使用MIPS伪指令可以简化任务。
  • $1 ($at) : 该寄存器为汇编保留,用做汇编器的暂时变量。
  • $2~$3($v0~$v1): 用于存放子程序的返回值或非浮点结果。当这两个寄存器不够存放返回值时,编译器通过内存来完成。
  • $4~$7($a0~$a3):用于将前4个参数传递给子程序,不够的用堆栈处理。$a0~$a3、$v0~$v1和$ra 一起完成子程序函数调用过程,分别用以传递参数、返回结果和存放返回地址。当需要使用更多的寄存器时就需要堆栈了。MIPS编译器总是为参数在堆栈中留有空间,以防有参数需要存储。
  • $8~$15($t0~$t7): 一个子函数可以不用保存并随意使用这些寄存器。在进行表达式计算时,这些寄存器是非常好的临时变量。在使用时需要注意,当调用一个子函数时,这些寄存器的值有可能被子函数破坏。
  • $16~$23($s0~$s7): 子函数必须保证当函数返回时这些寄存器的内容将恢复到函数调用以前的值,或者子函数里不使用这些寄存器或把它们保存在堆栈上并保存在函数退出时恢复。这种约定使这些寄存器非常适合作为寄存器变量,或者用于存放一些函数调用期间必须保存的原值。
  • $24~$25($t8~$t9): 同$t0~$t7,作为$t0~$t7寄存器补充。
  • $26~$27($k0~$k1): 通常被中断或异常处理程序使用,以保存一些系统参数。
  • $28($gp): C语言中有两种存储类型,分别是自动型和静态型。自动变量是一个函数中的局部变量。静态变量在进入和退出一个函数时都是存在的。为了简化静态数据的访问,MIPS保留了一个寄存器作为全局指针gp在编译时,数据需要在以gp为基指针的64KB范围内。
  • $29($sp): MIPS硬件并不直接支持堆栈,X86有单独的PUSH和POP指令,而MIPS没有单独的栈操作指令,所有对栈的操作都是统一的内存访问方式,单这并非不影响MIPS使用堆栈。在发生函数调用时,调用者把函数调用之后要用的寄存器压入堆栈,被调用者把返回地址寄存器$ra(并非任何时候都保存$ra)和保留寄存器压入堆栈。同时,调整堆栈指针,并在返回时从堆栈中恢复寄存器。
  • $30($fp): 不同编译器可能对该寄存器使用方法不同。GNU MIPS C编译器使用了栈指针(Frame Pointer)。SGI的C编译器则没有使用栈指针,只是把这个寄存器当成保存寄存器使用($s8),这虽然节省了调用和返回开销,但增加了代码生成的复杂度性。
  • $31 ($ra): 存放返回地址。MIPS有一个jar(jump-and-link,跳转并链接)指令,在跳转到某个地址时可把下一条指令的地址放到$ra中,用于支持子程序。例如,调用程序把参数放到$a0~$a3中,“jar X"指令跳到X过程,被调用时需要保存的寄存器为$a0~$a3、$s0~$s7、$gp、$sp、$fp、$ra。

2、特殊寄存器

  MIPS32架构中定义了3个特殊寄存器。分别为PC(程序计数器)、HI(乘除结果高位寄存器)和LO(乘除结果低位寄存器)。在进行乘法运算时,HI和LO保存乘法的运算结果,其中HI存储高32位,LO存储低32位;而在进行除法运算时,HI保存余数,LO存储商。
二、MIPS指令集初步

1、MIPS寻址方式

  MIPS架构的寻址模式有寄存器寻址、立即数寻址、寄存器相对寻址和PC相对寻址4种,其中寄存器相对寻址、PC相对寻址介绍如下:

1.1、寄存器相对寻址

  这种寻址模式主要被加载/存储指令使用,其对一个16位的立即数进行符号扩展,然后与指定通用寄存器的值相加,从而得到有效地址。

  通用寄存器GRP   +   16位立即数做符号扩展      =       有效地址

1.2、PC相对寻址

  这种寻址模式主要被转移指令使用。在转移指令中有一个16位的立即数,将其左移2位并进行符号扩展,然后与程序计数器PC的值相加,可得到有效地址。

  程序计数器PC     +      16位立即数左移2位并做符号扩展     =     有效地址

2、MIPS指令集

2.1、MIPS指令特点

  • MIPS固定4字节指令长度。
  • 内存中的数据访问(load/store)必须严格对齐(至少4字节对齐)。
  • 跳转指令只有26位目标地址,加上2位对齐位,可寻址28位寻址空间,即256MB。
  • 条件分支指令只有16位跳转地址,加上2位对齐位,共18位寻址空间,即256KB。
  • MIPS默认不把子函数的返回地址(就是调用函数的受害指令地址)存放到栈中,而是存放到$31($ra)寄存器中,这对那些叶子函数(在函数中不再调用其他函数的函数)有利。如果遇到嵌套函数,有其他机制处理。
  • 流水线效应。MIPS采用了高度的流水线,其中一个最重要的效应就是分支延迟效应。在分支跳转语句后面的那条语句叫做分支延迟槽。实际上,在程序执行到分支语句时,当它刚把要跳转到的地址填充好(填充到代码计数器里),还没有完成本条指令时,分支语句后面的那个指令就已经执行了,其原因就是流水线效应 ---- 几条指令同时执行,只是处于不同的阶段。

  流水线效应:

  mov $a0, $s2

  jalr strrchr

  move $a0, $s0

  在执行第2行跳转分支时,第3行的move指令已经执行完了。因此,在上面指令序列中,strrchr函数的参数来自第3行的$s0,而不是第1行的$s2。

  从流水线效应中可以看出,是否正确理解MIPS指令的这些特点会直接影响我们对MIPS程序逆向分析的结果,因此,我们需要熟悉把握这些特点。


2.2、指令格式

  所有MIPS指令的长度相同,都是32位。为了让指令的格式刚好合适,设计者做了折中:将所有指令定长,但是不同的指令有不同的格式。在MIPS架构中,指令的最高6位均为Opcode码,剩下的26位可以将指令分为3种类型,分别为R型、I型和J型。

  • R型指令用连续3个5位二进制码表示3个寄存器的地址,然后用1个5位二进制码表示移位的位数(如果未使用移位操作,则全为0),最后是6位的Function码(它与Opcode码共同决定R型指令的具体操作方式)。
  • I型指令则用连续2个5位二进制码表示2个寄存器的地址,然后是由1个16位二进制码表示1个立即数二进制码。
  • J型指令用26位二进制码表示跳转目标的指令地址(实际的指令地址应为32位,其中最低2位为“00”,最高4位由PC当前地址决定)。
类型 格式
R Opcode(6) Rs(5) Rt(5) Rd(5) Shamt(5) Funct(6)
I Opcode(6) Rs(5) Rt(5) Immediate(16)
J Opcode(6) Address(26)
  • Opcode: 指令基本操作,称为操作码。
  • Rs: 第一个源操作数寄存器。
  • Rt: 第二个源操作数寄存器。
  • Rd: 存放操作结果的目的操作数。
  • Shamt: 位移量。
  • Funct: 函数,这个字段选择Opcode操作某个特定变体。

3、汇编常用的指令

  注意:$Rd表示目的寄存器, $Rs表示源寄存器,$Rt表示作为中间缓存的寄存器,"imm"表示立即数,“MEM[]“表示RAM中的一段内存,“offset"表示偏移量。

3.1、LOAD/STORE指令

  LOAD/STORE指令有14条,分别是lb、lbu、lh、lhu、ll、lw、lwl、lwr、sb、sc、sh、sw、swl和swr。

  以"l"开头的都是加载指令,以"s"开头的都是存储指令,这些指令用于从存储器中读取数据,或者将数据保存在存储器中。

3.1.1、LA(Load Address) 指令用于将一个地址或标签存入一个寄存器。

语法 实例 备注
la $Rd, Label la $t0, val_1 复制val_1表示的地址到$t0寄存器中,其中val_1是一个Label

3.1.2、LI(Load Immediate)指令用于将一个立即数存入一个通用寄存器。

语法 实例 备注
lw $Rt, offset($Rs) lw $s0, 0($sp) "$s0 = MEM[$sp+0]",相当于取堆栈地址偏移0内存word长度的值到$s0中

3.1.3、LW(Load Word) 指令用于从一个指定的地址加载一个word类型的值到一个寄存器中。

语法 实例 备注
lw $Rt, offset($Rs) lw $s0, 0($sp) "$s0=MEM[$sp+0];",相当于取堆栈地址偏移0内存word长度的值到$s0中

3.1.4、SW(Store Word)用于将源寄存器中的值存入指定的地址。

语法 实例 备注
sw $Rt, offset($Rs) sw $a0,0($sp) "MEM[$sp+0]=$a0;",相当于将$a0寄存器中一个word大小的值存入堆栈,且$sp自动堆栈

3.1.5、MOVE指令用于寄存器之间值的传递。

语法 实例 备注
move $Rt, $Rs move $t5, $t1 $t5=$t1;


3.2、算术运算指令

  MIPS汇编指令的算术运算特点如下:

  • 算术运算指令的所有操作数都是寄存器,不能直接使用RAM地址或间接寻址。
  • 操作数大小都为word(4 Byte)。
  • 算术运算指令有21条,分别为add、addi、sub、subu、clo、clz、slt、slti、sltiu、sltu、mul、mult、madd、maddu、msub、msubu、div和divu,实现了加、减、比较、乘、乘累加、除等运算。
指令格式与实例 注释
add $t0,$t1,$t2 "$t0=$t1+$t2;",带符号数相加
sub $t0,$t1,$t2 "$t0=$t2 - $t2;", 带符号数相减
addi $t0,$t1,5 $t0 = $t1 + 5;
addu $t0,$t1,$t2 "$t0 = $t1 + $t2;",无符号数相加
subu $t0, $t1, $t2 "$t0 = $t2 - $t2;",无符号数相减
mult $t3, $t4 "$t3 * $t4", 把64 Bits的积存储到"Lo,Hi"中,即"(Hi,Lo)=$t3 * $t4;"
div $t5, $t6 "$LO=$t5/$t6", $LO为商的整数部分;"$HI=$t5 mod $t6", $HI为余数
mfhi $t0 $t0 = $HI
mflo $t1 $t1 = $LO

以上是MIPS指令基础。



阅读(7209) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~