当前位置: 首页 > news >正文

基于ESP32的独立CP/M模拟器:复古计算与现代硬件的完美融合

1. 项目概述与核心价值

如果你和我一样,对上世纪七八十年代微型计算机的黄金时代抱有浓厚兴趣,同时又热衷于用现代硬件“复活”这些经典系统,那么这个基于ESP32的独立CP/M模拟器项目,绝对值得你投入一个周末的时间。它不仅仅是一个简单的模拟器,更是一个完整的、可以独立运行的“复古电脑”。想象一下,用一块比信用卡还小的开发板,驱动一台标准的VGA显示器,接上老式的PS/2键盘,就能启动一个原汁原味的CP/M 2.2系统,运行WordStar编写文档,或者用SuperCalc处理表格——这种将新旧技术无缝衔接的成就感,是单纯在PC上运行模拟器无法比拟的。

这个项目的核心,在于巧妙地整合了两个优秀的开源项目:RunCPMFabGL。RunCPM是一个用C语言编写的高效Z80 CPU和CP/M操作系统模拟器,而FabGL则为ESP32提供了强大的VGA视频输出、PS/2键盘/鼠标以及声音驱动支持。我们的工作,就是将RunCPM移植到ESP32平台,并利用FabGL为其提供“显示器”和“键盘”,替代原本依赖的串口终端。最终成果是一个“开箱即用”的独立设备,无需连接电脑进行调试,通电即用。对于嵌入式开发者,这是一个学习如何将复杂软件栈(模拟器、文件系统、图形驱动)集成到资源受限MCU的绝佳案例;对于复古计算爱好者,这是一个成本极低、体验纯粹的怀旧平台。

2. 核心组件与方案选型解析

2.1 为什么是ESP32?

选择ESP32作为硬件核心,是经过多方面权衡的结果。首先,性能足够。ESP32搭载双核Xtensa LX6处理器,主频可达240MHz,拥有520KB SRAM。运行一个Z80模拟器和基本的文本模式VGA输出,这个计算和内存资源绰绰有余。其次,外设丰富且成本低廉。ESP32自带Wi-Fi和蓝牙,虽然本项目未使用,但为未来扩展(如网络虚拟磁盘)留下了可能。更重要的是,其丰富的GPIO可以方便地连接各种外设。最后,生态成熟。基于Arduino框架和PlatformIO的ESP32开发环境非常友好,有海量的库和社区支持,大大降低了开发门槛。

2.2 RunCPM模拟器:在非Z80硬件上运行CP/M的魔法

CP/M系统严重依赖Intel 8080/Z80 CPU的指令集。RunCPM的实现原理是“动态二进制翻译”与“系统调用拦截”的结合。它并非模拟整个硬件环境,而是实现了一个Z80指令解释器。当CP/M程序(.COM文件)被加载时,RunCPM会逐条读取Z80机器码,并将其“翻译”或解释为当前主机CPU(如ESP32的Xtensa核心)能执行的等效操作。同时,CP/M通过一系列固定的BDOS(基本磁盘操作系统)和CCP(控制台命令处理程序)系统调用来与硬件交互。RunCPM截获这些系统调用,并将其映射到宿主机的实际操作上,例如将“向控制台输出字符”的调用,重定向到串口或FabGL的VGA文本缓冲区。

注意:RunCPM模拟的是CP/M 2.2的API环境,而不是完整的硬件时序。这意味着绝大多数依赖纯CPU计算的商业软件都能完美运行,但少数直接操作硬件端口、依赖精确时钟周期的软件(如一些游戏或演示程序)可能会出现问题。不过,对于WordStar、dBase II、MBASIC等主流应用,兼容性非常好。

2.3 FabGL库:让ESP32“看见”并“感知”

FabGL是这个项目的“感官系统”。它通过软件bit-banging(位碰撞)技术,在ESP32的通用GPIO上生成了标准的VGA时序信号。VGA接口需要HSync(行同步)、VSync(场同步)和RGB色彩信号。FabGL以极高的精度和稳定性,通过程序控制GPIO电平的变化来模拟这些信号,从而驱动显示器。对于PS/2键盘,它同样通过GPIO模拟PS/2时钟和数据线的协议,实现按键扫描码的读取。这种纯软件实现的方式,最大程度地减少了对外部专用芯片的依赖,体现了嵌入式软件设计的巧妙。

硬件方案选择

  1. VGA32 ESP v1.4模块:这是最省事的方案。该模块已经将ESP32、VGA输出电路(通常是电阻分压网络)、PS/2键盘接口、SD卡槽集成在一块板子上,甚至自带3.5mm音频输出。你只需要连接显示器和键盘即可。它相当于一个“交钥匙”解决方案。
  2. 自制ESP32开发板:如果你喜欢动手,可以用一块ESP32开发板(如ESP32 DevKitC),搭配一些电阻、VGA接头和PS/2接口,在面包板或万用板上搭建。这能让你更深入地理解VGA信号和PS/2协议的硬件连接。你需要参考FabGL库的文档,确定正确的GPIO引脚映射。

3. 开发环境搭建与软件配置详解

3.1 Arduino IDE与ESP32支持包的安装

虽然PlatformIO是更专业的嵌入式开发环境,但原作者使用了Arduino IDE,为了确保与项目源码完全兼容,我们也沿用此路径。首先,确保你安装的是Arduino IDE 1.8.x或更高版本。接着,安装ESP32支持包:

  1. 打开Arduino IDE,进入文件->首选项
  2. 在“附加开发板管理器网址”框中,填入以下URL(如果是多个,用逗号分隔):https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
  3. 点击“好”保存。
  4. 进入工具->开发板->开发板管理器...
  5. 在搜索框中输入“esp32”,找到“esp32 by Espressif Systems”,点击安装。

安装完成后,你就可以在工具->开发板列表中选择ESP32相关的板子了。对于VGA32 ESP模块,如果列表中找不到,可以选择通用的ESP32 Dev Module。关键步骤在于后续的引脚配置。

3.2 关键库的安装:FabGL与SdFat

本项目依赖两个核心库,必须通过库管理器正确安装。

  1. 安装FabGL库

    • 在Arduino IDE中,点击项目->加载库->管理库...
    • 在搜索框输入“FabGL”,在列表中找到它,点击“安装”。确保安装的是官方版本。
    • 重要检查:安装后,最好到文档->Arduino->libraries目录下查看,确认存在FabGL文件夹。有时网络问题会导致安装不完整。
  2. 安装SdFat库

    • 同样打开库管理器。
    • 搜索“SdFat”。你会看到至少两个版本:“SdFat by Bill Greiman”和“SdFat - Adafruit Fork”。
    • 根据原项目说明,虽然早期推荐Adafruit分支,但现在明确指向Bill Greiman的原始版本。因此,请选择并安装“SdFat by Bill Greiman”
    • 这个库负责以高性能、可靠的方式访问SD卡,是CP/M“虚拟磁盘”的底层支撑。

3.3 获取并准备修改版RunCPM源码

原版的RunCPM默认输出到串行终端。社区开发者coopzone已经为我们做好了移植工作,将FabGL集成进去。我们需要使用这个修改版。

  1. 访问项目仓库:https://github.com/coopzone-dc/RunCPM
  2. 点击绿色的“Code”按钮,选择“Download ZIP”。将ZIP文件保存到你的Arduino项目目录(通常是文档/Arduino)。
  3. 解压这个ZIP文件。你会得到一个名为RunCPM-master的文件夹。
  4. 关键操作:将这个文件夹重命名为RunCPM(去掉-master)。这是因为Arduino IDE要求项目文件夹名称与主.ino文件名称一致。
  5. 现在,通过Arduino IDE的文件->打开,导航到刚才重命名的RunCPM文件夹,打开里面的RunCPM.ino文件。

打开后,不要急于编译。先花几分钟浏览一下源码,特别是开头的注释部分。你会看到针对不同硬件(如VGA32 v1.4, ODROID-GO等)的预配置宏定义。确保与你手头的硬件匹配。

4. 硬件连接与关键引脚配置

4.1 VGA32 ESP v1.4模块连接

如果你使用的是成品VGA32模块,连接非常简单:

  • VGA端口:使用VGA线缆连接到显示器。
  • PS/2端口:连接一个PS/2接口的键盘。请注意,是那种圆形的6针DIN接口,不是USB。市场上有很多USB转PS/2的转接头,但兼容性不一,建议直接使用原生PS/2键盘最稳妥。
  • Micro-USB端口:用于供电和程序上传。连接电脑USB口即可。
  • SD卡槽:插入准备好的SD卡(后续步骤准备)。特别注意:在上传程序时,必须拔出SD卡,否则可能导致上传失败。

4.2 自制硬件的引脚连接参考

如果你使用通用ESP32开发板自制,需要根据FabGL库的示例,手动连接VGA和PS/2。以下是一个常见的引脚配置参考(具体请以FabGL示例为准):

信号GPIO引脚说明
VGA_RED0GPIO22VGA红色信号低位
VGA_RED1GPIO21VGA红色信号高位
VGA_GREEN0GPIO19绿色低位
VGA_GREEN1GPIO18绿色高位
VGA_BLUE0GPIO5蓝色低位
VGA_BLUE1GPIO4蓝色高位
VGA_HSYNCGPIO23行同步
VGA_VSYNCGPIO15场同步
PS/2 时钟GPIO32PS/2键盘时钟线
PS/2 数据GPIO33PS/2键盘数据线

VGA信号需要通过一个简单的电阻分压网络(例如,每个颜色通道用270Ω和150Ω电阻分压)来将ESP32的3.3V逻辑电平转换为VGA所需的0.7V峰峰值模拟信号。PS/2接口则需要一个上拉电阻(通常4.7kΩ)连接到3.3V。

4.3 VGA32模块的GPIO12 SD卡冲突问题与解决

这是本项目最大的一个“坑”,也是必须详细说明的硬件知识。根据原文档和我的实测,VGA32 v1.4模块将SD卡的DATA2信号连接到了ESP32的GPIO12上。

问题在于,ESP32在上电或复位时,会读取GPIO12(MTDI引脚)的电平状态,以此判断内部Flash存储器的供电电压应该是1.8V还是3.3V。当SD卡插入时,该引脚可能被SD卡内部上拉至高电平,ESP32误判需要1.8V电压,但实际Flash是3.3V供电,这会导致启动异常、无法上传程序或软复位后死锁。

解决方案有三种,风险递增:

  1. 保守方案(推荐初学者):严格遵守操作顺序。上传程序前,拔掉SD卡。系统完全断电再上电可以正常启动。避免使用板载的“RST”复位按钮进行软复位。
  2. 检查硬件版本:较新批次的VGA32模块可能已经修改了设计,将SD卡换到了其他GPIO(如GPIO13)。你可以用万用表测量SD卡槽引脚到ESP32引脚的通路来确认。
  3. 激进方案(永久修改,有风险):通过烧写ESP32的eFuse(一次性可编程熔丝),强制将Flash电压设置为3.3V,忽略GPIO12的检测。这需要使用esptool.py执行命令:
    esptool.py --port YOUR_SERIAL_PORT write_flash_status --non-volatile 0x2000
    或者使用更具体的命令(取决于esptool版本):
    esptool.py --port /dev/cu.usbserial-XXXX set_flash_voltage 3.3V
    警告:此操作不可逆!如果模块上的Flash芯片不是3.3V供电,此操作会永久损坏(变砖)你的ESP32。仅在你完全确认风险,且模块确实是3.3V Flash(ESP32-WROOM-32系列通常是)的情况下考虑。我个人的VGA32 v1.4模块经过此操作后,SD卡问题彻底解决。

5. 创建CP/M系统盘镜像(SD卡准备)

这是让系统跑起来的关键一步。CP/M通过盘符(A:, B:, C: ... P:)来访问磁盘,在RunCPM中,每个盘符对应SD卡根目录下的一个文件夹。每个文件夹内的数字子文件夹(0-15)对应CP/M的“用户区”。

5.1 SD卡目录结构构建

请严格按照以下步骤操作,任何一步的错漏都会导致系统无法启动,提示“BDOS ERR ON A: BAD SECTOR”或“NO CCP”错误。

  1. 格式化:使用电脑将SD卡格式化为FAT32文件系统。分配单元大小选择默认即可。
  2. 创建盘符文件夹:在SD卡根目录,创建文件夹A,B,C,D... 理论上可以到P。建议至少创建AB
  3. 创建用户区文件夹:在每个盘符文件夹(如A)内,创建文件夹0(这是用户区0,CP/M默认)。你还可以创建1,2等,用于在CP/M内使用USER命令切换。
  4. 放置CCP文件:从你下载的RunCPM-master项目文件夹中,找到CCP子目录。里面有几个不同版本的CCP文件(如CCP-DR.60K, CCP-Z80.60K等)。将CCP-DR.60K文件复制到SD卡的根目录。这个文件是CP/M的“命令解释器”,系统启动时必须从A盘加载它。
  5. 放置系统文件:在项目文件夹的DISK子目录下,找到A.ZIP文件。解压这个ZIP文件。解压后你会得到一些.COM文件(如ASM.COM,STAT.COM等)和一个1STREAD.ME文件。将这些文件全部复制到SD卡的A/0/目录下

最终,你的SD卡目录结构应该如下所示(以树状图表示):

SD卡根目录/ ├── CCP-DR.60K ├── A/ │ └── 0/ │ ├── 1STREAD.ME │ ├── ASM.COM │ ├── DDT.COM │ ├── DUMP.COM │ ├── LOAD.COM │ ├── PIP.COM │ ├── STAT.COM │ ├── SUBMIT.COM │ └── ... (其他.COM文件) ├── B/ │ └── 0/ ├── C/ │ └── 0/ └── ...

5.2 获取更多CP/M软件

初始的A盘只包含一些基本工具。经典的CP/M软件如WordStar、SuperCalc、MBASIC等需要自己添加。你可以在网上搜索“CP/M Software Archive”找到大量的自由软件和旧版商业软件(已进入公共领域)。找到的.COM.CMD文件,只需将它们复制到对应的盘符文件夹的用户区(如A/0/)即可。例如,将WS.COM(WordStar)复制进去后,在CP/M命令行输入WS即可启动。

实操心得:整理软件时,注意CP/M 2.2是8.3文件名格式(最多8个字符的主文件名,3个字符扩展名),且字母必须大写。有些存档中的文件可能是小写,在复制到SD卡前,最好在电脑上将其重命名为大写,避免在CP/M下无法识别。

6. 编译、上传与首次启动

6.1 编译配置与上传

  1. 选择开发板:在Arduino IDE的工具->开发板中,选择ESP32 Dev Module
  2. 配置参数(非常重要):
    • Upload Speed: 921600(提高上传速度)
    • Flash Frequency: 80MHz
    • Flash Mode: QIO
    • Partition Scheme: Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS)Huge APP (3MB No OTA/1MB SPIFFS)。如果后续需要放入很多CP/M软件,选择后者提供更大程序空间。
    • Core Debug Level: 无
    • PSRAM: Disabled(除非你的模块有PSRAM)
  3. 选择端口:将ESP32通过USB连接电脑,在工具->端口中选择对应的串口(如COM3/dev/cu.usbserial-XXXX)。
  4. 编译与上传确保SD卡已从模块上拔出。点击“上传”按钮(向右的箭头)。IDE会先编译,然后上传。编译过程可能需要一两分钟。上传成功后,IDE状态栏会显示“上传完毕”。

6.2 首次启动与验证

  1. 上传完成后,先不要插SD卡。将VGA显示器(支持640x480@60Hz分辨率)和PS/2键盘连接到模块上。
  2. 给模块重新上电(拔插USB线)。此时,屏幕上可能会显示FabGL的初始测试图案,或者直接是黑屏。这是因为没有SD卡,RunCPM无法加载CCP。
  3. 插入SD卡。然后再次完全断电再上电(硬重启)。这次,你应该能看到屏幕上出现滚动的初始化信息,最后停留在经典的CP/M提示符A>上!
  4. A>提示符下,输入DIR并按回车,系统应该会列出A:0用户区下的所有.COM文件,如ASM COM,STAT COM等。恭喜你,你的复古CP/M系统已经成功运行了!

7. 系统使用、软件运行与高级技巧

7.1 基本CP/M命令

  • DIR:列出当前磁盘和用户区下的文件。
  • DIR B::列出B盘的文件。
  • USER 1:切换到用户区1。之后DIR显示的就是A:1下的文件。
  • TYPE READ.ME:显示文本文件READ.ME的内容。
  • STAT *.*:显示所有文件的详细信息(大小、记录数)。
  • PIP B:=A:*.*[V]:使用PIP工具将A盘所有文件复制到B盘,[V]参数表示验证。
  • ED MYFILE.TXT:使用行编辑器ED创建或编辑文本文件。

7.2 运行经典软件

假设你已经将WS.COM(WordStar) 和SC.COM(SuperCalc) 复制到了A/0/目录。

  • 运行WordStar:在A>提示符下,输入WS回车。稍等片刻,你就会看到那个熟悉的蓝色背景、黄色文字的WordStar主菜单。你可以使用方向键移动,^D(Ctrl+D) 向右等经典快捷键。退出WordStar返回CP/M,通常是^K X
  • 运行SuperCalc:输入SC回车。一个空白的电子表格界面就会出现。其操作逻辑与现代Excel截然不同,但公式计算等核心功能一应俱全。

7.3 虚拟磁盘管理技巧

RunCPM将SD卡目录映射为磁盘,这带来了极大的灵活性。

  • 添加新软件:只需将下载的.COM文件通过读卡器复制到PC上SD卡的对应目录(如B/0/),然后在CP/M中切换到B盘 (B:) 即可直接运行。
  • 文件传输:在CP/M下创建的文件(如用WordStar编写的文档.TXT),会以相同的文件名保存在SD卡的对应目录。你可以拔下SD卡,在电脑上直接读取这些文件。
  • 磁盘空间:使用STAT *.*可以查看剩余空间(以“K”字节,即1024字节为单位)。CP/M 2.2本身有磁盘大小限制,但RunCPM的映射机制基本规避了这个问题。

7.4 性能优化与故障排查

  • 屏幕闪烁或滚动慢:FabGL在生成VGA信号时占用了大量CPU时间。RunCPM在模拟Z80时可能会感到吃力,导致屏幕更新慢。可以尝试在RunCPM.ino的源码中,寻找与显示刷新率相关的设置(如vgaTaskDelay),适当增加延迟值,虽然会让屏幕更新稍慢,但能释放更多CPU给模拟器。
  • 键盘无响应或错乱:首先确认是PS/2键盘,而非USB键盘(即使通过转接头)。检查PS/2接口是否插牢。在源码中检查PS/2时钟和数据线的GPIO定义是否正确。某些国产ESP32模块的引脚驱动能力较弱,可以尝试在PS/2的时钟和数据线上加一个上拉电阻(4.7kΩ到3.3V)。
  • 启动时卡住或提示错误:99%的问题出在SD卡目录结构或CCP文件上。请严格按照第5部分的步骤重新检查。确保CCP-DR.60K根目录,且文件名全大写。确保A盘下有0文件夹,且文件夹内有文件。
  • 上传程序失败
    • 确认已拔出SD卡
    • 尝试降低上传波特率(如115200)。
    • 在上传时,按住ESP32模块上的“BOOT”按钮,再点击“RST”按钮,然后释放“RST”,再释放“BOOT”,使模块进入下载模式。
    • 检查USB线是否只供电不传数据,换一条可靠的USB数据线。

8. 项目总结与扩展思考

经过这一番折腾,当绿色的A>提示符在VGA显示器上稳定出现时,那种跨越时空的连接感非常奇妙。这个项目成功地将一个80年代初的主流商业操作系统,塞进了一块21世纪的廉价物联网芯片里,并且保持了完整的交互性和实用性。

我个人在反复搭建和测试中的体会是,细节决定成败。GPIO12的冲突、SD卡目录结构的严格性、文件名称的大小写,任何一个微小的疏忽都会导致失败。这也正是嵌入式开发和复古计算交叉领域的魅力所在——你需要同时理解硬件的电气特性、软件的历史约束以及现代工具链的运作方式。

这个项目还有巨大的扩展潜力。例如,利用ESP32的Wi-Fi功能,可以实现Telnet服务器,让你通过网络终端访问这个CP/M系统;或者实现一个FTP服务器,方便地传输文件。FabGL库还支持鼠标和声音,理论上可以运行一些更复杂的CP/M软件。甚至,你可以尝试移植其他8位模拟器,比如Apple ][ 或 Commodore 64,打造一个多合一的复古游戏站。

最后一个小技巧:如果你觉得每次修改代码后都要拔插SD卡很麻烦,可以在代码中初始化SD卡的部分加入一个延迟,或者通过检测某个GPIO的电平来决定是否挂载SD卡。例如,你可以设置一个拨码开关,当开关断开时,程序跳过SD卡初始化,方便你频繁上传调试;当开关闭合时,才正常启动CP/M系统。这能极大提升开发体验。

http://www.jsqmd.com/news/949510/

相关文章:

  • 终极Windows内核级硬件指纹伪装工具:EASY-HWID-SPOOFER完整指南
  • 上海租车合规选型全解析 资深从业者硬核经验分享 - 奔跑123
  • 盲审前最后一道防线,AIGC 检测误判与降痕全解析
  • 不用写代码!用Supervisely自带工具,4天搞定5711张人像分割数据集标注与格式转换
  • 2026年楚雄州黄金回收白银回收铂金回收门店 TOP5榜单无套路:实体店铺地址电话一览 - 诚金汇钻回收公司
  • 高并发服务器必备:小根堆定时器从设计到实现全流程
  • 5分钟终极指南:免费快速实现网盘直链下载的完整教程
  • 2026武汉特色湖北菜河鲜海鲜网红地标餐厅排行,晓江湖口碑 - 奔跑123
  • 生成式智能搜索下的流量卡位攻略:初创个体如何甄选高兼容性的 GEO 优化 服务商
  • 解密NomNom存档编辑器:三步搞定JSON导出异常问题
  • 2026年红河州黄金回收白银回收铂金回收门店 TOP5榜单无套路:实体店铺地址电话一览 - 诚金汇钻回收公司
  • 吕梁市2026年黄金回收白银回收铂金回收放心选真心推荐 靠谱门店排行 + 联系电话整理 - 中业金奢再生回收中心
  • Python量化交易实战:如何用jqktrader构建高效自动化交易系统
  • 出手西安闲置翡翠,为何优先选正规连锁实体店 - 奢侈品回收测评
  • 企业大模型API采购实战:DMXAPI如何用一个Key集成国内主流模型,顺便把发票管理也搞定了
  • Arduino Uno音乐播放器DIY:从硬件连接到状态机编程全解析
  • 淮北市2026年黄金回收白银回收铂金回收放心选真心推荐 靠谱门店排行 + 联系电话整理 - 中业金奢再生回收中心
  • 深入ethtool -E:网卡EEPROM的Magic Key、Checksum与底层驱动校验机制
  • GPT-4o是当前最新版大模型,不存在GPT-5.5
  • linux软件编程
  • 2026年汉中市口碑首选!黄金回收铂金回收白银回收权威门店 TOP5 附咨询电话 - 信誉隆金银铂奢回收
  • 2026内容创作者生存指南:构建人机协同的CRAFT操作系统
  • 解放你的桌面:My-TODOs如何用本地化设计重塑任务管理体验
  • 2026 年 6 月衢州市防水维修甄选指南:卫生间免砸砖、屋顶阳台外墙地下室漏水检修避坑全攻略 - 吉修匠
  • 实战指南:Python自动化获取B站数据全流程
  • 2026年山东面粉加工设备与豆类脱皮机、磨粉机械源头厂家深度选购指南 - 企业名录优选推荐
  • TCC-G15终极指南:快速掌控Dell笔记本散热性能的完整方案
  • 海南2026年黄金回收白银回收铂金回收权威门店 TOP5+正规可靠机构电话与地址汇总 - 中安检金银铂钻回收
  • 2026年宝鸡市黄金回收白银回收铂金回收门店 TOP5榜单无套路:实体店铺地址电话一览 - 诚金汇钻回收公司
  • 国内合规AI工具实战指南:文心一言、通义千问等主流大模型接入与应用