Hyaika Blog

Penguin is all you need

技术

一台 2002 年的调音台里,藏着一颗 1991 年的 386——有人写了 BIOS,跑起了 DOS

🔧 一台 2002 年的调音台里,藏着一颗 1991 年的 386——有人写了 BIOS,跑起了 DOS

目录

  • 32 年后,一个 386 处理器重新被唤醒
  • 两台「上古机器」的相遇
  • 三段折叠的开发经历
  • 为什么这件事比「cool project」更有意思
  • 结尾:一台调音台里什么都有,唯独没有说明书

32 年后,一个 386 处理器重新被唤醒

上周有人在 HN 上发了一篇博客,标题很长:「Running DOS on Behringers DDX3216 with a DIY x86-Bios from Scratch」。91 分,不算顶流热度,但评论区 50 多条讨论,从深夜吵到第二天中午。

一个德国嵌入式开发者 Chris,花 3 周时间逆向了一台 Behringer DDX3216——这是一款 2002 年发布的数字调音台,24bit/96kHz,16 路输入,当年卖 $1,300,比同规格的 Yamaha/Yamaha 便宜近一半。Behringer 的老传统了:功能堆满了,价格砍半了,内部用料全靠自己扛。

Chris 在 2026 年拆开这台机器时,发现了一件令他惊讶的事:主板中央躺着一颗 AMD Elan SC300——这是一颗 1991 年设计的 386SX 兼容 SoC,集成了 UART、PCMCIA、GPIO、LCD 控制器,主频 33MHz,16MB DRAM。

32 年前的处理器,运行 24 年前的调音台固件。

这个发现的直接后果是——Chris 决定手写一个 BIOS,在这台调音台上跑 DOS。


两台「上古机器」的相遇

Chris 在博客里写了一段很有画面感的话——1994 年他得到第一台电脑:Intel i486 DX2-66,4MB RAM,512MB 硬盘,系统是 OS/2 和 Windows 3.11。他在那台机器上学会编程,从 BASIC 到升级硬件,但从没接触过引导过程和 MS-DOS 的底层细节

32 年后,2026 年,他在 Behringer DDX3216 的拆机截图里看到 AMD SC300 的标识,大脑里的神经元立刻被点燃了。

他要做的不是简单地把一个操作系统塞进调音台。他的目标是:从头理解 x86 系统是怎么启动的,DOS 是怎么接管硬件的,要走到命令行需要跨越哪些障碍。

这本质上是一次「用最硬核的方式补完少年时期欠下的知识债」。

Behringer DDX3216 实物


三段折叠的开发经历

第一周:从零开始的 x86 BIOS

Chris 的第一步是找现成的 BIOS。正常逻辑:AMD Elan SC300 是 x86 兼容 SoC,应该有现成的嵌入式 BIOS 可用吧?

他联系了 PC Engines(一家瑞士公司,专门做 AMD Elan 开发板的 BIOS),得到的回复是:有 SC400 和 SC520 的源码,没有 SC300 的。

他又联系了 General Software(1989 年成立,后来 2008 年被 Phoenix 收购),Phoenix 德国的负责人花了几周翻档案,最后说:32 年了,没了。

于是只能自己写。

Chris 从零开始写 x86 汇编的 reset vector——就是 CPU 复位后跳到的第一个指令位置(0xFFF0)。这个模式从 8086 时代一直保留到今天,包括 Intel Core i9 和 AMD Threadripper,冷启动时仍然是 16 位实模式,从同一个位置开始。

reset_vector:
    nop         ; no-operation
    cli         ; disable interrupts
    jmp start   ; jump to beginning of current segment

    .zero (0x10 - (. - reset_vector) - 8)
    .ascii "06/04/26"   ; MM/DD/YY

这段代码的二进制放在 BIOS ROM 的 0xFFF0 位置,0x90 是 nop,0xFA 是 cli,0xE9 0x0B 是跳到地址 0x0B 处继续执行。就这样,CPU 从复位状态进入了 16 位实模式。

Chris 用 PicoROM(基于树莓派 Pico 的 ROM 模拟器)替代了反复烧录 EEPROM 的繁琐流程。先验证了原始固件能正常启动,然后把自制 BIOS 刷进去——第一次看到串口输出 "A" 字符时,他知道这条路走通了。

然后他花了更多时间调试外部 UART——DDX3216 用的不是 SC300 内部 UART,而是外接的 Toshiba TLC16C552。Behringer 通过一片 74HCT125 逻辑芯片做了输出使能控制,要走过 GPIO、并行口、SLIN# 信号、串口寄存器——全都要读 2002 年的芯片手册才能摸清。

第二周:LCD、段寄存器和 640KB 的终点站

调音台的显示屏连接在 SC300 的 4-bit LCD 接口上——这颗 SoC 实现了一个 CGA/HGA 兼容的显示控制器。文本模式下,每个字符占 2 字节(1 字节字符 + 1 字节属性色),存储段是 0xB800。

问题来了:BIOS 运行的代码段是 0xF000,DS 也指向 0xF000。想往 0xB800 写数据,需要一个「跨段写入」——用 ES(Extra Segment)寄存器手动设目标段,再通过 mov %%es:(%%bx) 写数据。

Chris 写了一个 writeFarByte(segment, offset, value) 的函数,用内联汇编操作 ES 寄存器。这是 x86 实模式最经典的「段寄存器体操」——也是为什么 DOS 时代的程序员看到这行代码会会心一笑。

他在博客里详细画了一张 x86 实模式的内存布局图:

地址 用途
0x00000 - 0x00400 IVT(中断向量表,1KB)
0x00400 - 0x00500 BDA(BIOS 数据区)
0x00500 - 0x07C00 DOS 内核空间(~29KB)
0x07C00 引导扇区加载地址
0x07E00 - 0xA0000 常规内存(~605KB)
0xA0000 - 0xC0000 视频 RAM(128KB)
0xC0000 - 0xF0000 扩展 ROM / 高内存(160KB)
0xF0000 - 0x100000 系统 BIOS(64KB)

640KB 上限的由来,一个图就讲清楚了:8086 的 20 位地址总线只能访问 1MB,但上端 384KB 被显卡、ROM、扩展设备固定占用,留给程序的空间只剩 640KB——而且这 640KB 里还要扣掉 IVT、BDA 和引导扇区,实际上只剩 605KB。

这就是为什么 DOS 老玩家对「himem.sys」「QEMM」「umb」这些名字充满感情——它们都是在跟这段硬件边界作战。

第三周:MS-DOS 6.22 挂了,FreeDOS 成了

整个项目中最紧张的时刻是第一次尝试引导操作系统。

Chris 准备好了 CF 卡(通过 PCMCIA 转接),写好了磁盘读写的中断服务程序(INT 13h),实现了必要的 BIOS 中断函数(INT 10h 显示字符、INT 16h 键盘、INT 1Ah 实时时钟),然后插卡、通电。

什么都没发生。

串口日志显示,MS-DOS 6.22 的启动过程进入了 INT 15h(系统服务中断)后卡死了。Chris 通过调试发现堆栈指针几乎降到了 0x0000——他设置的初始栈在 0x7C00 下方,但 DOS 运行时不断压栈,栈空间不够,溢出到了 IVT 区域,破坏了中断向量表。

这是实模式最经典的陷阱之一:栈向下生长,IVT 在最底部,栈溢出就会踩到 IVT,然后所有中断调用全部崩溃。

他换了一张牌:FreeDOS v1.4。这是因为 FreeDOS 对 BIOS 实现质量的要求比 MS-DOS 更宽容——它是一个还在活跃开发的开源项目,代码路径更清晰,对非标准硬件的兼容性更好。

FreeDOS 1.4 在 DDX3216 上启动成功

FreeDOS 的 shell 出现在调音台的 LCD 屏幕上时,Chris 说这是「three weeks of work compressed into a single moment」。

他把 GitHub 仓库链接放在了文章末尾,附了一句:下一步是尝试图形模式,看看能不能跑 Windows 2.0 或 3.0。


为什么这件事比「cool project」更有意思

HN 评论区的角度比我想象的丰富。

有人指出 2000 年代初在嵌入式产品中使用 x86 处理器其实非常常见——Intel 直到 2007 年左右还在向特定工业客户供货 386。你的汽车、你的工厂设备、甚至一些银行终端,里面可能都有一颗 386 在默默地数着时钟周期。

有人回忆 DDX3216 时代的 Behringer——功能堆满、价格砍半、做工全靠运气。但用过的人都承认,在 $1,300 这个价位上,它确实没有对手。评论区有人说「The DDX3216 was such a nice piece of tech to work with, sure it had its flaws, but compared to what else was out there the value was just insane for the time。」

但最让我留意的评论是这一条:

"This is the kind of project that makes embedded systems feel less like engineering and more like archaeology with a soldering iron."

——这不再是工程,这是用电烙铁做的考古学。

一台 2002 年的机器,用了 1991 年的芯片,2026 年还有人给它写新的固件。处理器从复位向量开始逐字节引导到 DOS shell——这三周里 Chris 读的每个寄存器、写的每个字节,都是 32 年前这本硬件手册里写好的接口。

x86 兼容性最疯狂的证明不是能跑最新的游戏——是 1991 年设计的 SoC 在 2026 年还能被一个人从零手写的固件引导,而且每一步都命中。


结尾:一台调音台里什么都有,唯独没有说明书

Chris 在文章最后列了下一步计划:给调音台的 VU 表 LED 阵列写驱动(5 级联的 8-bit 移位寄存器,交替接 GND 和 VCC,每个字节控制一组 LED)、通过触控推子和旋钮的数据、把 ADSP-21065L SHARC DSP 纳入控制范围……

但我觉得最有意思的是他在文章里轻描淡写提了一句话:

"The full sourcecode can be found on GitHub。There I prepared some DIY bootsector-programs that are able to switch to the protected mode as well."

他不仅写了一个能跑 DOS 的 BIOS——他还用这个 BIOS 进入了保护模式。32 位的 x86 世界,在这台 24 年前的调音台上被打开了。

如果一台调音台都能变 DOS 电脑——那我服务器的角落里是不是也藏着我没发现的秘密?

算了,还是不 cat /dev/port 了,三周可不够我修。

分享:

评论(0)

暂无评论,来写第一条吧~

发表评论