资讯动态

在SIMH中模拟Mitra-15小型机:架构设计与实现要点

发布时间:2026/8/30 11:34:19 来源:尧图企业网站定制
Mitra-15 是法国 CII 公司Compagnie Internationale pour lInformatique在 1970 年代推出的一款小型计算机主要用在工业控制、实验室数据采集和实时教学场景中。SIMH 则是模拟器开发领域使用最广泛的框架之一它为 PDP、VAX、IBM 等大量经典机型提供了跨平台的模拟环境。把一台 Mitra-15 放进 SIMH既是对这段计算机历史的一种保存方式也是一次完整的模拟器开发实践。目前这个项目仍处于 Work in Progress 阶段这篇文章会记录它的设计思路、当前实现方式、验证手段和常见坑位适合想了解 SIMH 内部机制或者准备为自己的新机型编写模拟器的开发者阅读。1. 先想清楚Mitra-15 是什么为什么要放进 SIMH1.1 Mitra-15 的历史定位和硬件特点Mitra-15 是法国 CII 在 1970 年代面向工业实时控制市场推出的小型机。公开资料里它的定位是“工业小型计算机”经常出现在过程控制、数据采集、实验室仪表和教学系统中。相比当时美国市场上的 PDP-8、PDP-11 这些机器Mitra-15 在国内资料很少中文文档尤其稀缺能找到的主要是法文和英文的硬件手册、指令手册以及少量应用案例。从模拟器开发者的角度看Mitra-15 有几个特点非常适合用来练手它是小型机内存空间不大寄存器数量不多指令集规模适中。它强调实时控制所以对中断、定时器、外设输入输出的时序要求比较高。它的操作系统和应用代码存在纸带、磁盘等介质上很多原始资料已经散失模拟器是恢复和保存这些软件遗产的重要途径。这些特点决定了“在 SIMH 中模拟 Mitra-15”不是一件不可能完成的事但也不能用模拟一台单片机的方式简单处理。需要同时理解两个层面一是 Mitra-15 的硬件行为二是 SIMH 这个模拟器框架的接口约定。只有把这两层接起来才能跑通从取指、译码、执行到外设和中断的完整链路。1.2 SIMH 能为这个项目提供什么SIMH 是一个用 C 语言编写的多机种模拟框架最初由 Bob Supnik 发起目前仍在维护者。它的核心思路是把“CPU 执行”和“命令控制台”解耦每个模拟器都是一个独立可执行程序启动后进入sim提示符用户通过load、run、step、examine、deposit这些命令控制模拟器。对 Mitra-15 这样的新机型来说SIMH 的价值主要在这几点提供统一的机器控制台不用自己写命令行解析。提供寄存器查看、内存查看、断点、单步、日志、指令计数等调试能力这些对指令集开发几乎是必需品。提供内存管理、文件加载、设备注册、中断调度等公共设施可以少写很多重复代码。社区已经有很多现成模拟器可以参考比如sim_pdp11.c、sim_vax.c它们在设备抽象、定时器处理、总线错误处理上有很多成熟模式。1.3 为什么不自己写一个独立模拟器自己写一个只跑 Mitra-15 的模拟器当然也可以而且门槛更低。但从长期维护和兼容性看基于 SIMH 有几个明显优势特性自己写独立模拟器基于 SIMH 开发命令行和调试器要自己实现直接复用 SCP 命令体系设备框架要自己设计有统一的 DEVICE / UNIT 模型跨平台移植要自己处理编译问题SIMH 已适配主流平台与其他机型对比无法直接互相加载可运行多种机器便于交叉验证学习交流成本别人需要从头理解熟悉 SIMH 的人可以直接上手所以这个项目选择用 SIMH而不是从零写一个模拟器是一个合理的工程决策。当然这也带来一个前提需要先理解 SIMH 的基本结构否则很容易对着源码不知道从哪里下手。2. 搭建 Mitra-15 模拟器的开发骨架2.1 获取源码和构建环境SimH 的源码托管在 GitHub 上通常使用git clone拉取到本地。开发环境推荐 Linux 或 macOSWindows 上也可以使用 WSL 或 MSYS2。基本依赖只有一个 C 编译器和 make 或 CMake不需要额外安装第三方库。git clone https://github.com/simh/simh.git cd simh make构建完整 SIMH 需要一些时间因为默认会编译所有支持的机型。如果只想构建少数模拟器可以修改 Makefile 中的目标列表或者使用 CMake 的配置选项。注意SIMH 的目录结构在不同版本之间有调整新的代码可能把公共库放在scp/把各模拟器源码放在src/或顶层目录。开始修改前先确认当前源码的实际组织方式不要不加分辨地照抄旧教程。2.2 理解一个模拟器文件需要包含什么在 SIMH 中一个机型通常由几个部分组成一个sim_xxx.c文件包含主函数、CPU 执行循环、设备注册。一个xxx_defs.h头文件定义寄存器结构、设备结构、内存大小、指令掩码等。一个或多个设备实现文件例如终端、磁盘、时钟。对于刚刚开始的 Mitra-15 项目可以先用一个单文件骨架验证链路后续再拆分。这个骨架至少要实现模拟器名称和启动信息。CPU 状态结构体。内存读写函数。寄存器描述表让 SCP 的examine和deposit能直接操作。设备列表把 CPU 和内存注册进模拟器。主循环负责在run命令执行时不断取指执行。下面是一个示意骨架重点不是它能在现有 SIMH 源码中直接编译而是展示结构关系。/* sim_mitra15.c 骨架示意 */ #include sim.h typedef struct { int32_t pc; int32_t acc; int32_t ix; int32_t ir; int halted; } M15_CPU; static M15_CPU m15_cpu; static uint16_t m15_mem[65536]; static t_stat M15_reset(DEVICE *dptr); static t_stat M15_examine(DEVICE *dptr, UNIT *uptr, int32_t val, int32_t mode); static t_stat M15_deposit(DEVICE *dptr, UNIT *uptr, int32_t val, int32_t mode); DEVICE M15_dev[] { { M15, NULL, NULL, m15_mem, NULL, 0, 65536, 0, 0, NULL, NULL }, { NULL, NULL } };这里只是一个抽象示意。真正写的时候需要按照当前 SIMH 源码中DEVICE结构体和UNIT结构体的字段定义来填充。如果版本不同字段名可能不同。2.3 把模拟器加入构建并启动到提示符要让sim_mitra15生成可执行文件需要把它加入构建系统。在 Makefile 中通常定义一个所有模拟器的列表例如SIM_OBJS sim_mitra15.o然后确保链接时包含scp公共库。如果构建成功运行后应该进入类似下面的会话$ ./sim_mitra15 Mitra-15 simulator V0.1 (Work in Progress) sim出现sim提示符代表骨架已经生效此时可以输入exit退出或者help查看通用命令。2.4 这个阶段最容易踩的坑初始化顺序许多初学者在写骨架时会在main函数里手动调用一堆初始化函数比如设置内存为零、设置 PC 初始值、打印版本号。这在独立程序里没问题但在 SIMH 中并不推荐。原因是 SIMH 的sim_init会遍历设备列表调用每个设备的 reset 回调。如果机器初始化逻辑没有放在 reset 回调里就会出现“启动一切正常但运行reset命令后状态没有恢复”的情况。更隐蔽的是load文件、设置寄存器值等命令可能依赖 reset 完成的初始状态。推荐的做法是定义统一的 reset 函数把初始化内容放在里面并在DEVICE结构中注册。static t_stat M15_reset(DEVICE *dptr) { memset(m15_cpu, 0, sizeof(m15_cpu)); memset(m15_mem, 0, sizeof(m15_mem)); return SCPE_OK; }然后让设备结构体指向这个函数。这样无论是启动、reset命令还是模拟器异常返回状态都会保持一致。3. 实现 Mitra-15 的 CPU 核心取指、译码、执行3.1 先从最小指令集开始一个完整的 Mitra-15 指令集需要按照官方手册逐条翻译。但在 Work in Progress 阶段不建议一次性实现全部指令而是先确定一个最小集能跑通“读内存、改寄存器、跳转、停机”这条链路。最小指令集可以按功能分成这几类指令分类作用为什么先做加载/存储把数据在寄存器和内存之间移动所有程序的基础算术逻辑加法、减法、与、或、移位等验证 ALU 执行逻辑跳转条件跳转、子程序调用控制流验证停机停止 CPU返回 SCP没有它无法结束执行循环具体的操作码、寄存器数量和寻址方式必须根据 Mitra-15 的手册确定我不在这里编造。下面的代码全部使用占位写法表示“接入时替换为真实编码”。/* 示意取指并分派 */ static int m15_step(void) { uint16_t opcode m15_mem_read(m15_cpu.pc); m15_cpu.pc INST_SIZE; /* 以手册为准 */ m15_cpu.ir opcode; switch (opcode OP_MASK) { case 0x0000: /* 伪指令 */ m15_halt(); return SIM_HALT; case 0x1000: /* 示例加载立即数 */ m15_cpu.acc (opcode 0x00FF); break; case 0x2000: /* 示例跳转 */ m15_cpu.pc (int32_t)(opcode 0xFFFF); break; default: m15_undef(opcode); return SIM_HALT; } return SIM_OK; }这里的INST_SIZE、OP_MASK、操作码的0x1000、0x2000都是示例不是真实的 Mitra-15 编码。在写代码时建议把编码常量放在头文件中并用注释标明手册页码这样后续核对会非常方便。3.2 寄存器模型和执行环境Mitra-15 的寄存器需要从手册确认。为了演示我们假设它至少有一组常用寄存器包括程序计数器、累加器、状态寄存器和变址寄存器。这里的命名只是占位。typedef struct { uint16_t pc; /* 程序计数器 */ uint16_t acc; /* 累加器 */ uint16_t ix; /* 变址寄存器如果存在则实现 */ uint16_t st; /* 状态字进位、零、负、溢出等标志 */ uint16_t ir; /* 当前指令寄存器 */ int halt; /* 停机标志 */ } M15_Regs;在 SIMH 中要让寄存器能通过examine和deposit命令查看与修改通常需要实现一个寄存器表。这个表里保存每个寄存器的名称、地址、位宽、读写函数。这样调试时就可以直接sim examine pc pc: 000200 sim deposit acc 42 sim examine acc acc: 0000423.3 编写执行循环执行循环是模拟器的发动机。它要做的事情非常简单调用m15_step如果 CPU 没有停机就继续然后定期检查外部事件比如控制台是否要中断、设备是否产生中断。static t_stat m15_run(double *sim_time) { while (!m15_cpu.halt) { m15_step(); m15_irq_check(); /* 检查外设中断 */ m15_console_check(); /* 检查用户控制台输入 */ } return SCPE_OK; }这个循环的粒度很重要。如果每次循环只执行一条指令单步调试会比较方便但性能可能会受影响。Mitra-15 这样的小型机执行速度不会太快所以一条指令一个循环是可接受的。如果未来要跑复杂操作系统则需要考虑批量执行和定时器校准。3.4 未实现指令的处理策略在模拟器开发早期最怕的事情是“遇到未实现指令后默默忽略”。如果忽略程序会跑飞而开发者很难定位。正确的策略是遇到未实现的 opcode打印指令地址、指令编码和当前寄存器状态然后停止模拟。static void m15_undef(uint16_t opcode) { fprintf(stderr, Undefined opcode %04X at PC%04X\n, opcode, m15_cpu.pc - INST_SIZE); m15_show_regs(); m15_cpu.halt 1; }这种“fail fast”的方式看着简单但会节省大量时间。不要试图让模拟器“猜”出未实现指令的含义一旦猜错后续所有验证结果都不可信。3.5 为什么执行循环里不要频繁打印很多初学者会在每条指令后打印一条日志例如PC0200 IR1234 ACC000A这在小程序上还能忍受但一旦跑几千条指令日志会淹没真正有用的信息。正确做法是在m15_step内部不打印只在断点、寄存器变更或错误时输出或者通过日志系统按需记录。SIMH 提供了日志控制命令可以直接利用。4. 加载程序镜像并验证第一条指令4.1 准备一个最小测试程序在实现第一条指令后需要用一个程序验证“加载、执行、停机”这条通路是否正常。假设我们实现了一个把立即数写入累加器的指令和一个停机指令那么可以写出这样的测试程序; 示意程序助记符需要按 Mitra-15 手册调整 LDA #0x2A ; 把 0x2A 加载到累加器 HLT把这段汇编手动汇编成机器码后得到一个简单的二进制镜像文件。文件格式可以自己定义例如每行一个地址和一个 16 位字0000 102A 0001 7000这里的102A和7000是占位编码。4.2 实现一个简单的镜像加载函数SIMH 已经支持多种标准格式例如二进制加载格式和十六进制格式。但在 Work in Progress 阶段可以先实现一个最简单的文本加载函数后面再对齐 SIMH 的通用加载接口。static t_stat m15_load_image(const char *filename) { FILE *f fopen(filename, r); int addr, val; if (f NULL) { return SCPE_OPEN_ERR; } while (fscanf(f, %x %x, addr, val) 2) { m15_mem_write(addr, val 0xFFFF); } fclose(f); return SCPE_OK; }这个函数的优点是简单缺点是只适用于自定义格式。如果后续要加载纸带文件或操作系统镜像需要实现更复杂的格式解析。但在这个阶段它足够验证 CPU 执行逻辑。4.3 在 SCP 中运行测试程序启动模拟器后通过命令加载镜像设置初始指针然后运行。sim load test.hex sim deposit pc 0 sim run ... 运行结束 ... sim examine acc acc: 00002A sim examine pc pc: 000002如果acc的值是0x2A并且pc停在下一条指令的地址说明取指、执行和停机逻辑都正常。注意不要只验证程序能“结束运行”。还要验证寄存器值是否符合预期、PC 是否停在正确位置、状态标志是否正确。只看到“模拟器停了下来”并不等于指令执行正确。4.4 字节序和地址编码的坑小型机时代不同厂商对内存字和字节序的处理习惯并不统一。Mitra-15 如果使用 16 位字你在从十六进制文件、纸带二进制或磁盘镜像读取时需要明确每个字节应该如何拼成一个字。常见错误是内存按字写但镜像按字节读导致高低字节颠倒。地址字段是八进制你按十六进制解析导致加载位置错误。指令长度不是固定 2 字节而你用固定偏移去步进 PC。遇到这种问题时先打印加载后的内存内容用examine 0和examine 1核对而不是直接运行。镜像加载的排查顺序应该是文件解析路径、字节组合方式、地址写入位置、PC 初始值。5. 外设和实时行为从一个控制台设备开始5.1 Mitra-15 的外设到底模拟到什么程度Mitra-15 作为工业控制计算机常见外设包括纸带阅读机、纸带穿孔机、电传打字机、磁盘、数模转换器、数字 I/O 等。在 SIMH 中外设模拟通常不追求完全重建物理信号而是把时间行为抽象成几个关键属性设备是否 ready也就是是否可以向 CPU 请求输入或输出。数据寄存器何时有效写入时何时完成。中断请求何时触发何时应该被清除。设备的工作时间例如磁盘寻道时间、纸带走带时间。在 Work in Progress 阶段不建议一开始就模拟磁盘。最好从一个最简单的字符终端设备开始因为终端可以立即和当前 SCP 控制台交互验证输入输出链路。5.2 实现一个最小终端设备终端设备可以简化成两个寄存器输入数据寄存器和输出数据寄存器。CPU 向输出寄存器写一个字符时模拟器把这个字符打印到屏幕CPU 读输入寄存器时模拟器从键盘缓冲区取一个字符。在 SIMH 中设备通常通过UNIT结构描述设备服务函数在每次中断周期或者每次 IO 操作时被调用。示意代码如下static t_stat m15_tty_svc(UNIT *uptr) { int ch; if (m15_tty_rdy_in) { // 从控制台读取字符 ch sim_poll_kbd(); if (ch ! 0) { m15_tty_in_data ch; m15_tty_rdy_in 0; m15_irq_raise(M15_IRQ_TTY_IN); } } if (m15_tty_rdy_out) { sim_printf(%c, m15_tty_out_data); m15_tty_rdy_out 0; } return SCPE_OK; }这个函数会在模拟主循环中被周期调用。关键是不要在一个无限循环里去等待输入否则整台模拟器都会被卡住。正确做法是设备服务函数每次只检查状态如果没数据就立即返回等待下一个时间片再次检查。5.3 定时器和中断处理实时时钟是工业控制机的核心外设。Mitra-15 如果要跑原始实时程序肯定需要模拟定时器中断。在 SIMH 中这通常靠设备服务函数按固定频率被调用或者通过sim_os_sleep等方式实现定时。一个简单的周期时钟可以这样设计static t_stat m15_clk_svc(UNIT *uptr) { m15_cpu.clock_ticks; if ((m15_cpu.clock_ticks % 100) 0) { m15_irq_raise(M15_IRQ_CLOCK); } return SCPE_OK; }这里每 100 个服务周期触发一次时钟中断具体频率需要与实际机器对齐。模拟器的精度取决于定时器服务周期与真实时间的换算关系这是后续最耗时的工作之一。5.4 学习环境与生产环境的外设差异学习阶段使用的最小终端设备只需要能用按键输入、能显示输出完全可以忽略时序。但如果目标是运行 Mitra-15 的完整操作系统外设时序就必须精确到指令周期级别否则驱动会等待超时或者误判设备状态。不同目标决定了你要投入多少精力在时序细节上。如果只是验证指令集终端设备可以非常粗糙如果要做数字保护系统、控制系统的仿真那么磁盘的寻道时间和中断优先级就是正确性的关键。6. Work in Progress如何验证指令模拟正确性6.1 建立指令测试用例模拟器写好后精确验证是必须的。最简单的方法是针对每条指令准备一个测试用例设置初始寄存器状态、写入内存、设置 PC、运行若干条指令然后检查寄存器值和内存值。测试编号指令初始条件期望结果实际结果T01LDA #42ACC 任意ACC 42待验证T02ADD 0100ACC5, mem[0100]7ACC12待验证T03JMP 0PC0100PC0待验证把这个表格转换成批处理脚本用 SCP 命令自动执行; test.do 示意 deposit pc 0 deposit acc 0 load t02.hex run examine acc如果期望值是 12但实际得到其他值就说明ADD指令的取操作数、相加或进位处理有问题。6.2 用单步和断点定位错误SIMH 提供了step和断点命令指令执行流程可以逐条观察。在发现混合指令错误时不要在第一次错误就退出而是记录错误模式缩小范围。推荐的排查顺序是确认取指是否读到了正确地址。确认操作码译码是否正确。确认操作数地址计算是否正确。确认 ALU 运算和状态标志更新是否正确。确认 PC 步进和跳转目标是否正确。其中最容易忽略的是“操作数地址计算”。很多模拟器错误并不是 ALU 算错了而是间接寻址、变址寻址时的地址拼写错了。6.3 指令计数和日志定位未实现指令当跑一段较长的程序时如果中途停下来需要知道它到底执行到哪里、因为什么指令停下。可以在m15_step中维护一个指令计数并在停机时输出static uint64_t m15_insn_count; static void m15_show_stats(void) { printf(Instructions executed: %llu\n, (unsigned long long)m15_insn_count); }日志建议记录最后几条指令的 PC、操作码和寄存器方便回溯。6.4 交叉验证和受限条件如果存在其他 Mitra-15 模拟器或仿真硬件可以用同一个二进制镜像在两个系统上运行对比结果。但要注意两台机器都必须使用相同的外设配置和初始内存状态否则差异可能来自外部因素。如果没有现成参考实现就只能依靠原始手册中的指令执行周期和例程示例来验证。这时要特别小心“输入缺失”的问题不要试图编造手册里没有的指令行为宁可标注为未验证状态也不要在模拟器里“猜测”。7. 常见问题与排查路径7.1 编译时找不到 sim_printf 或 sim_os 等符号现象编译或者链接时报错说找不到sim_printf、sim_os_ms、sim_register_device等函数。原因公共库没有被正确链接或者源码文件放置位置不对。SIMH 的公共函数通常在scp目录下必须确保新模拟器文件的编译命令包含了这些库。解决方式查看当前 Makefile 中其他模拟器是怎么编译的。把新文件加入正确的目标列表。避免自己重新实现同名函数否则会导致符号冲突。确认#include的路径正确不要使用绝对路径。排查顺序检查gcc命令中的-I参数是否包含scp目录。检查-L和-l是否包含公共库。检查是否有多个main函数。7.2 run 命令执行后一直停不下来现象执行run后模拟器不返回提示符也没有输出。原因程序陷入死循环。PC 初始值指向全 0 内存区而 0 被解释为 NOP 指令于是一直空转。停机指令还没有实现执行流不断落到默认分支。控制台没有收到“停止”信号循环判断条件不成立。解决方式先在键盘上按 Ctrl-C 或-看是否能中断确认模拟器没有挂死。使用deposit pc设置一个明确有指令的地址。在run前先step几步确认每一条指令的 PC 变化。检查m15_step是否有halt退出机制。如果程序确实进入死循环可以用断点命令在可疑地址下断点然后让程序跑观察是否触发。7.3 加载十六进制文件后内存值和预期不符现象通过examine查看内存时地址对了但数据不对比如高低字节颠倒或地址偏移。原因文件格式解析错误比如把八进制地址当十六进制。字类型长度不对fscanf读入 int 后截断错误。字节序颠倒。地址写错位置比如没有执行 PC 对齐。解决方式先打印文件的原始字节再打印内存写入前的组合值逐步排查。不要直接跑程序因为错误往往在加载阶段就已经发生。7.4 外设中断风暴现象程序一开始执行就不断进入同一个中断处理程序CPU 没有空闲时间。原因设备的ready位或中断请求在服务完成后没有被清除。设备服务函数每次调用都无条件置起中断标志。中断优先级处理逻辑错误没有在响应时关中断。解决方式检查设备服务函数中的状态位更新顺序。典型模式是CPU 读取设备数据时设备清除 ready 位设备准备好新数据时由服务函数重新置位。如果服务函数每次执行都无条件置位就会形成中断风暴。7.5 排查清单确认 CPU 结构体和寄存器表是否与DEVICE结构绑定。确认reset回调是否被正确注册。确认m15_mem_read和m15_mem_write是否越界保护。确认 PC 步进长度是否为固定指令长度。确认指令匹配掩码和实际操作码是否一致。确认 CPU halt 后是否将控制权交还给 SCP。确认外设设备和中断控制器是否注册到同一个设备列表。8. 最佳实践与扩展方向8.1 新模拟器开发检查清单写一个新的 SIMH 机型建议按这个清单推进构建 hello world 级别的空模拟器能进入sim提示符。实现内存读写让examine和deposit能操作内存。实现最小指令集至少支持加载、存储、跳转、停机。实现镜像加载用简单十六进制文本验证通路。为每个已实现指令建立测试用例。加入断点、单步、日志支持。接入一个简单终端设备验证 IO 指令。实现定时器和中断验证实时行为。再逐类补充指令每补一类跑一遍回归测试。最后再处理磁盘、网络、图形面板等大外设。这个清单的价值在于避免“一开始就陷入磁盘镜像格式”的泥潭。基础通路没跑通之前一切高级功能都是空中楼阁。8.2 控制实现范围保持可验证状态Work in Progress 项目的最大风险是“分支太多没有一个分支能跑”。建议在 Git 仓库中为每个里程碑打 tag例如min-cpu、tty-working、irq-working。每个 tag 都对应一个可编译、可运行、可通过对应测试用例的状态。这样无论后续怎么改都可以随时回退。8.3 利用 SIMH 的外部工具和调试命令SIMH 不只是run和step它还支持日志文件、多控制台、网络连接等能力。在排查问题时善用以下命令sim set logfile sim.log sim set log on sim step 10 sim examine -g 0 100 # 查看连续内存具体语法以版本为准定时查看日志文件可以帮助你了解程序执行到哪一步时开始偏离预期。8.4 扩展方向Mitra-15 在 SIMH 中一旦能够稳定执行指令集和终端 IO后续可以扩展的方向包括加载原始 Mitra-15 操作系统或运行库镜像这一步能验证指令集完整性。实现纸带阅读机和穿孔机支持真实软件介质格式。实现 DMA 控制器让磁盘读写不再占用 CPU 指令周期。实现多台 Mitra-15 的网络互联用于模拟多机控制系统。开发一个图形前面板用指示灯和开关展示运行状态。每一步扩展都应该配套对应测试程序。比如纸带阅读机至少准备一个“读一段纸带内容并打印到终端”的测试验证轮询和中断两种模式都正常。8.5 对新手最有价值的建议如果第一次写模拟器不要直接用 Mitra-15 作为第一个项目可以先在 SIMH 中运行一下已有的 PDP-11 模拟器理解sim命令、设备、寄存器之间的关系。然后再拿 Mitra-15 的指令手册从一个最简单的指令子集开始。把目标定在“跑通一个三指令程序”而不是“模拟整台机器”压力会小很多。Mitra-15 的 SIMH 模拟器还处在 Work in Progress 阶段但它已经是一个很好的工程样本它展示了一台 1970 年代的小型机如何通过现代模拟框架得到虚拟生命也展示了模拟器开发中真正消耗时间的地方在哪里。继续走下去的关键不是追求代码量而是每加一条指令、每加一个设备都保持可验证、可回滚、可解释。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价