本次笔记内容:
28.MIPS指令集与汇编程序设计-2
补充:MIPS32异常处理
注:我找到了对应内容的课件,请见我于GitHub的CS笔记仓库。
本节课对应幻灯片:汇编语言程式设计-MIPS.pdf,第49页起。
在B站视频中,缺了一节课,即 ‘MIPS32异常处理’ ,感谢 UP 主
东南偏南
做的补充:汇编语言程序设计(清华大学张悠慧)P27。
我们讲 MIPS32 ,主要集中在其特色部分。
文章目录
P49 定长指令造成了一些困扰P51 指令编码P58 MIPS汇编指示(Directives)示例一:分别计算整数数组中正数、负数的和示例二:阶乘(delay slot关闭)MIPS32异常处理异步异常(中断)异常处理向量(Vectors)精确异常处理(MIPS支持)MIPS32的异常种类MIPS异常处理基本过程异常返回中断MIPS处理器的中断控制设计SPIM模拟器支持的异常处理流程Status寄存器Cause寄存器异常处理实例P49 定长指令造成了一些困扰
如上。某些操作数的限制(如立即数宽度)增加了汇编编程难度。为降低难度,MIPS汇编器会做一些预处理。
如上,at
寄存器保留给汇编器专用,就用在了这里。因此一般情况下不要手动使用。
对于li $3, -5
,其意义其实就是因为 -5 不大,因此直接用 addiu 装到 $3 中。
对于li $4, 0x8000
正好放得下 16 位中,因此转换的方法较多,这里转换为ori 或指令
,是可以的。
对于0x120000
,把 12 拿出来,放到高 16 位对应的位置,低 16 位清零。
对于0x12345
,对于低 16 位不全为 0 的,转换为两条指令lui
与ori
。
在如上的例子中,对于第三条lw $2, addr
,这个数宽度较大,至少得把这个指令拆成两条:
%hi
相当于汇编中的宏,取高的半字(16位);%lo
则是取低的半字。
对于sw $2, addr($3)
,在基址上加上偏移。
P51 指令编码
三种类型:I-Type、J-Type、R-Type。
Op 段编码如上。其他段编码不再复制,详见 ppt 。
最终,我们要把段编码总结成一个真值表
,以此构建电路、控制信号。
P58 MIPS汇编指示(Directives)
注意这里的 word 是 32 byte 。
.data 是一般的数据段,存了 3 个 word ,初值分别是 1,2,3。
.text 定义了 func 的入口地址。
.bss 非初始化的变量在其中,还分为全局可见的
和local
。与 x86 类似。
数据类型定义如上。
这个 .extern 与 C 中的不太一样。
比如,对于lw "offset"
,偏移量 16 位宽可能不太够。假设地址不够,我们可以如之前一样,换成 2 条指令。但是这样太麻烦了,MIPS32做了简化:
有一个全局指针$gp
;程序初始化时,$gp
已经初始化好;我们把全局变量放在$gp
附近,这样只要变动$gp
一点,就可以访问全局变量了。相当于$gp
是个轴base。
因此,用.extern
定义了全局变量,让其在$.gp
上下 64 byte 范围。
示例一:分别计算整数数组中正数、负数的和
先声明数据段;遍历一遍数据段;进入 main 。
这里的syscall
是模拟器提供的syscall
。在 SPIM 这么简单的一个模拟器中,做不到一个模拟一个 OS ,只是做了一个模仿。
这里与计组的“控制冲突”结合起来了,在跳转jal
后要有nop
或无关指令
用于 delay slot 。
接着,老师操作了 SPIM 。
示例二:阶乘(delay slot关闭)
如上,在模拟器中还是尊重了MIPS的规范,建立了一个栈帧。
如上,把栈顶的地址$sp
向下拉了 32 。
MIPS32异常处理
关于同步异常需要自己看 ppt 了解。
异步异常(中断)
由“外部事件”引起,往往是外设触发。如:IO中断、Hard Reset、Soft Reset。
重新执行当前指令或者下一条指令。
异常处理向量(Vectors)
每类异常都有其编号以及异常处理入口地址,往往在内存中构成一张地址表。所以称之为向量。
精确异常处理(MIPS支持)
精确异常指的就是——在处理异常时,产生异常指令之前的指令都应执行完毕;该指令之后(包括该指令本身)的指令则都不处理。
需要精确记录异常的位置(指令) Branch Delay Slot 需要取消后续指令需要正确恢复执行
MIPS32的异常种类
MIPS异常处理基本过程
其实比较复杂,这里讲一个通用的情况。
保存现场——在异常程序入口,硬件只记录了被打断程序的很少量的信息,因此需要保留相关的控制寄存器等值使得异常处理程序能够执行(k0
、k1
寄存器专门保留给异常处理使用
);判断不同的异常——查询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寄存器。
异常中断后,如果能够正常返回之前的指令,那么返回的地址存储在 EPC 中。
Status寄存器
SPIM 实现了 Status 寄存器的部分功能。
Cause寄存器
用于记录发生异常的原因。
Branch 来决定返回的指令地址是 EPC 还是 EPC-4 。
异常处理实例
这节课也给大家留了一个作业,关于异常处理。
为什么不存储在栈上?
因为问题可能出现在栈上,因此现在不能相信栈。
为什么把 Cause 右移两位?见Cause寄存器
结构。
老师用 SPIM 做了些演示。