拓冰建站拓冰建站
首页 / 资讯中心 / 正文

MIPSsim实验精讲:寄存器约定、阶乘、移位与64位求补

简介这份实验报告文档面向计算机系统结构、计算机组成原理课程的本科生与考研复习者聚焦MIPS指令系统与MIPS体系结构的入门实践帮助读者借助MIPSsim模拟器理解RISC架构下指令的执行过程与寄存器、内存的状态变化。资源包共1个docx文件约754KB内容按实验目的、模拟器使用方法、补充实验与题目解析组织并附有完整汇编代码示例。报告覆盖阶乘计算、用SLL与SRL实现乘除运算、(X-Y)*2-(XY)/8的求值以及64位整数求补运算等典型题目逐条说明ADDIU、SUB、MUL、BGTZ、LW、XORI等指令的语义与用法还提示了$r0恒为0、.data与.text段分工、单步执行与连续执行观察结果等实用要点。目前已有646人学习下载适合需要对照范例完成实验报告、梳理指令级模拟思路并查漏补缺的读者参考。1. 从一个反直觉结论说起$r0$ 为什么永远是 0很多人第一次打开 MIPSsim 跑程序会下意识想给$r0赋个值试试结果发现改不动。这不是 bug而是 MIPS 体系结构里一个被写进硬件约定的设计第 0 号通用寄存器恒为 0任何写入操作都被丢弃读取永远返回 0。这个特性看起来无害实际上它解决了一个很实际的问题——当你需要一个常量 0 参与运算、需要一个「丢弃结果」的写入目标、或者需要把某个寄存器的值搬到另一个寄存器时不需要额外的立即数指令直接ADDIU $r1, $r0, 6就能把常数 6 送进$r1。计算机系统结构实验里的 MIPS 指令系统与体系结构这一块本质上就是让你在 MIPSsim 这个指令级模拟器上把「指令怎么改寄存器、怎么改内存、怎么跳转」这三件事看明白。它适合刚学完汇编、还没真正把指令和硬件对应起来的人也适合想给单周期 CPU、流水线 CPU 课程打底的人——你在模拟器里跑的每一条指令后面都会变成数据通路上的一个控制信号。这篇就从寄存器约定、.data与.text的分段、再到阶乘、移位代替乘除、64 位求补这几个具体题目把能复现的步骤和参数讲清楚。2. MIPSsim 的寄存器约定与 .data/.text 分段2.1 32 个通用寄存器和 $r0 的不可写特性MIPS 有 32 个通用寄存器编号$r0到$r31。MIPSsim 里你直接用$r0、$r1这种编号写法即可不需要记$zero、$t0、$s0那套 ABI 别名。$r0硬连到常量 0其余 31 个可以随意读写。实验里反复用到的几个寄存器各自承担的角色很明确寄存器在阶乘题中的用途在 64 位求补题中的用途$r0提供常量 0配合 ADDIU 装载立即数同左$r1累乘结果同时初值就是 n存放数据段基地址后作临时$r2计数器从 n-1 递减到 0低 32 位数据$r3常量 1用于递减高 32 位数据$r4未使用进位暂存把寄存器当成有限的草稿纸写代码前先分配好谁记什么比写一句想一句要省事。我一般习惯$r1放结果$r2放循环变量$r3放常量遇到需要额外临时值的运算再动$r4。2.2 用 ADDIU 装载立即数、用 LW 装载内存数据这里有个新手最容易踩的坑.data段里的X: .word 112X代表的是内存地址不是数值 112。你想把 112 拿进寄存器得两步走——先把地址装进寄存器再按照这个地址把内存里的字 load 进来ADDIU $r1, $r0, X # $r1 X 的地址因为 $r0 恒为 0等价于立即数装载 LW $r1, 0($r1) # $r1 Mem[$r1 0]即 112ADDIU是「无符号立即数加法」格式是ADDIU rt, rs, imm语义为rt rs imm。当rs是$r0时就退化成纯粹的立即数装载。之所以用ADDIU而不是ADDI是因为它不做有符号溢出检查装载地址这类操作更省心。LW rt, offset(base)则是从base offset处读一个 32 位字进rt0($r1)表示偏移为 0。注意MIPSsim 里X这种符号会被汇编器解析成地址常量所以ADDIU $r1, $r0, X是合法的立即数用法。如果你直接写LW $r1, X反而会因为寻址方式不对而报错或取错值。2.3 .text 与 .data 的分工和加载顺序.text段放指令.data段放数据两者在文件里的顺序不影响执行——PC 从.text段的入口开始取指。但有一个细节值得记住.data段的初始化发生在程序运行之前所以你在.text里读到的一定是已经写好的初值。.data X: .word 112 Y: .word 8 .text main: ADDIU $r1, $r0, X LW $r1, 0($r1)常见做法是把.data写在文件末尾.text放前面这样代码主体更靠上、便于阅读。模拟器加载时会自己分段解析不用你手动指定段基址。3. 用 MIPSsim 跑通阶乘循环、分支与 NOP3.1 阶乘的算法拆解与寄存器分配求 n 的阶乘本质是一个从 1 乘到 n 的循环。但实验里给出的写法更巧妙先把结果寄存器$r1初始化成 n让计数器$r2从 n-1 开始每轮做$r1 $r1 * $r2然后$r2减 1直到$r2不大于 0 就跳出。这样 6 的阶乘就是 6×5×4×3×2正好等于 720。寄存器分工如下$r1既是初值 n 又是累乘结果$r3固定存常量 1$r2是递减计数器。用SUB $r2, $r1, $r3得到 n-1避免了再写一条立即数减法指令。3.2 阶乘完整代码与逐条注释.text main: ADDIU $r1, $r0, 6 # $r1 6既是 n 也是结果初值 ADDIU $r3, $r0, 1 # $r3 1常量用于递减 SUB $r2, $r1, $r3 # $r2 n - 1 5计数器初值 PROG1: MUL $r1, $r1, $r2 # $r1 $r1 * $r2 SUB $r2, $r2, $r3 # $r2 $r2 - 1 BGTZ $r2, PROG1 # 若 $r2 0 则跳回 PROG1 NOP NOPMUL rd, rs, rt做乘法结果写回rd在 MIPSsim 里乘法结果直接进通用寄存器不像真实流水线那样放 HI/LO这点写代码时要注意。BGTZ rs, label是「大于零则分支」判断rs 0时跳转到label。分支后面紧跟的NOP是空操作占一个时钟周期给分支延迟槽留位置——即使模拟器不一定严格要求写上也是好习惯和后续做流水线 CPU 时保持一致。执行到$r2变成 0 时BGTZ不跳转程序顺序执行两条NOP后结束。此时$r1里就是 720。3.3 在模拟器里观察执行结果跑这个程序有两种方式单步「执行」和「连续执行」。我一般先用连续执行看整体结果对不对再用单步定位问题。具体操作是在 MIPSsim 里把上述代码存成.txt文件加载寄存器窗口会实时刷新。连续执行走完后看$r1的值是不是 720十进制或0x000002D0十六进制。如果不对切回单步重点盯三个位置MUL后$r1是否在累积变大、SUB后$r2是否在正常递减、BGTZ是否在$r2到 0 时正确跳出。提示MIPSsim 的寄存器窗口通常可以切换十进制/十六进制显示。验算 6! 720 用十进制最直观如果题目要求看二进制位模式再切十六进制。4. 移位代替乘除与 64 位求补的实现细节4.1 SLL 和 SRL 实现乘 2 与除 2 的幂MIPS 里没有直接给你一个「乘 8」的指令但左移 3 位等价于乘 2³8右移 3 位等价于除以 8无符号整数向下取整。实验题目要求算(X-Y)*2 - (XY)/8其中*2就是SLL ... , 1/8就是SRL ... , 3。.text main: ADDIU $r1, $r0, X # $r1 X 的地址 LW $r1, 0($r1) # $r1 112 ADDIU $r2, $r0, Y # $r2 Y 的地址 LW $r2, 0($r2) # $r2 8 SUB $r3, $r1, $r2 # $r3 X - Y 104 SLL $r3, $r3, 1 # $r3 104 * 2 208 ADD $r4, $r1, $r2 # $r4 X Y 120 SRL $r4, $r4, 3 # $r4 120 / 8 15 ADD $r1, $r3, $r4 # $r1 208 15 223 NOP NOP .data X: .word 112 Y: .word 8SLL rd, rt, sa是逻辑左移sa是移位位数0 到 31低位补 0SRL rd, rt, sa是逻辑右移高位补 0。注意是「逻辑」移位对负数和无符号数行为不同——逻辑右移不保留符号位所以做算术除法时不能直接用SRL处理负数。这题 X、Y 都是正数没问题。ADD rd, rs, rt和SUB都是普通的加减不检查溢出。如果题目里 X、Y 可能很大导致溢出就要换成ADDU、SUBU的变体思路但 MIPSsim 里这几条通常够用。4.2 64 位求补两次 load、两次 XORI 和进位判断MIPSsim 不支持 64 位 load/store所以处理 64 位整数必须拆成高、低两个 32 位字分别操作。求补运算按位取反再加 1这个「加 1」要小心低 32 位加 1 可能产生进位进位要加到高 32 位上。先看数据布局。假设高 32 位是 90低 32 位是 112.text main: ADDIU $r1, $r0, ZHI # $r1 高 32 位地址 LW $r2, 0($r1) # $r2 高 32 位数据先暂存 ADDIU $r1, $r0, ZLO # $r1 低 32 位地址 LW $r3, 0($r1) # $r3 低 32 位数据 # 下面交换安排R3 存高 32 位R2 存低 32 位 XORI $r2, $r2, -1 # 高 32 位按位取反 XORI $r3, $r3, -1 # 低 32 位按位取反 ADDIU $r3, $r3, 1 # 低 32 位加 1 BEQ $r3, $r0, CARRY # 若低 32 位加 1 后为 0说明溢出产生进位 NOP J DONE NOP CARRY: ADDIU $r2, $r2, 1 # 高 32 位加 1 DONE: NOP .data ZHI: .word 90 ZLO: .word 112XORI rt, rs, imm是立即数异或把立即数按无符号扩展后和rs逐位异或。这里立即数写-1汇编器会把它当成 32 位全 1 的补码0xFFFFFFFF所以XORI $r2, $r2, -1的效果就是按位取反。关键的进位判断逻辑低 32 位取反后加 1如果结果是 0说明取反前的值是全 1即真值 -1加 1 后正好进位溢出需要给高 32 位加 1。用BEQ $r3, $r0, CARRY判断——只有$r3等于$r0也就是 0才跳去处理进位。两种例子的差别就在这里低 32 位是 112 时取反加 1 不会变成 0不产生进位低 32 位是 0 时取反变成全 1加 1 后溢出为 0产生进位高 32 位必须加 1。这就是实验正文里说的「只有取反后二进制为全 1即真值为 -1才会产生溢出进位」。5. 单步调试与跨题目的通用验证套路5.1 用单步执行定位寄存器异常模拟器最大的价值不是「跑出结果」而是能一条一条看寄存器怎么变。我一般固定一套排查顺序先连续执行拿到最终结果再重置、单步走前 3 到 5 条指令确认每个寄存器在「预期时刻」拿到了「预期值」。检查点预期现象异常时怀疑ADDIU $r1, $r0, X之后$r1等于 X 的地址不是 112是否漏了LWLW $r1, 0($r1)之后$r1变成 112地址偏移是否写错MUL循环中$r1逐轮增大循环变量是否递减正常BGTZ处$r2 0时跳转判断符号和标签是否对应64 位求补的ADDIU $r3, $r3, 1后$r3 0表示进位是否忘了判断直接加高 32 位5.2 把进位判断抽象成一个可复用的模式64 位加法、64 位求补、64 位比较本质都是「低位运算 进位传播到高位」。把上面那段进位判断固化下来换成别的 64 位运算也能套低位做完运算用BEQ 低位结果, $r0, 进位分支判断是否溢出进位分支里给高位加 1。一个容易忽略的点是这道题的进位判断只在「低位加 1」这一步做而不是取反那一步。因为取反不产生进位只有加 1 才可能。把这个区别记牢遇到其它 64 位运算时先问自己——这一步会不会产生进位再决定在哪加判断。5.3 分支延迟槽里 NOP 到底是干什么用的实验代码里每个分支后面都跟了NOP很多人照抄但不知道原因。MIPS 采用分支延迟槽机制分支指令后面那条指令会在分支真正生效前先执行。如果那条位置放的是有效指令就必须保证它在「跳」和「不跳」两种情况下都无害。放NOP是最保守、最不容易出错的做法。在 MIPSsim 这种指令级模拟器上你甚至可以用一条有用的指令去填延迟槽来「省周期」但前提是你完全清楚它的副作用。我的建议是实验阶段一律用NOP等做单周期或流水线 CPU 设计时再回头优化延迟槽填充。这样错误来源少调试时看寄存器变化也更干净。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门