《计算机组成原理:硬件/软件接口》(MIPS版)第二章:MISP汇编指令

前言

本系列文章将梳理《计算机组成原理》这门课的相关知识点,教材使用的是《计算机组成原理:硬件/软件接口》MIPS版,原书第五版。

本文所属章节为MIPS汇编指令章节,重点内容为MIPS汇编指令的格式、五种寻址方式以及MIPS汇编对过程的支持。


一. MISP32指令集概述

MIPS是一种RISC指令集,即精简指令集,MISP指令都是32位长。

常见的指令集:MIPS,ARMv7, ARMv8,Intel x86

MISP中的运算操作数必须来自寄存器或者指令本身,不像X86。

MISP一共有32个32位的寄存器,一共128B。

  • 保存寄存器:$s0-$s7
  • 临时寄存器:$t0-$t7
  • 零寄存器:$zero

二. MISP三类汇编指令

1. MISP的三种指令格式

MISP有三种指令格式,分别是R型,I型和J型。

下文将逐一进行解释。

1.1 机器码字段和寄存器号

  • op:操作码
  • rs:源寄存器
  • rt:第二个源寄存器
  • rd:目的寄存器
  • shamt:shift amount 偏移量
  • funct:功能码
  • $t0-$t7是8-15号寄存器
  • $s0-$s7是16-23号寄存器

1.2 指令格式

  • R型指令格式:op(6) + rs(5) + rt(5)+ rd(5) + shamt(5) + funct(6)
    • 常见指令:add、sub、sll、srl、sltjr寄存器跳转指令
    • funct:add的功能码是32,sub的功能码是34
    • 移位指令sll、srl的rs字段为0(之前说错了,说成了rt字段!)
  • I型指令格式:op(6) + rs(5) + rt(5) + address(16)
    • 常见指令:
      • 立即数系列:addi、ori。
      • 数据传送指令:sw、lw。
      • 决策指令:beq
    • 注意:
      • addiori中,目的寄存器变成了rt ,address字段存立即数。
      • lw的op是35,sw的op是43。
      • rs永远是内存地址,所以lw和sw中会形成“交叉现象”。例如,lw $s0,20($s1)指令中,rs存放的是s1,rt存放的是s0,address存放20;sw $t0,8($s1)指令中,rs存放的是s1,rt存放的是t0。
      • 解释3:beq指令中,address存标签的偏移量。
  • J型指令格式:op(6) + address(26)

2. 运算指令

  • 加法add t0,t1,t2,R型指令,将t1和t2结果相加放到t0。
  • 减法sub t0,t1,t2,R型指令,将t1减去t2的结果存到t0。
  • 加/减立即数addi t0,t1,100,I型指令,(无减立即数指令
  • 逻辑按位操作
    • 按位与and t0,t1,t2
    • 按位或or t0,t1,t2
    • 取反指令MISP中用或非指令nor代替NOT指令,而根据离散数学,NOT(A OR 0) = NOT A,故nor $t1,$t0,$zero,表示将t0的值取非。
    • 立即数与andi $s1,$s2,100,I型,s1为目的寄存器
    • 立即数或ori $s1,$s2,100,I型
  • 逻辑移位:空出来的位置补0。
    • 逻辑左移:sll s0,s2,2,2是shamt字段,s0是rd字段,s2是rt字段,rs字段为空。
    • 逻辑右移:srl s0,s2,2
  • 算术右移:空出来的位用符号位填充。
    • sra s0,s2,2

例题:翻译下面C语言代码:B[8] = A[i-j],假设i和j分别赋值给寄存器$s3$s4,数组A和B的基地址分别在寄存器$s6$s7中。

解答
这下面的解答有几个注意的点:
①MISP中每次进行数组访问,都要将偏移左移两位。
②进行lw的时候,一般直接加上基地址。

sub $t0, $s3,$s4  # i-j
sll $t1,$t0,2 # MISP中数组访问一定要 << 2 转为字节地址
add $t1,$s0,$t1 # &A[i-j]
lw $t2, (0)$t1 # tmp = A[i-j]
sw $t2, (32)$s7 # B[8] = tmp

3. 数据传送指令

  • 寄存器数据送到内存lw $s0,20($s1)
    • 作用:将a[5] 送到s0。
    • 内存是按照字节编址,而a[5]实际上是5个字!
  • 寄存器间的数据传送
    • 写法1:addi s1,t0,zero—也可以用于将立即数放入寄存器。
    • 写法2:伪指令。move s1, t0
  • 取立即数li $s2,10,伪指令

3.1 装载32位立即数

addi的address字段用来存放立即数,但是这个字段只有16位。

那么我们要如何装载32位的立即数到寄存器中呢?

一般有两种做法:

  • 方法①:lui + ori
    • 第一步,使用lui将立即数的高16位放入寄存器。lui $s2,10A2,这个指令将立即数放到寄存器的高16位,并且把低16位设置为0。
    • 第二步,将立即数的低16位和寄存器进行或运算。ori $s2,7FFF
  • 方法②:直接使用li指令
    • li $s2,10A27FFFF

思考题:能否使用addi指令替代ori指令?

解答:不能。
首先我们需要理解addi的实现原理。
addi会将立即数进行符号扩展为32位,然后和源操作数相加,将结果送入目标操作数。
而且,addi指令的立即数必须是一个16位的有符号整数。
所以,如果是低16位最高位是1,那么符号扩展后的这个数的高16位都是1,那么相加就会影响原来的高16位。故不能用addi替换ori。

4. 决策分支指令

  • 相等则分支beq $s0,$s1,Label,I型
  • 不等则分支bne $s0,$s1,Label,I型
  • 小于则置位slt t0,s0,s2,R型。如果s0 < s2,将t0设为1.
    • 立即数版本slti $t0,$t2,10,I型
    • 无符号版本sltu $t0,$s3,$s4,R型
  • 寄存器跳转指令jr $ra,跳转到目标寄存器,R型
  • 跳转指令j Label,跳转到指定标签

4.1 if-else型翻译

例题:将C代码中的if-else翻译为汇编代码,假设f ~ j ---- $s0 ~ $s4
if ( i = = j ) f = g + h ; else f = g - h ;

解答
①一般相等我们反而用bne,不等反而用beq。

bne $s3,$s4,Else 
add $s0,$s1,$s2  # f = g + h
j Exit
Else:
sub $s0,$s1,$s2
Exit:
...

4.2 while型翻译

例题:( Assume: i ~ k---- $s3 ~ $s5 base of save ---- $s6 )
while ( save[i] = = k )
i = i + j ;

解答

loop:
move $t0,$s3 # i 
sll $t0 ,$t0 ,2 # i << 2
add $t1, $t0, $s6 # &save[i]
lw $t2, 0($t1)
bne $t2, $s5, Exit
add $s3,$s3,$s4
j loop
exit:
...

5. 指令小结

在这里插入图片描述
在这里插入图片描述

三. MISP中的五种寻址方式

1. 立即数寻址和寄存器寻址

立即数寻址,例如,i型指令 addi、ori、lui等的立即数字段。

寄存器寻址,例如,R型指令的add。

2. 基址偏移寻址:lw/sw

例如,使用数据传送指令lw和sw来访问数组的时候,都是将基地址和偏移量相加后得到内存地址

3. PC相对寻址:beq/bne

分支指令中的16位偏移地址是字地址,并且是补码,可正可负

实际上,分支32位地址 = PC + 4 + 字地址偏移量左移两位形成的字节地址

那么,beq的寻址范围:16位的字地址偏移量,扩展后是18位,那么一共有 2 17 − 4 2^{17}-4 2174个正字节,以及 2 17 2^{17} 217个负字节,即前后大约128KB的范围。

4. 伪直接寻址:j/jal

伪地址寻址包括:j指令、jal指令

为什么说是伪直接寻址?因为直接寻址是直接给出32位的目标地址,而J指令只有26位的地址

J指令执行逻辑:先将26位地址左移两位形成28位字节地址,然后和PC+4的高32位进行拼接。

所以J指令的寻址范围:26位字地址,扩展28位的字节地址,所以是 2 8 = 256 M B 2^8=256MB 28=256MB,是一块的,不是上下前后的关系。

思考:如何扩大分支跳转指令的范围

扩大beq的跳转范围

我们以beq $s0,$s1,L1为例。

先将分支指令取反:bne $s0,$s1,Exit,再来一条J指令:J L1,这样寻址范围就被扩大了。

扩大J指令的跳转范围

对于J指令,如何跳到32位的范围?

可以先将32位地址用lui + ori组合装入寄存器,然后使用jr指令跳转。

四. 过程调用

1. 支持过程的寄存器

  • 参数寄存器:$a0-$a3
  • 返回值寄存器:$v0-$v1
  • 返回地址寄存器:$ra
  • 程序计数器:PC,保存当前指令的运行地址。

2. jal-jr指令对

  • jal:跳转并链接。执行原理:第一步,无条件跳转到下一个标签;第二步,将下一条指令保存到$ra中,方便回来。
  • jr:跳到寄存器存放的32位地址去。一般配和$ra做返回用。

3. 过程调用的C代码翻译

3.1 简单叶过程翻译

题目:翻译下面C代码。

int leaf_example(int g, int h, int i, int j)
{
    int f;
    f = (g + h) - (i + j);
    return f;
}

解答

addi     $sp, $sp,-12   
sw      $t1, 8($sp) 
sw      $t0, 4($sp)            
sw      $s0, 0($sp)           
add    $t0, $a0, $a1      
add    $t1, $a2, $a3      
sub    $s0, $t0, $t1       
add    $v0, $s0, $zero  # 结果放到返回值寄存器
lw    $s0, 0($sp)         
lw    $t0, 4($sp)         
lw    $t1, 8($sp)         
addi  $sp, $sp, 12        
jr      $ra                 

3.2 嵌套过程的翻译

题目:翻译下面C代码。

int fact(int n)
{
    if (n < 1)
        return (1);
    else
        return (n * fact(n - 1));
}

解答

fact:   addi    $sp, $sp, -8
sw     $ra, 4($sp)                      
sw     $a0, 0($sp)                     
slti      $t0, $a0, 1               # test for  n  <  1
beq    $t0, $zero, L1          # if  n  >=  1, go to L1(else)
addi    $v0, $zero, 1         # return if n <1
addi    $sp, $sp, 8     # Recover $sp (Why not recover $ra and $a0 ?)
jr       $ra                # return to after jal

L1:  addi   $a0, $a0, -1       # n  >=  1: argument gets ( n  -  1 )
jal    fact                # call fact with ( n  -  1 )
lw    $a0, 0($sp)         # return from jal: restore argument n
lw    $ra, 4($sp)             # restore the return address
addi   $sp, $sp, 8        # adjust stack pointer to pop 2 items
mul   $v0, $a0, $v0        # return  n*fact ( n  -  1 )
jr       $ra               # return to the  caller

4. 堆栈操作

需要压栈保存的寄存器:

  • $s0-$s7存放主程序变量。
  • $ra:嵌套过程中需要压栈。

入栈:把$sp减去保存寄存器个数的4倍,再用sw指令将数据入栈。

帧指针$fp:指向高地址。$fp$sp之间的空间叫做过程帧。

全局指针:$gp。主程序使用的变量以及static变量都放在固定的区域,全局指针$gp指向这个区域。内存从高到低依次是:栈、堆(栈向下生长,堆向上生长),静态数据、正文、保留。

题目:翻译C代码为MISP汇编。

int cal(int g, int h , int i , int j){
	int f;
	f = (g+h) - (i+j);
	return f;
}

解答:假设f保存到$s0中。

cal:
addi sp, sp, -4  # 入栈
sw s0, 0(sp)  # 只需要保存s0
add t0,a0,a1
add t1,a2,a3
sub s0,t0,t1
add v0,s0,zero # 寄存器之间数据传输
lw s0,0(sp) 
addi sp, sp,4 # 出栈

五. 总结

1. 寄存器

在这里插入图片描述

2. 指令

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值