普莉姆拉计算机 v3

MIPS-I 子集虚拟机 · 在《Alice in Cradle》中实现图灵完备
本虚拟机使用游戏的 .cmd 事件脚本系统构建。32 个 MIPS 寄存器、完整五级流水(取指→译码→执行→访存→写回)、延迟槽、系统调用、交互式调试。

一、架构概览

calc.cmd                            — 主菜单 + 实时寄存器面板
  ├─ reset.cmd                      — 初始化 32 个 MIPS 寄存器, PC=0x3000
  ├─ run_once.cmd                   — 取指→译码→执行→访存→写回
  │    ├─ memory_operation/         — ROM/RAM 读写
  │    ├─ register_operation/       — 32 寄存器读写基元
  │    └─ decoder/                  — 指令译码器
  │         ├─ op6_0.cmd            — op6=0 转 SPECIAL
  │         ├─ op6_1~15.cmd         — I/J 型指令
  │         ├─ op6_32~43.cmd        — Load/Store 指令
  │         └─ special/             — funct6 二级分发 (R 型)
  ├─ run_100/1000.cmd               — 批量执行
  └─ input_int.cmd                  — SYSCALL 整数输入

二、寄存器

编号名称用途说明
r0$zero零寄存器恒为 0,写入无效
r1$at汇编器临时伪指令用
r2 r3$v0 $v1返回值函数结果 / 系统调用号
r4~r7$a0~$a3参数函数参数 / 系统调用参数
r8~r15$t0~$t7临时变量调用者保存,函数内随意使用
r16~r23$s0~$s7保留变量被调用者保存
r24 r25$t8 $t9临时变量同上
r26 r27$k0 $k1内核保留异常处理用
r28$gp全局指针初始值 0x1800
r29$sp栈指针初始值 0x2ffc
r30$fp帧指针
r31$ra返回地址JAL/JALR 写入

特殊寄存器

名称说明初始值
pc程序计数器 (Program Counter)0x3000
hi乘法高 32 位 / 除法余数0
lo乘法低 32 位 / 除法商0
db / dbpc延迟槽标志 / 跳转目标地址0
stopped停机标志0

三、指令格式

每条指令 4 字节,小端序,按字段提取:

字段位宽提取方式用途
op66&0xFC00 / 1024主操作码
rs55&0x03E0 / 32源寄存器
rt55&0x001F目标/第二源寄存器
rd55&0xF800 / 2048目标寄存器 (R型)
shamt55&0x07C0 / 64移位量 (R型)
funct66&0x003F功能码 (R型二级)
imm1616&0xFFFF立即数 (I型)
jmm2626(hi16 & 0x03FF)*65536 + lo16跳转目标 (J型)

有符号立即数扩展:signimm = ((imm16 + 32768) % 65536) - 32768
分支目标地址:bta = pc + 4 + signimm * 4
跳转目标地址:jta = jmm26 * 4

四、指令集

4.1 R 型指令 (op6=0 → funct6)

funct6指令格式说明
0SLLSLL rd, rt, shamt逻辑左移 (rd = rt << shamt)
2SRLSRL rd, rt, shamt逻辑右移 (零填充高位)
3SRASRA rd, rt, shamt算术右移 (符号填充高位)
4SLLVSLLV rd, rt, rs可变逻辑左移 (移位量 = rs[4:0])
6SRLVSRLV rd, rt, rs可变逻辑右移
7SRAVSRAV rd, rt, rs可变算术右移
8JRJR rs寄存器跳转 (pc = rs)
9JALRJALR rd, rs寄存器跳转并链接 (ra = pc+8, pc = rs)
12SYSCALLSYSCALL触发系统调用 (见 §六)
13BREAKBREAK断点调试 (显示关键寄存器)
16MFHIMFHI rd读 HI 到 rd
17MTHIMTHI rs写 rs 到 HI
18MFLOMFLO rd读 LO 到 rd
19MTLOMTLO rs写 rs 到 LO
24MULTMULT rs, rt有符号乘 (HI:LO = rs × rt),仅低 32 位有效
25MULTUMULTU rs, rt符号乘存根 (未完成)
26DIVDIV rs, rt有符号除 (LO=商, HI=余)
27DIVUDIVU rs, rt无符号除存根 (未完成)
32ADDADD rd, rs, rt有符号加 (rd = rs + rt)
33ADDUADDU rd, rs, rt无符号加
34SUBSUB rd, rs, rt有符号减 (rd = rs - rt)
35SUBUSUBU rd, rs, rt无符号减
36ANDAND rd, rs, rt按位与
37OROR rd, rs, rt按位或
38XORXOR rd, rs, rt按位异或
39NORNOR rd, rs, rt按位或非
42SLTSLT rd, rs, rt有符号比较置 1 (rd = rs < rt ? 1 : 0)
43SLTUSLTU rd, rs, rt无符号比较置 1

4.2 I 型指令 (立即数)

op6指令格式说明
8ADDIADDI rt, rs, imm立即数加 (rt = rs + signimm)
9ADDIUADDIU rt, rs, imm无符号立即数加 (与 ADDI 等价)
10SLTISLTI rt, rs, imm有符号立即数比较置 1
11SLTIUSLTIU rt, rs, imm无符号立即数比较置 1
12ANDIANDI rt, rs, imm立即数按位与 (零扩展)
13ORIORI rt, rs, imm立即数按位或 (零扩展)
14XORIXORI rt, rs, imm立即数按位异或 (零扩展)
15LUILUI rt, imm加载立即数到高位 (rt = signimm << 16)

4.3 Load / Store 指令

op6指令格式说明
32LBLB rt, offset(rs)读字节,有符号扩展
33LHLH rt, offset(rs)读半字 (2 字节),有符号扩展
35LWLW rt, offset(rs)读字 (4 字节)
36LBULBU rt, offset(rs)读字节,无符号扩展
37LHULHU rt, offset(rs)读半字,无符号扩展
40SBSB rt, offset(rs)写字节
41SHSH rt, offset(rs)写半字 (2 字节)
43SWSW rt, offset(rs)写字 (4 字节)

地址计算:address = rs + signimm

4.4 分支 / 跳转指令

op6指令格式说明
1BGEZBGEZ rs, labelrs≥0 则跳转 (rt5=1)
1BLTZBLTZ rs, labelrs<0 则跳转 (rt5=0)
2JJ target无条件跳转 (26 位地址)
3JALJAL target跳转并链接 (ra = pc+8)
4BEQBEQ rs, rt, label相等则跳转
5BNEBNE rs, rt, label不等则跳转
6BLEZBLEZ rs, labelrs≤0 则跳转 (rt5=0)
7BGTZBGTZ rs, labelrs>0 则跳转 (rt5=0)

五、指令编码总表

每条指令 32 位,字段布局如下。位 31 最高,位 0 最低。

5.1 类型模板

类型31–2625–2120–1615–1110–65–0
R op=0 rs rt rd shamt funct
I op rs rt imm (16-bit)
J op target (26-bit)

5.2 R 型 (op=0)

指令rsrtrdshamtfunct操作
SLL0srcdstsa0rd = rt << sa
SRL0srcdstsa2rd = rt >> sa (logical)
SRA0srcdstsa3rd = rt >> sa (arithmetic)
SLLVcntsrcdst04rd = rt << rs[4:0]
SRLVcntsrcdst06rd = rt >> rs[4:0] (logical)
SRAVcntsrcdst07rd = rt >> rs[4:0] (arithmetic)
JRtarget0008pc = rs
JALRtarget0ra09rd=pc+8, pc=rs
SYSCALL12触发系统调用
BREAK13调试断点
MFHI00dst016rd = HI
MTHIsrc00017HI = rs
MFLO00dst018rd = LO
MTLOsrc00019LO = rs
MULTab0024LO = a×b (低32)
MULTUab0025stub
DIVnumden0026LO=商, HI=余
DIVUnumden0027stub
ADDabdst032rd = rs+rt
ADDUabdst033rd = rs+rt
SUBabdst034rd = rs−rt
SUBUabdst035rd = rs−rt
ANDabdst036rd = rs & rt
ORabdst037rd = rs | rt
XORabdst038rd = rs ^ rt
NORabdst039rd = ~(rs|rt)
SLTabdst042rd = (rs<rt) ? 1:0
SLTUabdst043rd = (rs<rt) ? 1:0

5.3 I 型

指令oprsrtimm操作
ADDI8basedstsignrt = rs + signimm
ADDIU9basedstsignrt = rs + signimm
SLTI10adstsignrt = (rs<signimm)
SLTIU11adstzerort = (rs<zeroimm)
ANDI12adstzerort = rs & zeroimm
ORI13adstzerort = rs | zeroimm
XORI14adstzerort = rs ^ zeroimm
LUI150dstsignrt = signimm << 16

5.4 Load / Store (I 型)

指令opbasertoffset操作
LB32addrdstsignrt = sext mem8[rs+imm]
LH33addrdstsignrt = sext mem16[rs+imm]
LW35addrdstsignrt = mem32[rs+imm]
LBU36addrdstsignrt = zext mem8[rs+imm]
LHU37addrdstsignrt = zext mem16[rs+imm]
SB40addrsrcsignmem8[rs+imm] = rt[7:0]
SH41addrsrcsignmem16[rs+imm] = rt[15:0]
SW43addrsrcsignmem32[rs+imm] = rt

5.5 分支 / 跳转

指令oprsrtimm / target条件
BGEZ1test1offsetrs ≥ 0
BLTZ1test0offsetrs < 0
J2— target(26bit) —×4unconditional
JAL3— target(26bit) —×4ra = pc+8
BEQ4aboffsetrs == rt
BNE5aboffsetrs ≠ rt
BLEZ6test0offsetrs ≤ 0
BGTZ7test0offsetrs > 0
offset = imm (sign extended) × 4    → bta = pc + 4 + offset
target = jmm26 (26-bit) × 4         → jta = target

六、系统调用

通过 SYSCALL 指令触发,调用号存入 $v0

$v0服务输入输出说明
1print_int$a0 = 整数打印整数到游戏对话框
5read_int$v0 = 整数弹出输入框读取整数
10exit停止程序执行
注意: read_int 的负数输入有特殊限制。负号按键执行的是「对已有值取反」,而非前缀。正确输入 -5 的方式:先输入 5,再按负号键,然后确认。不能先按负号再输入数字。

七、延迟槽

MIPS 经典特性:跳转指令的下一条指令必定执行(即延迟槽)。

IF npc == 0 {
    pc = pc + 4          // 正常顺序
} ELSE {
    pc = pc + 4           // 执行延迟槽指令
    db = 1                // 标记:下周期跳转
    dbpc = npc
}

含义: 跳转发生时,当前 PC+4 处的指令 仍然会被执行一次,然后下一条指令才从跳转目标开始。这是物理 MIPS CPU 的行为复刻。编写跳转指令时,确保延迟槽指令有实际用途(通常放一条 NOP 或可提前执行的无关操作)。

八、内存布局

区域地址范围说明
程序区0x3000PC 初始值,程序代码加载于此
堆/数据区0x1800~0x2ffb$gp 初始 0x1800
栈区0x2ffc$sp 初始 0x2ffc

内存通过 memory_operation/memory_read_Nmemory_write_N 事件读写,底层为游戏脚本变量数组。

九、与 cmd_computer (x86 风格) 对比

cmd_computerprimula_computer v3
指令集自定义 8-bit opcodeMIPS-I 标准
寄存器8 个 (x86 风格 eax~eflags)32 个 MIPS 通用 + HI/LO
指令宽度1 字节4 字节
译码范围 IF 分发位域提取 + 二级 funct6 分发
延迟槽
系统调用有 (print/read/exit)
调试文本菜单实时寄存器面板 + BREAK
← aliceincradle.org