知识点

Simple-MIPS教学指令集

定位与记号

Simple-MIPS是为教学目的设计的简化指令集。它参考MIPS等真实架构,但并不完全等同于真实的MIPS指令集,目的是更直观地讲解计算机组成原理的核心概念。本页的指令都按Simple-MIPS的写法给出;PC、IR、ALU和寄存器组如何配合执行这些指令,见CPU的核心部件。

每条Simple-MIPS指令固定32位,每个通用寄存器也固定32位。常见指令的基本形式是

text
操作码 目标, 源1, 源2

操作数按角色分为以下几类:

记号含义
$rd目标寄存器,结果写在这里
$rs第一个源寄存器;访存时兼作基址寄存器
$rt第二个源寄存器;在load/store中承担另一角色
offset立即数偏移,与 $rs中的地址相加
shamt移位位数,取0–31
label标签,代表一条指令的地址

寄存器编号的具体分工($zero、$t0–$t9、$s0–$s7等)由寄存器组的约定决定,不在指令本身的语义里体现。

指令与共同约定

指令总表

指令类别语法语义
load数据加载lw $rt, offset($rs)从地址 $rs + offset读出一个字,存入 $rt
store数据存储sw $rt, offset($rs)把 $rt的一个字写到地址 $rs + offset
mov数据移动move $rd, $rs$rd = $rs
sub算术运算sub $rd, $rs, $rt$rd = $rs - $rt
shiftl逻辑左移sll $rd, $rt, shamt$rd = $rt << shamt,右侧补0
shiftr逻辑右移srl $rd, $rt, shamt$rd = $rt >> shamt,左侧补0
slt比较slt $rd, $rs, $rt$rs < $rt时 $rd = 1,否则 $rd = 0
sle比较(伪指令)sle $rd, $rs, $rt$rs <= $rt时 $rd = 1,否则 $rd = 0
beqz条件分支beqz $rs, label$rs == 0时转到label
goto无条件跳转goto label直接转到label

表中的mov、shiftl、shiftr是教学指令集里的名称,汇编写法分别是move、sll、srl;goto对应真实MIPS的j。读真实MIPS汇编时按真实指令集的助记符理解,不要混用。

访存指令的地址计算

load与store都采用“基址寄存器加偏移”的寻址方式:$rs中存放基址,offset是立即数,实际访问的地址为两者之和。偏移可以为0,此时直接访问 $rs指向的字。这两条指令一次都只移动一个32位的字。

可以借助一个类比记住方向:寄存器像手边的工作台,内存像书架;load把数据从书架搬到工作台,store把工作台的内容放回书架。所以load的 $rt是写入目标,store的 $rt是数据来源。

复制、减法与移位

复制指令把源寄存器的值写到目标寄存器,两个寄存器此后的值相同。Simple-MIPS把复制视为一条基本指令;真实MIPS通常用add或or与 $zero配合实现,例如add $t0, $t1, $zero。

sub把 $rs当作被减数、$rt当作减数,结果写入 $rd。减法由ALU完成,操作数来自寄存器组,写法与slt一样是三个寄存器。例如 $t1为10、$t2为3时,sub $t0, $t1, $t2使 $t0等于7。

shamt给出移动的位数。当左移结果未丢失高位时,左移shamt位相当于乘以 2shamt2^{shamt};对无符号数,右移shamt位相当于除以 2shamt2^{shamt} 并向下取整。这个“相当于除以”的结论只对非负整数成立:srl是逻辑右移,左侧补0,不能用它处理带符号数的除法。

控制流指令的组合方式

slt和sle不改变执行顺序,它们只把比较结果写成0或1;beqz检查一个寄存器是否为0,为0就转到标签;goto无条件转到标签,实质是把PC置为标签对应的地址,例如goto Loop回到循环开头。三者配合,才能让程序离开“顺序执行”:

  1. 用slt把条件变成0或1。

  2. 用beqz检查这个0或1,决定是否跳出。

  3. 用标签和goto回到循环开头。

sle是伪指令,汇编器通常把它展开成与slt相关的指令序列。它的语义完整,可以直接在作业中使用,只是不要把它当成硬件直接实现的一条指令。

例子

load与store的地址计算

设 $s1中存放0x1000。

memory.s
lw $t0, 4($s1) # 读地址0x1004
sw $t0, 8($s1) # 写地址0x1008

lw把地址0x1004处的字读入 $t0;随后sw把 $t0的内容写到地址0x1008。两次访问的地址都由 $s1加上偏移得到,$s1本身不变。

复制寄存器:move与add ... $zero

move.s
move $t0, $t1
add $t0, $t1, $zero

两条指令都把 $t1的值复制到 $t0。第二条利用 $zero恒为0:$t1 + 0仍等于 $t1,所以结果与move相同。

移位代替乘除

shift.s
sll $t0, $t1, 2 # $t1 = 5时,$t0 = 20
srl $t0, $t2, 2 # $t2 = 13时,$t0 = 3

左移2位相当于乘以 22=42^2=4,所以 5×4=205\times 4=20;右移2位相当于除以4并向下取整,所以 13÷4=313\div 4=3。

比较结果如何交给分支

设 $s2的值为5,依次考察 $s1取不同值时的结果:

$s1的值slt $t0, $s1, $s2sle $t0, $s1, $s2beqz接在slt后beqz接在sle后
311不跳转不跳转
501跳转不跳转
700跳转跳转

slt在相等时给出0,sle在相等时给出1,这是两条比较指令唯一的差别。beqz只看寄存器是不是0,所以它接在slt后面时,跳转的含义是“不小于”;若接在sle后面,跳转的含义变成“大于”。写循环时要先确定自己用的是哪一条。

探索

比较结果如何控制分支?

改变寄存器的值和比较指令,查看beqz的去向。

正在准备演示…

题集

练习

  1. 若 $s1的值为4096,执行lw $t0, 8($s1) 时读取的字节地址是多少?

    地址是 。

    参考解答

    实际地址等于基址加偏移:4096+8=41044096+8=4104。

  2. 下列哪条指令会把寄存器中的值写入内存?

    选择答案
    参考解答

    sw是store,把 $t0写到内存;lw的方向相反,是从内存读入寄存器。

  3. 设 $t1的值为5,执行sll $t0, $t1, 2后 $t0的值是多少?

    $t0的值是 。

    参考解答

    左移2位相当于乘以 22=42^2=4,5×4=205\times 4=20。

  4. 设 $t1的值为13,执行srl $t0, $t1, 2后 $t0的值是多少?

    $t0的值是 。

    参考解答

    右移2位相当于除以4并向下取整,13÷4=3.2513\div 4=3.25,取3。

  5. 设 $s1和 $s2的值都为7。

    1. 执行slt $t0, $s1, $s2后 $t0的值是 。

      参考解答

      slt判断第一个操作数是否严格小于第二个;两者相等,结果为0。

    2. 执行sle $t0, $s1, $s2后 $t0的值是 。

      参考解答

      sle判断是否小于或等于;两者相等,结果为1。

  6. 设 $t0的值为0,执行beqz $t0, Loop时会发生什么?

    选择答案
    参考解答

    beqz在寄存器等于0时跳转;$t0为0,所以转到标签Loop。

  7. 要把 $t1的值复制到 $t0,下列哪条指令正确?

    选择答案
    参考解答

    move $t0, $t1把源寄存器 $t1的值写到目标 $t0;写成move $t1, $t0方向相反。sub $t0, $t1, $t1的两数相同,结果恒为0,不是复制。