构建一个可以运行3D游戏的手持设备
从小时候起,我就一直喜爱《合金装备》。当我拿到像Seeed Studio XIAO ESP32S3 Sense这样小巧的ESP32-S3开发板时(尺寸仅21×17.8毫米),一个疑问始终困扰着我:一款完整的32位PlayStation主机游戏,能否在如此小巧的硬件上原生运行?
我想测试微控制器的极限,挑战双核Xtensa架构下的内存管理和软件光栅化技术,并证明90年代末期复杂的3D游戏,完全可以在如今售价仅15美元的硬件上运行,而无需进行完整的系统模拟。
它是如何工作的?
1. 系统架构
该端口位于原始游戏逻辑与ESP32-S3 SoC之间的三个主要层级:
从游戏到硬件的各层:
•MGS引擎:反编译的C语言,编译为Xtensa指令
•psyz:PlayStation SDK和GTE以C语言实现,包含软件光栅化器
•平台层:基于FreeRTOS的mts调度器、垂直同步时钟、虚拟CD、LCD及输入设备
•ESP32-S3
2. PlayStation SDK 与几何协处理器(psyz)
该移植版本使用 psyz,即 PlayStation SDK 和 GTE 寄存器文件的主机端 C 实现。
GTE 模拟:缺失的 MIPS 内联汇编宏被转换为 psyz 的 GTE 操作,采用游戏原生的 MIPS 汇编代码作为直接规范。
顺序表与 64 位指针:原始 PSX 标签字将一个 24 位指针和一个 8 位长度打包。在现代系统中,主机端指针需要标准的 32 位对齐。将原始的原始顺序表转换为 psyz 的分拆标签 + 长度布局,需修复 42 个局部声明、100 个参数,并解决游戏中引擎头文件中的硬编码布局假设问题。
3. 合作任务调度(Konami MTS 在 FreeRTOS 上)
《合金装备》依赖于 Konami 的多任务调度器(MTS ver2.02)。
每个 MTS 任务都被封装在 FreeRTOS 任务中,并固定到核心 0 上。
游戏的垂直同步中断(VSync)以优先级 10 预占引擎,唤醒休眠中的任务,从而保持调度语义与 PSX 硬件一致。
显示扫描输出和 LCD 数据传输被隔离到核心 1,以防止线程竞争和堆栈损坏。
4. 内存分配(SRAM vs. PSRAM)
内部SRAM速度极快但容量有限(512 KB),而外部PSRAM容量大(8 MB)但速度较慢:
游戏内存(Game RAM)和PSX显存(PSX VRAM):静态分配在PSRAM中(EXT_RAM_BSS_ATTR),位于主基地址数组(mgs_main_ram[0xC9000])上。
任务栈:在内部SRAM中固定为20 KB,以防止从闪存/SPI外设读取时因缓存禁用导致的崩溃。
5. 通过SPI microSD卡的虚拟CD文件系统
游戏直接从存储在SPI microSD卡上的完整71.8 MB STAGE.DIR文件中读取场景数据:
port/virtual_cd.c 将扇区读取请求映射到合成的文件范围。
总线仲裁(Mgs_SpiBusTake/Give)可防止microSD卡读取与ST7789/ILI9341 SPI LCD显示屏在SPI2上进行的数据传输发生冲突。
硬件配置与手持设备构建
为测试开发,设计了两种主要的硬件配置:
•Waveshare ESP32-S3-Touch-LCD-2DIY:用于初期开发,配备16 MB闪存和8 MB PSRAM。
自定义XIAO ESP32S3手持设备:
•SoC:Seeed Studio XIAO ESP32S3 Sense(双核@240 MHz,8 MB PSRAM,带MicroSD扩展插槽)。
•显示:ILI9341 320×240 SPI LCD屏幕,工作频率为40 MHz。
•控制:无人机云台模拟摇杆(动态校准并带有死区)+ ADC上的6键电阻梯形按钮 + 两个专用的开火/瞄准按钮。
关键工程挑战及已解决的问题
•严格别名问题:优化过程中旋转功能消失。GCC 14在-O2选项下因类型转换导致矩阵转置存储被移除。通过在游戏构建时添加 -fno-strict-aliasing 参数解决了该问题。
•“白屏”现象:通过修正SetDrawStp中的包原始长度,并在soft_raster.inc.c中实现正确的GTE饱和限制,丢弃大于1023×511的多边形来解决。
•方块位移偏差:Square0意外地将数值左移了12位,导致所有世界距离缩小了64倍。移除该位移后,玩家移动、子弹轨迹和碰撞检测功能恢复正常。
性能与当前状态
•帧率:在Dock(s00a)等复杂的3D区域,使用15美元的微控制器运行软光栅化3D图形时,可达到8至15 FPS。
•可玩性:实时移动、碰撞检测、摄像机视角、世界脚本、敌人触发事件以及场景切换等功能均可直接从microSD卡运行。
本文编译自hackster.io





