【汇编语言与计算机系统结构笔记18】MIPS指令集与汇编程序设计 & 异常处理

本次笔记内容:
28.MIPS指令集与汇编程序设计-2
补充:MIPS32异常处理

注:我找到了对应内容的课件,请见我于GitHub的CS笔记仓库

本节课对应幻灯片:汇编语言程式设计-MIPS.pdf,第49页起。

在B站视频中,缺了一节课,即 ‘MIPS32异常处理’ ,感谢 UP 主 东南偏南2018 做的补充:汇编语言程序设计(清华大学张悠慧)P27

我们讲 MIPS32 ,主要集中在其特色部分。

P49 定长指令造成了一些困扰

【汇编语言与计算机系统结构笔记18】MIPS指令集与汇编程序设计 & 异常处理
如上。某些操作数的限制(如立即数宽度)增加了汇编编程难度。为降低难度,MIPS汇编器会做一些预处理。

如上,at 寄存器保留给汇编器专用,就用在了这里。因此一般情况下不要手动使用。

对于 li $3, -5 ,其意义其实就是因为 -5 不大,因此直接用 addiu 装到 $3 中。

对于 li $4, 0x8000 正好放得下 16 位中,因此转换的方法较多,这里转换为 ori 或指令 ,是可以的。

对于 0x120000 ,把 12 拿出来,放到高 16 位对应的位置,低 16 位清零。

对于 0x12345 ,对于低 16 位不全为 0 的,转换为两条指令 luiori

【汇编语言与计算机系统结构笔记18】MIPS指令集与汇编程序设计 & 异常处理
在如上的例子中,对于第三条 lw $2, addr ,这个数宽度较大,至少得把这个指令拆成两条:

  • %hi 相当于汇编中的宏,取高的半字(16位);
  • %lo 则是取低的半字。

对于 sw $2, addr($3) ,在基址上加上偏移。

P51 指令编码

【汇编语言与计算机系统结构笔记18】MIPS指令集与汇编程序设计 & 异常处理
三种类型:I-Type、J-Type、R-Type。

【汇编语言与计算机系统结构笔记18】MIPS指令集与汇编程序设计 & 异常处理
Op 段编码如上。其他段编码不再复制,详见 ppt 。

最终,我们要把段编码总结成一个真值表,以此构建电路、控制信号。

P58 MIPS汇编指示(Directives)

【汇编语言与计算机系统结构笔记18】MIPS指令集与汇编程序设计 & 异常处理
注意这里的 word 是 32 byte 。

.data 是一般的数据段,存了 3 个 word ,初值分别是 1,2,3。

.text 定义了 func 的入口地址。

.bss 非初始化的变量在其中,还分为全局可见的local 。与 x86 类似。

【汇编语言与计算机系统结构笔记18】MIPS指令集与汇编程序设计 & 异常处理
数据类型定义如上。

【汇编语言与计算机系统结构笔记18】MIPS指令集与汇编程序设计 & 异常处理
这个 .extern 与 C 中的不太一样。

比如,对于 lw "offset" ,偏移量 16 位宽可能不太够。假设地址不够,我们可以如之前一样,换成 2 条指令。但是这样太麻烦了,MIPS32做了简化:

  • 有一个全局指针$gp
  • 程序初始化时,$gp已经初始化好;
  • 我们把全局变量放在$gp附近,这样只要变动$gp一点,就可以访问全局变量了。
  • 相当于$gp是个轴base。

因此,用 .extern 定义了全局变量,让其在 $.gp 上下 64 byte 范围。

【汇编语言与计算机系统结构笔记18】MIPS指令集与汇编程序设计 & 异常处理

示例一:分别计算整数数组中正数、负数的和

【汇编语言与计算机系统结构笔记18】MIPS指令集与汇编程序设计 & 异常处理
【汇编语言与计算机系统结构笔记18】MIPS指令集与汇编程序设计 & 异常处理
【汇编语言与计算机系统结构笔记18】MIPS指令集与汇编程序设计 & 异常处理
先声明数据段;遍历一遍数据段;进入 main 。

这里的 syscall 是模拟器提供的 syscall 。在 SPIM 这么简单的一个模拟器中,做不到一个模拟一个 OS ,只是做了一个模仿。

这里与计组的“控制冲突”结合起来了,在跳转jal后要有nop无关指令用于 delay slot 。

接着,老师操作了 SPIM 。

示例二:阶乘(delay slot关闭)

【汇编语言与计算机系统结构笔记18】MIPS指令集与汇编程序设计 & 异常处理
【汇编语言与计算机系统结构笔记18】MIPS指令集与汇编程序设计 & 异常处理
【汇编语言与计算机系统结构笔记18】MIPS指令集与汇编程序设计 & 异常处理
如上,在模拟器中还是尊重了MIPS的规范,建立了一个栈帧。
【汇编语言与计算机系统结构笔记18】MIPS指令集与汇编程序设计 & 异常处理
如上,把栈顶的地址$sp向下拉了 32 。

MIPS32异常处理

关于同步异常需要自己看 ppt 了解。

异步异常(中断)

由“外部事件”引起,往往是外设触发。如:IO中断、Hard Reset、Soft Reset。

重新执行当前指令或者下一条指令。

异常处理向量(Vectors)

【汇编语言与计算机系统结构笔记18】MIPS指令集与汇编程序设计 & 异常处理
每类异常都有其编号以及异常处理入口地址,往往在内存中构成一张地址表。所以称之为向量。

精确异常处理(MIPS支持)

精确异常指的就是——在处理异常时,产生异常指令之前的指令都应执行完毕;该指令之后(包括该指令本身)的指令则都不处理。

  • 需要精确记录异常的位置(指令)
    • Branch Delay Slot
  • 需要取消后续指令
  • 需要正确恢复执行

MIPS32的异常种类

【汇编语言与计算机系统结构笔记18】MIPS指令集与汇编程序设计 & 异常处理
【汇编语言与计算机系统结构笔记18】MIPS指令集与汇编程序设计 & 异常处理
【汇编语言与计算机系统结构笔记18】MIPS指令集与汇编程序设计 & 异常处理

MIPS异常处理基本过程

其实比较复杂,这里讲一个通用的情况。

  • 保存现场——在异常程序入口,硬件只记录了被打断程序的很少量的信息,因此需要保留相关的控制寄存器等值使得异常处理程序能够执行(k0k1寄存器专门保留给异常处理使用);
  • 判断不同的异常——查询Cause寄存器,根据其不同的异常原因来进行不同的处理流程;
  • 构造异常处理内存空间——异常处理程序可能由高级语言书写,需要保留通用寄存器,构造堆\栈存储区(可选);
  • 处理异常——……
  • 返回——恢复寄存器,清零Cause寄存器,将Status寄存器的相关位置1以开中断。

异常返回

一般而言,异常处理代码工作在核心态,而被中断的程序是在用户态,所以异常返回意味着状态转换。

◦ 这个转换与指令返回必须“同时”完成,即用一条指令完成。

  • 为什么?加入一个当前程序是工作在核心态,标识位核心态;如果先把核心态标识去掉,变成用户态;后面再执行核心态的 ERET 指令,这就非法了。

ERET指令:

  • 返回EPC指向的地址:一般来说 EPC 或 EPC-4 是被中断的那个地址
  • Status寄存器修改

中断

中断是异步发生的,是来自处理器外部的I/O设备的信号引发的。

硬件中断不是由任何一条专门的指令造成的:

  • I/O设备,比如网络适配器,磁盘控制器等通过处理器芯片上的一个引脚发送信号,并将中断号放到系统总线上,用来触发中断,这个异常号标识了引起中断的设备。

MIPS处理器的中断控制设计

在中断发生时,如果指令已经完成了 MEM 阶段的操作,则需保证该指令执行完毕。

反之,则丢弃这条指令的工作。

除 NMI 外,所有的内部或外部硬件中断(Hardware Interrupt)均共用这一个异常向量(Exception Vector)。

P84 嵌套异常比较复杂,不细讲。

SPIM模拟器支持的异常处理流程

SPIM实现了部分CP0寄存器。
【汇编语言与计算机系统结构笔记18】MIPS指令集与汇编程序设计 & 异常处理
异常中断后,如果能够正常返回之前的指令,那么返回的地址存储在 EPC 中。

Status寄存器

【汇编语言与计算机系统结构笔记18】MIPS指令集与汇编程序设计 & 异常处理
SPIM 实现了 Status 寄存器的部分功能。

Cause寄存器

用于记录发生异常的原因。
【汇编语言与计算机系统结构笔记18】MIPS指令集与汇编程序设计 & 异常处理
Branch 来决定返回的指令地址是 EPC 还是 EPC-4 。

异常处理实例

这节课也给大家留了一个作业,关于异常处理。

【汇编语言与计算机系统结构笔记18】MIPS指令集与汇编程序设计 & 异常处理
【汇编语言与计算机系统结构笔记18】MIPS指令集与汇编程序设计 & 异常处理
为什么不存储在栈上?

因为问题可能出现在栈上,因此现在不能相信栈。

为什么把 Cause 右移两位?见 Cause寄存器 结构。
【汇编语言与计算机系统结构笔记18】MIPS指令集与汇编程序设计 & 异常处理
老师用 SPIM 做了些演示。