Hotdry.

Article

CP/M-86 与 MS-DOS 交叉开发工具链构建:16位实模式与现代环境的桥接

基于 tsupplis/cpm86 生态,构建 CP/M-86 与 MS-DOS 的复古计算交叉开发工具链,涵盖 Docker 容器化、16位实模式内存分段、系统调用映射与二进制兼容层设计。

2026-06-04systems

CP/M-86 作为 8086 处理器时代的早期操作系统,在 IBM PC 诞生初期与 MS-DOS 形成直接竞争。尽管最终 MS-DOS 凭借 IBM 合作占据主流,但 CP/M-86 的架构设计 —— 尤其是其清晰的系统调用接口和内存管理模型 —— 为理解 16 位实模式编程提供了独特的历史视角。本文基于 tsupplis 维护的 cpm86-crossdev 生态,探讨如何在现代 Linux/macOS 环境中构建完整的交叉开发工具链,实现 16 位实模式代码的编译、链接与测试。

工具链架构:容器化与复古编译器的整合

现代开发环境与 1980 年代的编译器之间存在显著的 ABI 差异。cpm86-crossdev 采用 Docker 容器化方案,将 rasm86、asm86、MASM 等时代汇编器与链接器封装在受控环境中。这种设计的核心优势在于隔离依赖:宿主机只需安装 Docker,无需处理 16 位工具链在现代 glibc 上的兼容性问题。

工具链的工作流程遵循典型的交叉编译模式:源代码经预处理后进入汇编阶段,生成目标文件;链接器将目标文件与 CP/M-86 系统库结合,输出原始二进制;最后通过 bin2cmd 工具将二进制转换为 CP/M-86 可识别的 .CMD 格式。Makefile 中定义的编译规则需要显式指定代码段(CS)、数据段(DS)和堆栈段(SS)的基址与大小,这是 16 位实模式分段内存模型的直接体现。

16 位实模式开发的内存分段策略

8086 的实模式采用段:偏移寻址,每个段最大 64KB,段寄存器左移 4 位后与偏移量相加形成 20 位物理地址。CP/M-86 在内存布局上采用与 CP/M-80 相似的设计哲学:低地址存放操作系统内核,随后是 Transient Program Area(TPA),供用户程序加载执行。

在交叉开发中,链接脚本需要精确控制段的加载地址。典型的 CP/M-86 程序期望在 TPA 起始处(通常为 0x100)加载,这与 MS-DOS 的 .COM 文件格式兼容,但系统调用接口完全不同。开发者在编写汇编代码时,必须显式管理段寄存器的切换:访问数据时加载 DS,访问堆栈时确保 SS 有效,远调用(far call)时需要同时更新 CS 和 IP。

系统调用映射:BDOS 与 DOS 接口的差异

CP/M-86 的系统调用通过 BDOS(Basic Disk Operating System)接口实现,功能号置于 CL 寄存器,参数置于 DX,调用 INT 0xE0 进入内核。这与 MS-DOS 的 INT 0x21 接口形成鲜明对比 —— 尽管两者都运行在 x86 实模式下,但中断向量号和调用约定完全不同。

对于需要同时支持 CP/M-86 和 MS-DOS 的程序,常见的兼容策略是在程序入口检测运行环境,动态设置系统调用向量。cpm86-kernel 项目中提供的修补内核允许在 CP/M-86 上运行部分未经修改的 DOS 程序,其原理是在 BDOS 层拦截特定 INT 0x21 调用并映射到等效的 CP/M-86 功能。这种二进制兼容层的设计思路与 Wine 在现代 Linux 上运行 Windows 程序类似,只是规模更小、场景更特定。

二进制格式转换与测试流程

CP/M-86 的 .CMD 文件格式与 MS-DOS 的 .COM/.EXE 存在结构性差异。.CMD 文件以 0xC9 开头,包含重定位表,允许程序加载到任意可用的 TPA 地址;而 .COM 文件固定加载到 0x100,无重定位能力。cpm86-cmdtools 提供的转换工具可以在这两种格式间进行有限度的转换,但涉及内存布局或系统调用的代码仍需手动调整。

测试环节是交叉开发中最具挑战的部分。由于现代 x86-64 处理器已移除对 16 位实模式的完整支持(尤其是在长模式下),开发者需要依赖模拟器或兼容层。cpm86-crossdev 推荐的测试方案包括:在 DOSBox 中运行 CP/M-86 模拟层,或使用 86Box 等完整系统模拟器启动修补后的 CP/M-86 1.1 内核。对于持续集成场景,可以配置无头模式的模拟器,通过串口或文件系统共享实现自动化测试。

可落地的开发参数与检查清单

基于上述分析,以下是构建 CP/M-86/MS-DOS 交叉开发环境的可执行参数:

容器化工具链配置:

  • 基础镜像:ubuntu:20.04 或 debian:bullseye-slim
  • 必需包:build-essential, nasm, dos2unix, mtools
  • 复古工具链:rasm86 3.12、MASM 5.1、Microsoft Link 3.02
  • 输出格式:bin → cmd 转换使用 bin2cmd 0.3

内存分段参数(链接器脚本):

  • 代码段基址:0x100(TPA 起始)
  • 数据段大小:≤ 64KB
  • 堆栈段:建议 1KB-4KB,位于数据段高端
  • 重定位表:必须包含,支持 .CMD 格式

系统调用映射检查点:

  • CP/M-86 BDOS:INT 0xE0,功能号 CL,参数 DX
  • MS-DOS:INT 0x21,功能号 AH,参数 DX/BX/CX
  • 文件 I/O:CP/M-86 使用 FCB(File Control Block),MS-DOS 支持句柄式 API

测试验证流程:

  1. 编译生成 .CMD 后,使用 cmdcheck 验证头部格式
  2. 在 DOSBox 中加载 CP/M-86 模拟器,执行程序验证系统调用
  3. 对比在原生 CP/M-86(86Box)和 MS-DOS 兼容层中的行为差异

结语

CP/M-86 与 MS-DOS 的交叉开发不仅是对复古计算的技术探索,更是理解早期 x86 系统设计的有效途径。通过容器化工具链,现代开发者可以在保持开发效率的同时,深入 16 位实模式的底层细节。tsupplis 维护的 cpm86-crossdev、cpm86-kernel 和 cpm86-cmdtools 构成了完整的开发 - 测试 - 分发闭环,为这一小众但富有教育意义的领域提供了可持续的技术支持。


资料来源:

  • tsupplis/cpm86-crossdev: CP/M-86 cross development environment (GitHub)
  • tsupplis/cpm86-kernel: A commodity patched kernel and distribution of CP/M-86 1.1 (GitHub)
  • tsupplis/cpm86-cmdtools: CP/M-86 .cmd file tools (GitHub)

systems

内容声明:本文无广告投放、无付费植入。

如有事实性问题,欢迎发送勘误至 i@hotdrydog.com