《汇编语言程序设计》学习笔记(5)五、MIPS汇编
5.1 MIPS32架构与指令集初步
5.1.1 MIPS32架构与指令集初步-1
MIPS架构——最经典的RISC
5.1.2 MIPS32架构与指令集初步-2
MIPS的经典流水线结构
5.1.3 MIPS32架构与指令集初步-3
指令集的特点(与x86指令集对比)
MIPS32里面呢有一个寄存器 就是0号寄存器比较特殊 就是它的值啊永远是0 它就认为就是说0是最常用的常数 所以说呢我就直接把它用 一个寄存器来代表 它就是0 这样的话呢我实际上就可以省编码 0号寄存器有点特殊 就是说它的值永远是0 就是说你可以拿0号寄存器作运算 你甚至可以拿0号寄存器作为 你运算一个目地寄存器 你可以去改它 但是反正改完之后它还是0 反正是不出错 就是这么个意思
5.1.4 MIPS32架构与指令集初步-4
程序员可见的流水线效果
5.1.5 MIPS32架构与指令集初步-5
简单总结
MIPS汇编指令初步
5.1.6 MIPS32架构与指令集初步-6
访存指令
寄存器
MIPS32寄存器命名与使用惯例
5.1.7 MIPS32架构与指令集初步-7
传统的MIPS32 传递过程参数方式(不包括浮点数)
5.1.8 MIPS32架构与指令集初步-8
协处理器0——CP0
指令:MFC0 即 move c0
指令:MTC0 即 move to c0
CP0部分寄存器汇总
CP0示例-1
CP0示例-2
Context寄存器
5.2 MIPS32指令集与编程
5.2.1 MIPS32指令集与编程-1
目录
指令分类(主要部分,不包括浮点)
举例
5.2.2 MIPS32指令集与编程-2
装载(Load)、存储(Store)指令(部分)
5.2.3 MIPS32指令集与编程-3
LL / SC指令
再往下的话呢 就是叫作LL/SC指令 这什么意思呢 这指令蛮重要的 我们知道就是说 简单讲讲看不解释了
如果你们以后讲这个操作系统 里头做这种多线程互斥的话 会用到这个东西 就是在多线程的程序中 多线程程序当中 因为大家是共享内存的嘛 所以某个变量可能不能 线程同时去访问它 但是我为了要保证 这个原文操作的原子性 所以一般需要一个test and set的原子操作 比方说我要累加 两个线程同时去累加一个数 那先把它取进来 我们知道啊 首先通过load操作把它取进来 取进来之后呢 再线程加一 再把它store出去 另外一个线程也是同时再做这个操作 先load 再加一 再store出去 大家注意 首先load load完了之后 你做个加modification 再写回去 这个至少需要 按照MIPS说法 至少需要3条指令来完成 一个load 一个加 一个store 那么在这种情况下呢 如果两个线程同时做这样的操作 可能相互之间会有干扰 因为你可能load 1 你load 1之后呢 另外一个线程 取得控制权了 它也load 1 完了你这边 原来那个线程加一变成2写回去 第二个线程 写回去之后 第二个线程再次获得控制权 还是 因为它以为它拿了个1嘛 还是把它加一变成2 再写回去 那这样的话 你写个2 他写个2 那样做了两次累加 两个不同线程做了两次累加
结果呢 体现出来只做了一个 那就是错误的 那错误的根源在什么地方呢 就是说你这个共享变量 没有对多线程的访问进行保护
所以一般来说 需要用这种TestAndSet操作 看看啊是不是有别人在访问它 如果别人在访问它的话呢 我得放弃 我得等着下一次的机会 实际上就防止这种冲突么 一般来说情况下 很多原子操作需要这种 专门的硬件来完成的
而在MIPS当中呢 是通过这种特殊的LLSC 这个指令对来完成的 load linked 以及store condititonal 这一指令对完成
5.2.4 MIPS32指令集与编程-4
陷阱(Trap)指令
syscall 是什么呢?相当于在 x86 里头的 int next 80 ,相当于就是说你的用户层的程序,如果要求调用操作系统的一些功能,提供一些调用,你得通过 syscall 这条指令进行。
特权(Privileged)指令
特权指令和在内核态才能使用这个指令。
指令编码
Op 段编码*
Special2操作码对应的Function段编码*
CP0中的rs为CO时Function段编码*
5.2.5 MIPS32指令集与编程-5
目录
注意首先跟大家说一下 就是说现在我们也要写一些 简单的MIPS程序嘛 但MIPS问题在于就是说 你上哪去找MIPS的机器 很少手机上的都是ARM 很少找到这个东西 所以我们需要一个模拟器 软件去虚拟出一个MIPS系统的处理器 但你代码可以在软件模拟上去跑 有点像个虚拟机似的 那我们一般用什么呢 推荐大家使用SPIM 的一个开源的 一个MIPS系统的模拟器 SPIM 就是MIPS倒过来写 SPIM
5.2.6 MIPS32指令集与编程-6
示例一:分别计算整数数组中正数、负数的和
spim 模拟器里,它虚拟了一些系统调用,就是说你可以利用这个系统调用的形式,就是程序里面可以利用系统调用的形式,进行写这个程序。那模拟器看到这个系统调用的话呢,他会把相关的这些参数,通过 v0、a0传到参数给它读出来,看看你所要,它是通过 v0 来指定,你所要调用的功能是哪个功能,你是 print 一个 string 还是 print 一个 int 。
然后你如果是print string的话呢,那就看下 a0 , a0 里面就告诉你,我要print这个string,它的这个地址层在 a0 里面。
如果是 int 的话呢,就在a0里面放了你所要print出来的int类型的值,就放在这里头。
相当于spim模拟器它自己做了一套简单的系统调用。你只要遵循这套规范,你就可以对外进行输出了,或者进行输入也可以,或者一系列的功能。
SPIM模拟的系统调用
5.2.7 MIPS32指令集与编程-7
示例二:阶乘(delay slot关闭)
5.3 MIPS32异常处理
5.3.1 基本概念
目录
异常(Exception)
同步异常
异步异常(中断)
除了同步异常之外 叫异步异常 也可称为中断 是由“外部事件”引起 往往是外设的触发
比方说程序跑着跑着 网卡上面收到一个数据包 同时网卡结构buffer快满了 这个时候如果你再不处理话呢 在网络上开始丢包buffer有限 这种情况话呢 可能会发生一个 网卡给你CPU传一个中断过来 赶紧处理我一下 在这种情况下话呢 CPU得中断当前正在跑的 你的用户程序 跑过去处理它 处理完之后再回来 当然回来之后回到哪条指令呢 那看情况 是执行当前指令或者下一条指令 就得看情况咱们一会儿再说
5.3.2 异常处理向量
异常处理向量( Vectors )
针对异常进行处理有个概念 叫做异常处理向量 什么意思 就是说异常分不同类型 不同类型都有一个号 所以它的系统里就维护一个 异常的处理向量表 你这个号 对应表里的一项 这一项的地址 就是要处理这个异常的代码的入口的地址 就是向量处理方式 注意啊 向量处理方式并不是说所有的处理器 都有这种方式 因为这种方式想想看 如果不同的异常号 直接可以通过查表的方式 马上跳到它所对应的处理入口 这种方法是比较快的 比较实时的 但是有一些处理器 比如说在mips课上讲的 4kc 蓝本讲的 4kc处理器就没有这种方式 它的异常入口地址非常少 就那么几个 完了进去之后呢通过软件访问 来判断当前到底哪个异常 然后再跳到不同的入口 这个就慢了 所以它实时性不是太好 处理向量就是实时性比较好的一种方式 所以不能一概而论 实际情况不一定一样 这个里面提到过了 每类异常都有编号 对应一个入口地址 这样如果快的话 就可以直接跳过去 但是有些处理器 mips处理器早期没有这种方式 它从4ke才开始引入 我们讲到4kc里面还没有 但是这不是什么本质问题
精确异常处理(MIPS支持)
MIPS32下的异常种类
5.3.3 异常处理流程
MIPS异常处理基本过程
异常返回
中断
SPIM模拟器支持的异常处理流程
Status寄存器
Cause寄存器
5.3.4 异常处理实例
5.4 虚存与MIPS32内存管理
5.4.1 虚存设计背景
虚存概念的一个初步 首先就是说虚存设计背景是什么 实际上什么叫做虚存
实际上回想一下第一节课 第二节课的时候 当时给大家讲过的概念是什么呢 就是你写程序的时候 对于一个系统程序而言 你写一个汇编程序的时候 实际上写c程序也是类似 有人认为就是说 好像你占理这个逻辑处理器 同时你也占理一片连续的内存空间 内存空间如果是32位地址的话呢 那么就是说 你是地址0一直到2的 当然高端的地址 你不能使用了 一般来说是低的2G 或者说低的3G是可以被你使用的 在你面前有一个连续空间 在里面展开 当然大家也想过这个问题了 也就是说你同时有那么多的进程 在一块跑 那如果说每个进程 都占了2G 3G空间的话 你的物理内存有没有那么多 100个程序跑 每个人都是2G 200G 笔记本哪里能插得下那么多内存条呢
再者就是说 大家应该也考虑到这个问题 就是说 以前讲link的时候也好 就是说一个程序 你通过链接把多个正文段代码段 组合成单一的正文段数据段 那这种情况下话呢 你好像从来不需要考虑 你这个程序 是跟其它程序一块在那边 同时使用系统内存的 你要就认为我就独占了那一块 从没考虑过到 我跟别人share我这个系统的物理空间 你从来说没考虑过这个事
为什么会有这么一些 那应该说这是一个 比较好的特性对编程而言 就是否则的话你如果写程序的时候 你写个程序需要考虑 跟别人合作使用内存 那这个事情就非常麻烦了 所以这个对于写程序的人 是一个好的事情 当然这个好的现象是怎么造成的呢 很多程度上是在于 这个虚存这个概念的引入 有软硬件协同实现的一个 虚存Virtual memory 这个概念 给你造成了这么一个 理想的编程的一个假象
5.4.2 虚存设计背景详述-1
我们首先要有这么一个清楚的概念 你写程序的时候 你程序里的任何一个地址 包括你cpu处理的时候 就是cpu这个内部 流水线内部里任何的一个地址 除了涉及到 真的涉及到 我们回头会讲到 虚实转换的一个模块之外 所有cpu内部的地址 当然不用说你的程序地址 都叫做虚拟地址 或者叫程序地址
虚拟地址需要进行转换 才真正转换成物理地址 什么意思 呢 物理地址就是你真正读写内存的时候 你内存条 你读写内存条的时候 总线走的那个地址 那个叫做物理地址 其它的程序内部的 cpu内部的都是虚拟地址
所以肯定有一个模块 在那边做了一个虚实的一个转换 这个呢从逻辑上来讲话呢 一般通过处理器硬件 有一个页表 将虚拟地址转换成物理地址
页缺失(Page fault)
5.4.3 虚存设计背景详述-2
简化程序的内存管理
存储保护功能
虚存地址转换:命中
虚存地址转换:页缺失
5.4.4 虚存地址转换
页表结构
使用快表(TLB)来加速
5.4.5 MIPS32内存管理-1
5.4.6 MIPS32内存管理-2
工作模式
用户态
TLB结构(以MIPS 4KC处理器为例)
虚拟地址到物理地址的详细转换流程
TLB缺失和多匹配
TLB操作指令*
参考文献:
1. 汇编语言程序设计 - 清华大学 - 学堂在线。