# 诞生那一刻

*1981 年 4 月 28 日，86-DOS 1.00 长什么样*

DOS 起源考古 · 第一册。行号基准：Paterson-Listings 3_source_code/86-DOS_1.00/86DOS.ASM（MIT）。


## 0 · 一叠纸

*2026 年 4 月 28 日，微软把一份打印清单放到了网上*

2026 年 4 月 28 日，微软在开源博客宣布公开一批早期 DOS 打印清单及其转录源码。按微软的说法，这是目前发现的最早 DOS 源码材料；这一天也正是 86-DOS 1.00 定版 45 周年。这些材料不是一份从磁盘镜像中恢复出来的完整发行版，而是 Tim Paterson 保存的连续纸清单。研究者找到原件、完成扫描和转录之后，代码才变成今天可以检索、复核和装配的文件。
> **先看材料形态** 这批材料更像一份开发现场档案，而不是一张完成品说明书：打印清单、修订记录、不同时间点的快照和手写批注，仍然保存在同一条材料链上。
先把材料说清楚，因为它和通常的「开源源码」不是一回事。这叠纸里不能简单认定存在一份代表正式发行时刻的唯一源码副本，现存的是一批打印清单（assembler listing）：汇编器把源码打印在连续纸上时，会在每行前面印上行号和地址，旁边配上当时的注释。程序员在纸上改，再把改完的内容敲回机器。Paterson 在 2025 年 4 月 24 日回复考证者 Yufeng Gao 的邮件里，自己把这叠纸清点了一遍：高 3.5 英寸（他顺手解释了一句，「我原以为有六英寸，那是底下垫了一叠白纸」），一共十捆，里面有 86-DOS 1.00 的内核、几个 PC-DOS 1.00 的开发快照、CHKDSK，还有汇编器自身的清单，以及他在微软负责的 BASIC 编译器运行库。

> **1981 年前后的一台 IBM PC，与它旁边摊开的打印清单**　微软开源博客配用的这张照片，标题是「80 年代初在 IBM PC 上做 DOS 开发是什么样子」，摄影 Rich Cini。画面中可以看到 IBM PC、连续纸清单、计算尺和计算器；屏幕上的绿色字符对应本册第六章要读的那份清单。（内嵌图见 HTML 版）

> **Tim Paterson（86-DOS 作者）**　Tim Paterson。1980 年他在西雅图电脑产品公司（Seattle Computer Products）写这个系统时，它叫 QDOS；改名 86-DOS 后，1981 年 4 月 28 日定版 1.00。照片来自清单仓库的公开页。（内嵌图见 HTML 版）

### 材料为什么有用
这批材料的价值不只在于年代早，更在于它把版本过程保留下来：同一内核在不同时间点的快照、记录文件差异的清单（diff：把两份文件逐行比较，只列出不同之处），以及 Paterson 写在纸边的批注，都还可以相互对照。微软把它称作「一个 Git 仓库的打印版提交历史」（a printed commit history of a Git repository）。这个比喻并非泛泛而谈：Git 中由程序计算的差异，在这里变成了打印出来的文本，有时旁边还留着手写说明。
这套软件后来进入了 IBM PC 的产品谱系：86-DOS 1.00 定版三个月后，IBM PC 出货，其操作系统称为 PC-DOS 1.00；微软随后继续发展同一血统的 MS-DOS。但在 1981 年 4 月 28 日这个时间点，眼前的仍是一份属于 Seattle Computer Products 的产品：源码文本约六万字节，整个内核集中在一个文件里。

### 本册的任务
这一册围绕一个问题展开：1981 年 4 月 28 日定版的 86-DOS 1.00，究竟是什么样子？回答这个问题时，需要把源码、快照、差异清单和手写批注分开使用。正文会依次检查三件事：哪些功能已经有实现，哪些仍然只是占位；哪些接口沿用了 CP/M 的约定，哪些内部结构是为 8086 和新的磁盘组织方式重新设计的；定版之后，系统又沿着什么方向变化。

### 材料清单与使用方法
本册引用的所有行号，都指向 Paterson-Listings 仓库里 `3_source_code/86-DOS_1.00/86DOS.ASM`（MIT 许可）。这个文件的第一行就是自我声明：「86-DOS High-performance operating system for the 8086 version 1.00 04/28/81」。读者想在本地复核，把仓库取下来、打开这个文件、按行号跳过去即可。
另有两个来源容易与本册混用，先说明白。一是同仓库的 `2_printed_files/`，那是打印原件的转录，行号带打印行编号（形如 `00520`），和定版源码的行号不是一套；二是网上流传的 86-DOS 0.11 社区逆向重建版（文件名后缀是 `.A86`），它比 1.00 更早，README 只说写于 1980 年、具体月份无据，重建者自己在 README 里写明「法律地位可能不明确」。本册只在需要对照时才提它，每次都标注，也不拿它的行号当基准。

> 图 0　四个时间点、两种许可状态、以及这叠纸为什么是一条时间线。（见 HTML 版内嵌图）

| 档位 | 事实 | 出处 | 复核方式 |
|---|---|---|---|
| 外部资料 | 2026-04-28 发布、45 周年、「打印版提交历史」的比喻、MIT 许可、清单捐给 Interim Computer Museum、考证团队名单 | 微软开源博客 Continuing the story of early DOS development（2026-04-28，作者 Stacey Haffner / Scott Hanselman） | 原文直引，链接见参考文献 [5] |
| 外部资料 | 清单高 3.5 英寸、十捆内容清点、5/5/81 时间戳口径、BASIC 运行库是他在微软期间的职责 | Tim_Paterson_24Apr2025_emails.txt（随仓库发布的四封往来邮件） | 邮件全文在引用基准仓库根目录 |
| 源码实证 | 基准文件第一行自证版本与日期 | 86DOS.ASM:1 | 直读 |


## 1 · 九个锚点

*文件开头先记录版本变化*

打开 86DOS.ASM，头十七行里没有一条指令。第 1 行声明身份，第 4 行到第 17 行是一份修订台账：Paterson 用注释写的版本记录，从 1980 年 12 月 29 日的 0.34 排到 1981 年 4 月 28 日的 1.00，九个锚点。开头第一行原文是全大写的命令「EVERY change must noted below!!」，每一处改动都必须记录在下面。这是这份 86-DOS 1.00 源码里能直接看到的最早一层版本记录。

> **86-DOS 内核清单的一页打印原件（6 月 16 日打印）**　这份清单的一页。页脚印着两行打印元数据，第二行是本册最要紧的：Created: 15-Jun-81 03:18:51　Printed: 16-Jun-81 15:17:10，与仓库 README 里 6 月 16 日快照的记录一致到分钟；秒位纸面为 :10、README 为 :17，相差 7 秒，原因不明。纸的下沿有一行手写批注：PC-DOS 1.0　NO DATE/TIME FUNCTIONS（PC-DOS 1.0，没有日期时间功能）。另一页上还有一条方向相反的批注，第六章会把两条并排看。（内嵌图见 HTML 版）

### 台账全文

```
; 0.34 12/29/80 General release, updating all past customers
; 0.42 02/25/81 32-byte directory entries added
; 0.56 03/23/81 Variable record and sector sizes
; 0.60 03/27/81 Ctrl-C exit changes, including register save on user stack
; 0.74 04/15/81 Recognize I/O devices with file names
; 0.75 04/17/81 Improve and correct buffer handling
; 0.76 04/23/81 Correct directory size when not 2^N entries
; 0.80 04/27/81 Add console input without echo, Functions 7 & 8
; 1.00 04/28/81 Renumber for general release
```

*86DOS.ASM:7–15，逐行转录，未作改写*
这九行可以当作一份简短的版本时间表。0.34 的说明是面向既有客户的通用发布；0.42 把目录项从 16 字节扩到 32 字节；0.56 允许记录大小和扇区大小变化；0.60 调整 Ctrl-C 的退出路径；0.74 让 I/O 设备可以用文件名表示；0.80 加入无回显的控制台输入。最后一行 1.00 只写着 `Renumber for general release`。这说明台账把 1.00 记作一次面向发布的重新编号，但不能据此断定源码没有其他变化。
0.80 条目还特别点名了「功能 7 和 8」。第四章会把这两个编号与 CP/M 的对应功能放在一起比较，因为同一个编号在两套系统中并不总是代表同一项服务。

### 台账与当事人邮件对得上
考证者曾问 Paterson：清单里的 5 月 5 日是否代表发布前最后一轮修 bug。Paterson 的回答是，这只是文件最后一次改动时留下的时间戳，并不对应某个发布或版本；他认为 5 月 5 日的文件应当与 4 月 28 日发布的 v1.00 非常接近，也可能只是上传到微软服务器的日期。因此，源码台账提供了 4 月 28 日的版本锚点，邮件则解释了为什么清单里还会出现 5 月 5 日。

| 档位 | 事实 | 出处 | 复核方式 |
|---|---|---|---|
| 源码实证 | 修订台账九个版本锚点，1.00 的说明是 Renumber for general release | 86DOS.ASM:7–15 | 直读；行号范围 :4–17 含标题行 |
| 外部资料 | 5/5/81 是文件时间戳、不对应发布；与 4/28 的关系 | Tim_Paterson_24Apr2025_emails.txt（第三、四封） | 邮件原文 |
| 源码实证 | 0.80 条目点名「功能 7 和 8」 | 86DOS.ASM:14 | 直读，第四章回看 |
| 外部资料 | 打印元数据 Created/Printed 两项与仓库 README 一致 | Paterson-Listings README「Bundle 04」条目 + 打印页原件扫描（bundle04 第 1 页） | 扫描件裁切放大后逐字核对，避免缩略图误读 |


## 2 · 一个文件的内核

*源码文件 3,622 行、60,124 字节，代码和数据住在一起*

86-DOS 1.00 的内核源码本身，集中在一个汇编文件里：86DOS.ASM。这份源码文件 3,622 行、60,124 字节，注意这是源码文本的大小，不是编译出来的内核大小（后者见第 8 章）。它没有被拆成多个源文件，也没有头文件；模块边界主要靠标号、分区注释和调用关系维持。模块化不等于多文件，这一点后面会反复看到。全文只有一处分区记号，在第 3206 行：`;***** DATA AREA *****`。这一行把文件划成两个区域：前 3,205 行是代码区（其中也包含注释、标号和定义），后 417 行是内核集中声明的可写数据区。

### 先说明行数口径
同一个文件用两把尺子量，会得到两个数：Python 按行切开数，是 3,622 行；Unix 的 `wc -l` 数换行符，得 3,621，因为这个文件的最后一行没有行尾换行符。本册统一用 3,622。这一处先讲明白，读者自己一量才不会有疑问。

### 六个区
自我介绍区（1–56 行）：版本声明、九个版本的台账、中断入口常量、功能号上限。文件虽小，开头先把自己的来历交代清楚。数据结构区（58–185 行）：文件控制块（FCB）的 32 字节布局、目录项的字段说明、磁盘参数块、BIOS 入口表、保存用户寄存器的位置表，还有一张行编辑器的按键表。第五章会回到这里。大门与走廊（186–301 行）：开机横幅、两条系统调用入口、保存九个寄存器和中断控制。第三章将详细说明入口路径。总机接线板（302–356 行）：DISPATCH 跳转表，共 42 个功能槽，其中 5 个仍是空壳。第四章会逐槽比较它们的语义。功能实现区（357–3205 行）：FAT 查表与改写、目录搜索、文件的开读写删、字符输入输出、Ctrl-C 处理、报错。DATA AREA（3206–3622 行）：当前盘号、扇区大小、FAT 计数、目录缓冲、内核自己的栈。
DATA AREA 有 417 行，约占整个文件的九分之一。当前盘号、扇区大小、FAT 状态、目录缓冲和内核栈等运行时状态，都在这里留下位置。这不是说内核只会写这一区域，而是说源码把主要的可变状态集中放在了这里；代码区负责执行，数据区负责保存执行过程中的状态。

### 单文件不是偷懒
现有 README 能直接证明的是构建流程：用 SCP ASM 汇编一个源文件，得到 Intel HEX 目标文件，再用 HEX2BIN 转成二进制；示例命令是 `A:ASM 86DOS`，随后执行 `A:HEX2BIN 86DOS`，汇编器最后报告 `Error Count = 0`。这条流程与单文件内核能够很好地配合，但源码和 README 没有直接说明「为什么采用单文件」。从源码本身看，模块边界主要依靠标号、数据区和调用关系来维持。

> 图 2　内核地图。第 3 章从蓝框的两条入口读起，第 4 章读绿框的接线板。（见 HTML 版内嵌图）

| 档位 | 事实 | 出处 | 复核方式 |
|---|---|---|---|
| 源码实证 | 全文唯一分区记号在第 3206 行，其后为 DATA AREA | 86DOS.ASM:3206 | vol1_map.py 分节标记扫描 |
| 数据实证 | 源码文件 3,622 行 / 60,124 字节 / 代码 3,205 行 + 数据 417 行 | vol1_map.py 输出 | splitlines 计数与实际文件大小；wc -l 少 1 的原因已说明 |
| 外部资料 | ASM + HEX2BIN 的两步构建流程 | Paterson-Listings README「Compiling/Assembling」一节 | README 原文 |


## 3 · 两个大门

*CALL 5 与 INT：一个内核，两套门铃*

从应用程序的公开调用界面看，86-DOS 提供两条进入内核的路径：传统的 `CALL 5`，以及通过中断进入的 `COMMAND` 入口。中断可以理解为 CPU 按约定跳到系统代码处理请求，处理完再返回原处。两条路径最后都进入 SAVREGS（第 219 行）之后的保存现场和分发流程，但可接受的功能号范围不同：CALL 5 的上限是 36（0–36，共 37 个），中断入口的上限是 41（0–41，共 42 个）。

### CALL 5 是什么
在 CP/M 中，程序通常把功能号放进 C 寄存器，再执行 `CALL 5`；地址 5 是 BDOS 的入口。许多 CP/M 程序把这个调用约定直接写进了机器码。如果 8086 上的新系统希望接住这批程序，就必须提供相同的入口位置和寄存器约定。至于入口之后如何实现，应用程序并不需要知道。

```
ENTRY:	;System call entry point and dispatcher
	POP	AX		;IP from the long call at 5
	POP	AX		;Segment from the long call at 5
	SEG	CS
	POP	[TEMP]		;IP from the CALL 5
	PUSHF			;Start re-ordering the stack
	DI
	PUSH	AX		;Save segment
	SEG	CS
	PUSH	[TEMP]		;Stack now ordered as if INT had been used
	CMP	CL,MAXCALL	;This entry point doesn't get as many calls
	JA	BADCALL
	MOV	AH,CL
```

*86DOS.ASM:206–218，注释原文点明 CALL 5，也点明这道门能点的功能少一些*
这段入口代码先处理 `CALL 5` 带来的栈布局：它取出远调用留下的段地址和偏移，再把栈整理成与中断入口相同的形状。随后，功能号从 C 寄存器移到 AH（`MOV AH,CL`），这样两条路径就可以共用后面的分发逻辑。`CMP CL,MAXCALL` 则把 CALL 5 的功能号限制在 36 以内。

### 两个上限，两种身份
文件头部并排放着两个常量：`MAXCALL` 等于 36，`MAXCOM` 等于 41（第 44–45 行）。CALL 5 检查前者，中断入口 `COMMAND` 检查后者。因此，37–41 号虽然出现在分发表中，却不属于 CALL 5 这条兼容入口；它们要通过更宽的中断入口才能到达。

### 进门先关中断
入口在重排栈时执行 `DI`（关中断，第 212 行），避免现场尚未整理好就被再次打断。栈切换完成后，代码在第 254 行执行 `EI`；返回阶段又在第 272 行暂时关中断，恢复保存的栈和寄存器，最后用 `IRET` 返回。文件头的 `DSKTEST` 开关（第 25–29 行）还说明，打开它后，磁盘 I/O 可以使用另一套栈，从而具备注释所说的可重入条件。默认值为 0 时，内核仍按共享栈工作；这正是入口需要先保护栈状态的原因。

| 档位 | 事实 | 出处 | 复核方式 |
|---|---|---|---|
| 源码实证 | CALL 5 入口重排栈、功能号从 CL 搬进 AH、上限 36 拦截 | 86DOS.ASM:206–218 | 直读；MAXCALL/MAXCOM 定义于 :44–45，COMMAND 入口的上限检查在 :199–201 |
| 源码实证 | 入口立即关中断；DSKTEST 双栈注释；出口九个寄存器 + IRET | 86DOS.ASM:212 / :25–29 / :271–300 | 直读 |
| 外部资料 | CALL 5 是 CP/M 时代系统调用入口的约定俗成 | CP/M 2 参考资料（seasip.info BDOS 汇总；classiccmp 邮件列表的 CP/M 2 分发表片段） | 该约定在 CP/M 文档中反复出现，见参考文献 [4] |


## 4 · 四十二个槽

*从 DISPATCH 表看 1.00 提供了哪些接口*

功能号进入 AH 后，分发器把它扩展成表内偏移，再通过 `DISPATCH` 表找到对应的处理例程。相关代码在第 256–270 行，表本身从第 302 行开始，共有 42 个表项。Paterson 用 `Standard Functions` 和 `Extended Functions` 两个注释区分标准功能与扩展功能；这张表因此清楚地展示了 1.00 对外提供了哪些编号。

### 四十二格里有什么
0 号终止程序、1 号读键盘、2 号写屏幕、3 号读带机、4 号送凿孔机、5 号送打印机，3、4 两号是纸带机与凿孔机的功能，1981 年已属文物，但兼容层不问过时不过时，只问对方有没有。接着是 9 号打印字符串、10 号读一行、11 号看键盘有没有按键；13 号复位磁盘系统、14 号选盘，15 到 23 号是文件操作：开、关、找、删、顺序读写、建、改名；26 号设置数据交换地址、27 号取 FAT 指针、33 到 36 号是随机读写与文件大小；37 号往后五格是扩展功能：置中断向量、置新基段、块读、块写、构造 FCB。

### 和 CP/M 的分发表逐格对账
「86-DOS 兼容 CP/M」仍然太笼统。本节只比较它与 CP/M 2 的系统调用槽位，不把这个数字扩展成对所有 CP/M 版本或所有程序行为的判断。本册使用留存的 CP/M 2 分发表片段和功能清单，与 86-DOS 的 `DISPATCH` 表逐格比较。比较的不是标号名字是否相同，而是同一个编号在两边是否代表同一项服务：例如都表示「返回当前盘」时，即使名字写成 `CURDRV` 或其他形式，也算作同一语义。
结果是：42 格里有 34 格对得上，编号相同，而且功能对象相同（名字可以不一样，比如 CP/M 叫「返回当前盘」，86-DOS 叫 `CURDRV`，是一件事）。4 格被征用：编号还在，里面换成了别的东西。2 格填空：CP/M 在那个编号上没有定义过东西，86-DOS 拿来用了。1 格同一个位置换了对象：27 号，两边都是「取地址」，但 CP/M 取的是分配图地址，86-DOS 取的是 FAT 指针，第五章详谈。1 格超出 CP/M 表的范围：41 号构造 FCB，是 86-DOS 自己加的。判定怎么定的：看的是功能对象是否相同，优先依据两边的调用约定与文档描述，名字不同不影响判定，也不做字符串匹配。这条判定协议写在 `vol1_cpm.py` 的头部，读者可以照它逐格复核，也可以不同意其中某一格的归类。

### 被征用的那几格，正是线索
7 号和 8 号：CP/M 在这里放的是「读/写设备重定向字节」，86-DOS 换成了无回显的键盘输入。回头对第一章那份台账，0.80 那行写的是「加上无回显控制台输入，功能 7 和 8」，台账里的「功能 7 和 8」，就是这两个被征用的编号。37 号：CP/M 的 `Reset Drive`，86-DOS 换成了置中断向量。40 号：CP/M 的「随机写并零填充」，换成了块写。
这些差异会影响实际兼容性：如果 CP/M 程序调用 7 号功能来读取 I/O 重定向字节，在 86-DOS 上得到的却是另一种输入操作。同时，34 个对齐槽仍然构成了最主要的接口骨架，开文件、读写记录和删除文件等常用服务都在其中。不过，槽位和功能对象对齐，并不能保证某个具体 CP/M 程序直接运行；BIOS 假设、内存布局、参数格式和错误处理方式仍可能不同。因此，这份对账衡量的是接口层的对应关系，不是完整的程序兼容率。

### 空壳标号与功能槽

```
VERSION:
GETIO:
SETIO:
WRTPROT:
GETRDONLY:
SETATTRIB:
USERCODE:
	MOV	AL,0
	RET
```

*86DOS.ASM:348–356，七个标号共用同一个函数体，全文照录*
42 格里有 5 格的函数体只有两行：把 AL 清零，然后返回。上面七个标号中，`GETIO` 与 `SETIO` 没有被分发表引用，真正占着功能槽的是另外 5 个，其中包括 12 号的 `VERSION`。在 1.00 里，「请报出你的版本号」得到的答案是 0。
从行为看，`VERSION` 是一个兼容占位：编号先保留下来，函数体只返回 0。第五、六章会追踪这些空壳的去向，有的后来长出了新实现，有的则被删除。

> 图 4　上：与 CP/M 的对账结果；下：对齐槽数在四个时间点上的走向。（见 HTML 版内嵌图）

| 档位 | 事实 | 出处 | 复核方式 |
|---|---|---|---|
| 源码实证 | DISPATCH 跳转表 42 格，注释分为标准与扩展两段 | 86DOS.ASM:302–346 | vol1_evolve.py 提取 + 直读 |
| 源码实证 | 七个标号共用 MOV AL,0 / RET（五格在分发表内），其中包含 12 号报版本号 | 86DOS.ASM:348–356 | 直读 |
| 外部资料 | CP/M 2 功能分发表的编号与名称 | seasip.info BDOS Function Summary；classiccmp.org 邮件列表留存的 CP/M 2 分发表源码片段 | 两份来源的编号与名称一致；本册只用其编号与名称，不用其代码 |
| 数据实证 | 34 对齐 / 4 征用 / 2 填空 / 1 同位换对象 / 1 新增；对齐槽数 34→33→33→32 | vol1_cpm.py 输出 | 判定口径写在脚本头部：以「功能对象是否相同」为准，名字不同不影响判定 |


## 5 · 盘上的新世界

*接口层沿用 CP/M，文件系统内部另起炉灶*

上一章有一个槽位没有展开：27 号功能在 CP/M 中返回分配图地址，在 86-DOS 1.00 中则返回 FAT 指针。这个差别正好把两套文件系统的内部组织区分开：CP/M 用静态位图记录盘块是否占用，86-DOS 用 FAT 把一个文件的簇串成链。应用程序看到的调用编号仍然相近，盘上的数据结构已经换了一套。

### FCB：36 字节的结构
程序与内核之间关于文件的部分状态，放在 FCB（文件控制块）里。1.00 的主体区域占 32 字节，后面还有 1 字节的当前记录号 `NR` 和 3 字节的随机记录号 `RR`，合计 36 字节。FCB 的前部布局沿用了 CP/M 约定，但 `RECSIZ`、`FDATE` 和 `FIRCLUS` 等字段已经体现出 86-DOS 自己的文件系统设计。

```
	ORG	0
	DS	12		;Drive code and name
EXTENT:	DS	2
RECSIZ:	DS	2		;Size of record (user settable)
FILSIZ:	DS	4		;Size of file in bytes
FDATE:	DS	2		;Date of last writing
FILDIRBLK:DS	2		;Location in directory
FIRCLUS:DS	2		;First cluster of file
LSTCLUS:DS	2		;Last cluster accessed
CLUSPOS:DS	2		;Position of last cluster accessed
DIRTYFIL:DS	1		;File has been written to if <>0
	ORG	32
NR:	DS	1		;Next record
RR:	DS	3		;Random record
```

*86DOS.ASM:60–73，FCB 布局定义，全文照录*
前 12 字节保存盘号和 8.3 文件名，这是 CP/M 程序熟悉的布局。后面的字段则加入了 86-DOS 的信息：`RECSIZ` 允许记录大小变化，`FDATE` 保存最后写入日期，`FIRCLUS` 保存文件的首簇号。`EXTENT` 这个旧名称仍在，`FIRCLUS` 这样的新字段也已经出现；同一个结构里可以同时看到兼容接口和新的盘上组织方式。

### 十二位一格，两个格子挤三个字节
FAT 是文件分配表。源码注释把每个表项称为一个 allocation unit；为方便叙述，本文沿用常见说法，把它称作簇。表项中的值通常是下一个簇的编号，文件因此可以由多个簇串成一条链。1.00 的每个表项占 12 位，两个表项打包进三个字节。这会让读取和写入都多一步位操作，源码注释随后给出了完整算法：

```
; The File Allocation Table uses a 12-bit entry for each allocation unit on
; the disk. These entries are packed, two for every three bytes. The contents
; of entry number N is found by 1) multiplying N by 1.5; 2) adding the result
; to the base address of the Allocation Table; 3) fetching the 16-bit word at
; this address; 4) If N was odd (so that N*1.5 was not an integer), shift the
; word right four bits; 5) mask to 12 bits (AND with 0FFF hex). Entry number
```

*86DOS.ASM:87–92，六行整行转录，步骤一到五是 Paterson 自己编号的；:92 行尾的 Entry number 接下文（EOF 陷阱说明），与本章无关，未转录*
最容易卡住的是第一步：为什么要乘 1.5？因为每个表项占 12 位，也就是 1.5 字节。第 N 个表项从第 1.5N 个字节开始；N 为奇数时，起点落在一个字节的高、低半区之间，所以要把取出的 16 位值右移四位。源码用移位和加法代替乘法，最后用 `AND DI,0FFFH` 保留 12 位。这种两个 12 位表项共用三个字节的布局，后来通常被称为 FAT12。

### 为什么第一个簇叫「簇 2」
第一次读 FAT，很多人都会在这里停下来：0 号和 1 号表项做什么用？为什么第一个能存数据的簇不叫「簇 0」，而叫「簇 2」？Paterson 在注释里给了答案。0 号表项既是系统用来触发文件结束处理的陷阱，也在 `SMALLDIR` 打开时充当目录项大小标志；1 号表项保留。真正分配给文件的表项从 2 号开始，所以第一个可用簇就叫簇 2。注释特意写道：`even though it is the first, is called cluster 2`。
0 表示该簇未分配，普通值表示下一簇的编号。源码注释写的是「大于 `0FF8H` 为文件结束标记」；实际遍历代码在比较 `0FF8H` 后使用 `JAE`（见 `86DOS.ASM:1496`），所以从实现看，`0xFF8` 及以上都被当作结束标记。这里把注释和执行路径并列写出，避免混淆两种边界口径。

### 目录项里的日期，是被位运算塞进去的
1.00 的目录项有 32 字节，偏移 24（从 0 起算，即第 25 个字节）开始的两个字节保存写入日期。源码注释把位分配写得很清楚：bit 0–4 是日，bit 5–8 是月，bit 9–15 是 year-1980（86DOS.ASM:83）。也就是说，年份存成相对于 1980 年的偏移，而不是直接存四位数；7 个 bit 在表示层面最多可以覆盖到 2107 年。把多个小字段塞进同一组二进制位，叫作位场。

### SMALLDIR 为什么存在
文件头第 19–22 行定义了 `SMALLDIR`。注释说，打开它可以同时接受旧的 16 字节目录项。0.42 的修订台账记录了目录项从 16 字节扩到 32 字节；目录项变宽后，目录占用的扇区和数据区起点都会变化。因此源码在开关打开时保留了两套磁盘参数：一套对应 16 字节目录，一套对应 32 字节目录（第 118–123 行）。1.00 的开关值为 1，说明这两种格式在编译时都被纳入考虑；它后来何时退出，见第六章。

### 接口层与盘上结构
把前面的证据放在一起，可以看到两层设计。在程序可见的边界上，86-DOS 保留了 `CALL 5`、功能号、FCB 前部布局和 `$` 结束字符串等 CP/M 约定；在文件系统内部，它改用了 FAT 链、32 字节目录项和可变记录大小。因此，兼容主要发生在接口层，文件组织则按照 8086 系统自己的需要展开。

> 图 5　上：12 位的打包方式与注释里的五步；中：一次查表的完整路径；下：把这五步写成可执行模型后跑出来的结果。（见 HTML 版内嵌图）

| 档位 | 事实 | 出处 | 复核方式 |
|---|---|---|---|
| 源码实证 | FCB 主体 32 字节 + NR/RR 共 36 字节；EXTENT 与 FIRCLUS 两种命名并存 | 86DOS.ASM:60–73（EXTENT 在 :62，FIRCLUS 在 :67） | 直读 |
| 源码实证 | FAT 12 位条目、查表五步、簇 0/1 用途、第一个可用簇叫簇 2；实现按 0xFF8 及以上判断结束 | 86DOS.ASM:87–98、1496 | 注释原文与 `CMP 0FF8H`/`JAE` 实现对照；打包实现见 :369–432 的 UNPACK/PACK |
| 源码实证 | 目录项偏移 24（第 25 个字节）起的两个字节存日期，位场划分 | 86DOS.ASM:83 | 直读；源码位置按从 0 起算的目录项偏移解释 |
| 源码实证 | SMALLDIR 开关与两套磁盘参数（16/32 字节目录各一套） | 86DOS.ASM:19–22 与 :118–123 | 直读 |
| 数据实证 | FAT12 查表算法的可执行验证：注释五步与源码指令模拟对 4,096 个簇号逐条一致；写后读回 1,000 次零失败且未破坏邻格；按注释原文写入的簇链 2→3→5 能原样读回 | vol1_fat_check.py 输出 | 纯 Python、无外部依赖、可复跑；验证范围只覆盖 FAT12 打包算法这一条路径，不汇编源码也不启动系统 |


## 6 · 定版之后

*四个快照，看一个系统怎样开始变形*

1.00 定版之后，同一条材料链上还留下了三个可比较的时间点：6 月 16 日快照、7 月 7 日快照，以及后来的 MS-DOS 1.25 源码。把四个文件放在一起，先比较行数和字节数，再比较分发表和开关，可以看到系统在定版之后增加了哪些能力，又放弃了哪些兼容路径。

> **7 月 8 日打印的清单页，下沿有手写批注**　纸下沿的手写批注：DATE/TIME FUNCTIONS ADDED FOR IBM CLOCK。页脚的打印元数据写着 Date 08-Jul-81 13:49:36、Created 07-Jul-81 17:06:59、Printed 08-Jul-81 13:49:52。这一页与上一页出自同一叠连续纸，同一种绿白横纹。（内嵌图见 HTML 版）

> **IBM PC 屏幕上显示的清单内容，与旁边的打印原件对照**　屏幕上是这份源码的头部：版本行、修订台账、以及 `EXCESS: EQU 511　;Make 86-DOS artificially larger` 与 `SMALLDIR: EQU 0` 两行，这正是 7 月 7 日快照的两个特征值，也就是说屏幕里跑的是本册讲的第三个点。右边摆着的打印件，就是屏幕上这份内容的纸质版。（内嵌图见 HTML 版）

### 先看四个数
源码文件的大小依次是：1.00 为 3,622 行、60,124 字节；6 月 16 日快照为 3,888 行、66,985 字节；7 月 7 日快照为 3,901 行、67,261 字节；MS-DOS 1.25 为 4,031 行、114,253 字节。从 1.00 到 1.25，行数增加 409 行；从 4 月 28 日到 6 月 16 日增加 266 行，从 6 月 16 日到 7 月 7 日增加 13 行，之后再增加 130 行。这些数字只能说明源码体量的变化，不能直接换算成开发时间或代码工作量；「7 月阶段更接近收尾」只是根据体量和功能槽变化作出的概括，不是台账原话。

### 功能槽的变化，比行数更说明问题
跳转表的格子数：42 → 46 → 46 → 47。6 月 16 日那一跳加了四格，全部是日期与时间：取日期、置日期、取时间、置时间。与此同时，12 号「报版本号」那一格里的功能被删掉了，从 6 月 16 日起，内核里再也没有 `VERSION` 这个标号（标号是给代码位置起的名字，方便在别处按名字引用它），腾出来的 12 号位置给了「清键盘缓冲」（`FLUSHKB`）。一个 CP/M 定义过、86-DOS 1.00 也占着位的功能，就这样从表里消失了。
到了 1.25，槽位变化继续：25 号功能由 `CURDRV` 改名为 `GETDRV`；28 号由 `WRTPROT` 换成 `GETFATPTDL`，用于返回 FAT 和磁盘参数表相关信息；末尾新增 46 号 `VERIFY`，用于写盘后的校验。1.25 的修订台账在 1.20 条目中还记下了「separate FAT from DPT; Kill SMALLDIR」。
四个时间点之间，和 CP/M 语义对齐的槽位从 34 个变成 32 个。不过变化并不是每个阶段都发生：6/16 和 7/7 的分发表相同，主要变化集中在 1.00 到 6/16，以及 7/7 到 1.25。可以确认的是，常用接口被保留的同时，部分旧语义被替换，新的系统需求被加入。

### 两条批注与源码时间线
日期时间功能在清单上留下了两条批注，但两条批注对应的材料层次不同。
第一条出现在 6 月 16 日打印的 1.00 基线清单页下沿：「PC-DOS 1.0　NO DATE/TIME FUNCTIONS」。第二条出现在 7 月 8 日打印、文件创建于 7 月 7 日的快照页下沿：「DATE/TIME FUNCTIONS ADDED FOR IBM CLOCK」。
源码本身显示，1.00 的 42 个槽里没有日期时间接口，而 6 月 16 日快照已经新增 42–45 四个入口，并且包含完整的取值和设置例程。可以确定的是：接口在 1.00 基线之后、6/16 快照中已经出现，7 月的批注又把这项工作明确联系到 IBM 时钟。至于第一条批注究竟是对哪一份 PC-DOS 成品的回看、两条字迹是否由同一人写下，现有页面没有提供更完整的说明；本文不再从批注之外推断更多。

### 两个开关的命运
`SMALLDIR` 的状态可以沿四个时间点追踪：1.00 为 1，6 月 16 日和 7 月 7 日为 0；到 MS-DOS 1.25，定义已经消失，修订台账在 1.20 条目中写着 `Kill SMALLDIR`（1981 年 12 月 31 日）。从保留旧目录格式，到关闭编译期开关，再到删除相关定义，这条线清楚显示了一个兼容选项如何退出。
另一个开关只出现在 7 月 7 日快照中：`EXCESS` 的值为 511，注释是 `Make 86-DOS artificially larger`。它在数据区预留 511 字节的填充，但现有注释没有说明为什么要这样做。可以确认的只有三点：它确实存在，只出现在 7 月 7 日快照中，而且作者明确把它标为人为增大系统长度的操作。

### 这条时间链能回答什么
这组材料同时提供了四个源码时间点和一份版本间差异清单。因此，哪些功能在某个时间点已经出现、哪些开关后来被关闭，可以回到具体文件、行号和脚本逐项核对；至于改动动机，仍要依靠批注、当事人材料或明确标注为推断。

> 图 6　四个时间点的行数、功能槽与两个开关的走向，数字全部由脚本算出。（见 HTML 版内嵌图）

| 档位 | 事实 | 出处 | 复核方式 |
|---|---|---|---|
| 数据实证 | 四个时间点的行数、字节数、功能槽数、开关取值、空壳功能的去向、对齐槽数走向 | vol1_evolve.py 与 vol1_cpm.py 输出 | splitlines 计数；DISPATCH 表由脚本提取；四点源码均在 3_source_code 下 |
| 源码实证 | 6/16 起 VERSION 标号消失、12 号槽改为 FLUSHKB；新增四格日期时间功能且实现完整 | PC-DOS_1.00_dev/86DOS_1981-06-16.ASM（GETDATE 实现见该文件 :3373–3384） | 脚本比对 + 直读 |
| 源码实证 | 1.20 台账「Add time to FCB; separate FAT from DPT; Kill SMALLDIR」 | MS-DOS_1.25/MSDOS.ASM:23 附近的修订台账（共 20 个锚点） | 直读 |
| 源码实证 | EXCESS 只在 7/7 快照出现，值 511，注释 Make 86-DOS artificially larger | PC-DOS_1.00_dev/86DOS_1981-07-07.ASM:19 | 全树 grep 确认四个点的分布 |
| 外部资料 | 两条手写批注的原文与所在页的打印日期 | archive.org item paterson_listings 的 images/listings.jpg 与 bundle04 第 1 页扫描件 | 扫描件裁切放大后逐字确认；页脚元数据与仓库 README 交叉核对 |


## 7 · 开机那一刻

*横幅上写着一家西雅图的小公司*

内核里有一段开机时要打印的文本，位于第 186–193 行。它包含版本号和一行版权声明：「Copyright 1980,81 Seattle Computer Products, Inc.」。对 1981 年 4 月 28 日的 1.00 源码来说，屏幕上出现的是 Seattle Computer Products 的名称，而不是微软。

### 横幅与它的开关

```
HEADER:	DB	13,10,"86-DOS version 1.00"

	IF	DSKTEST
	DB	"D"
	ENDIF

	DB	13,10
	DB	"Copyright 1980,81 Seattle Computer Products, Inc.",13,10,"$"
```

*86DOS.ASM:186–193，全文照录*
开头的 `13,10` 是回车和换行的字符编码；末尾的 `$` 则是这套打印约定使用的字符串终止符，打印例程遇到它就停止。`DSKTEST` 打开时，版本字符串后还会插入一个 `D`；这说明调试版本会在启动横幅上留下可识别的标记。

### 内建的行编辑器
第 170–184 行定义了 `ESCTAB`，其中有十组两字母控制序列，分别对应复制或跳过字符、复制或跳过到指定字符、复制整行、删除整行、重新编辑、退格，以及进入和退出插入模式。10 号功能读取一行时，行编辑器可以利用这些序列修改输入，错误处理因此由系统接口的一部分来承担，而不必由每个上层程序各自实现。

### 这一刻在历史里的位置
按材料上的日期排列，4 月 28 日是 1.00 定版日；5 月 5 日是 Paterson 解释过的文件时间戳，可能对应上传日期；6 月 16 日和 7 月 8 日分别是两批清单的打印日期，后一批文件创建于 7 月 7 日。8 月 IBM PC 出货时，使用的是由 86-DOS 演化而来的 PC-DOS 1.00。这些日期属于不同文件和清单，不能理解为同一个 `86DOS.ASM` 连续保存到了 8 月。材料还延伸到 1982 年；Paterson 当时已经在微软，清单中的后续部分包括他负责的 BASIC 编译器运行库。
本册停在 1.00 定版这个节点：源码文件约六万字节，内核集中在一个文件里，版权声明写着 Seattle Computer Products。IBM 的适配、微软取得相关权利之后的演化，留给后续各册。

| 档位 | 事实 | 出处 | 复核方式 |
|---|---|---|---|
| 源码实证 | 开机横幅两行文字与 SCP 版权声明；DSKTEST 调试字母；$ 结束符 | 86DOS.ASM:186–193 | 直读 |
| 源码实证 | 内建行编辑器的十项命令表（两字母指令） | 86DOS.ASM:170–184 | 直读；表长由 :184 的 ESCTABLEN 自行计算 |
| 外部资料 | 清单时间线（4/28 定版、5 月上传、6–7 月快照、8 月 IBM PC）、Paterson 在微软负责 BASIC 运行库 | Tim_Paterson_24Apr2025_emails.txt + 微软开源博客 | 邮件与博客原文 |


## 8 · 交房图纸

*结论，以及材料明确无法回答的问题*

回到系列的问题：一个据称七周写出来的系统，怎么长成统治 PC 十几年的 MS-DOS。这一册只回答它的前半句，而且只是前半句里的一小段：把时间停在 1981 年 4 月 28 日，看它当时是什么样子。读完之后，那句话本身也需要修正一下。

### 源码证实了这五件事
从源码和逐槽对账可以得到五个较稳妥的结论。第一，程序可见的接口明显沿用了 CP/M 约定：`CALL 5`、与 CP/M 2 对齐的 34 个功能槽、36 字节 FCB，以及以 `$` 结尾的字符串都保留了下来。第二，文件系统内部采用了另一套组织方式：FAT 链、32 字节目录项、可变记录大小和位打包日期字段。第三，置中断向量、置新基段等扩展功能，说明 8086 的原生需求已经进入接口。第四，版本台账、调试开关、分开的调用上限和入口处的栈保护，都是源码中可以直接看到的工程安排。第五，兼容并非整张表原样保留：4 个槽被判为征用，另有 1 个槽与 CP/M 同位但更换了对象，分发表内还有 5 个空壳；在后续时间点上，对齐槽数量从 34 个降到 32 个。

### 源码没有说的事
「七周」这个说法，现有材料既不能证实，也不能证伪。清单里最早的版本锚点是 0.34，日期为 1980 年 12 月 29 日；更早的 QDOS 阶段没有出现在这批纸面材料中。材料的边界到哪里，结论就到哪里。FAT 格式更早的来历也不在本册。1.00 的注释完整描述了它的布局和算法，却没有说明这套设计从哪一版开始形成。版权行能证明当时的权利归属写着 Seattle Computer Products，不能单凭代码推出公司之间的交易、渠道或价格。6 月和 7 月的快照已经进入 PC-DOS 开发阶段，但要判断哪些改动专门服务于 IBM PC，还需要逐条读完两个快照之间的 1,840 行差异清单，那是第二册的工作。

### 把本册的证据等级说清
本册的证据分成四类：源码实证，指向文件和行号；数据实证，由 `_evidence/vol1/` 下脚本计算；外部资料，包括清单扫描、官方说明和 CP/M 参考资料；作者推导则明确标注为综合判断。`vol1_fat_check.py` 属于数据实证：它用 Python 模拟并检查 FAT12 的打包算法，并没有启动 86-DOS。至于把源码汇编、写入现代 IBM PC 适配复刻盘并在 DOSBox-X 中启动，那是公开项目 phyous/DOS-1.0 的执行路径，使用的盘来自 TheBrokenPipe 的现代复刻工程，不是 1981 年的历史发行物；完整的执行证据和限制见第四册。

### 后续的两条线索
同一棵素材树里还保存着 0.11 社区重建源码：内核 2,259 行，比 1.00 少 1,363 行。它可以作为早期状态的对照，但要逐行说明 0.11 到 1.00 增加了什么，还需要另写比较脚本；现有材料没有为这两个版本提供现成的差异清单。
另一条线索是 `86DOS.DIF`。它列出了 1.00 与 6 月 16 日快照之间的改动：按本册口径共 1,840 行，其中含 30 个打印换页符；按 `wc -l` 统计则是 1,810 行。文件头明确写着两个比较对象：`86DOS.ASM created: 1056 05-MAY-1981` 与`86DOS.A86 created: 1425 16-JUNE-1981`。第二册会把这份清单从总量拆到具体改动，讨论哪些变化是保留、替换、新增或移除。

| 档位 | 事实 | 出处 | 复核方式 |
|---|---|---|---|
| 作者推导 | 五条结论（接口 CP/M 化 / 存储 FAT 化 / 原生层起步 / 工程习惯老练 / 兼容有选择） | 由第 3–6 章各证据条目合成 | 综合判断；分项证据见对应章节 |
| 作者推导 | 0.11 内核 2,259 行、1.00 与其相差 1,363 行；86DOS.DIF 共 1,840 行（splitlines 口径，含 30 个换页符） | vol1_evolve.py 输出 + 差异清单文件行数统计 | splitlines 口径；0.11 为社区逆向重建，法律灰色，已声明只作对照 |


## 术语速览
- **打印清单（listing）**　汇编器把源码打印在连续纸上时，每行带上行号、地址和注释的产物。当年程序员就在纸上读代码、改代码。
- **FCB**　文件控制块。程序与内核之间关于一个文件的 36 字节合同：主体 32 字节，另有 1 字节记录号和 3 字节随机记录号。CP/M 提供了这套约定，DOS 沿用并扩展。
- **FAT**　文件分配表。盘上记录文件的下一簇在哪儿的链式结构，是 DOS 文件系统的基础。
- **簇（cluster）**　盘上分配空间的最小单位。FAT 的每个表项记录一个簇的下一站。
- **FAT12**　每个表项 12 位的 FAT，两个表项打包进三个字节。后来的 DOS 文献通常把这种格式称为 FAT12。
- **目录项**　目录里描述文件的一条记录。1.00 的目录项为 32 字节，包含文件名、首簇号、文件大小和写入日期。
- **位场（bit field）**　把几个小数字塞进同一段二进制位里。日期字段就是按日 5 位、月 4 位、年 7 位分配。
- **BDOS / CALL 5**　CP/M 的操作系统核心及其调用入口。程序把功能号放进寄存器，再执行 CALL 5 请求服务。
- **INT 指令**　8086 的软件中断指令。86-DOS 的另一条系统调用入口通过中断向量进入内核。
- **跳转表（dispatch table）**　一张地址清单。功能号乘二得到表内偏移，跳到对应表项就能找到处理例程。
- **空壳（stub）**　占着功能号、但实现只返回默认值的函数。1.00 的分发表内有 5 个空壳槽，共享 MOV AL,0 / RET。
- **寄存器**　CPU 内部用于暂存数字的小型存储单元，各有固定名称。程序与系统之间通常通过寄存器传参。
- **中断**　送给 CPU 的控制信号。CPU 按向量跳到系统代码，处理完再返回原处。
- **可重入**　代码执行到一半被打断后，仍能安全地再次进入。1.00 默认使用共享栈，因此入口先关中断；测试开关才会启用另一套栈。
- **快照（snapshot）**　某个时间点保存下来的整套源码。1.00 之后，本系列还使用了 6/16 和 7/7 两份快照。
- **修订台账**　源码文件开头的版本记录。86-DOS 的台账从 0.34 排到 1.00，每行写日期和改动摘要。
- **Provenance（来历链）**　证据从保存、扫描、转录到发布的来源链。本册指 Paterson 保存清单、团队扫描转录和公开发布过程。
- **86-DOS / PC-DOS / MS-DOS**　同一软件血统在不同产品和发行关系中的名称：SCP 时代称 86-DOS，IBM PC 版本称 PC-DOS，微软面向其他厂商销售的版本称 MS-DOS。


## 参考文献
- [1]（SRC）**Paterson-Listings 仓库：3_source_code/（引用基准树）** — Tim Paterson 提供原始清单；Yufeng Gao、Rich Cini 等考证团队转录；微软 2026-04-28 以 MIT 许可发布。github.com/DOS-History/Paterson-Listings。其中 86-DOS_1.00/86DOS.ASM 是本册行号基准（第一行自证 version 1.00 04/28/81）；PC-DOS_1.00_dev/ 下是 6/16 与 7/7 两个快照；MS-DOS_1.25/MSDOS.ASM 是链的末端。 用途：全文所有源码实证与四个时间点的脚本比对。
- [2]（SRC）**同仓库 2_printed_files/：打印原件转录（含打印行号）** — 同上团队；转录自 Paterson 的连续纸清单。bundle_02（7/8 打印的内核快照）、bundle_04（6/16 打印的内核快照）、bundle_08（86DOS.DIF，版本间差异，splitlines 口径 1,840 行）等。文件行号带打印行编号，与定版源码不是一套。 用途：本册第 6 章快照链的对照，以及第二册使用的差异清单素材。
- [3]（DOC）**Tim Paterson 与 Yufeng Gao / Richard Cini 往来邮件（2025-04-24）** — 随 Paterson-Listings 仓库发布的誊本（Tim_Paterson_24Apr2025_emails.txt）。四封往来。Paterson 亲述清单堆高 3.5 英寸、十捆内容清点、5/5/81 只是文件时间戳不对应发布、BASIC 编译器运行库属他在微软的职责范围。 用途：第 0 章的证据形态、第 1 章的 5/5 口径、第 7 章的历史位置。
- [4]（DOC）**CP/M 2 功能分发表与功能号清单** — seasip.info「BDOS Function Summary」（编译者 Jim Lopushinsky）；classiccmp.org 邮件列表留存的 CP/M 2 分发表源码片段。两份来源给出的 CP/M 2 功能编号与名称一致。邮件列表那份片段即 CP/M 2 的 `DISTBL` 分发表，格式与 86-DOS 的 DISPATCH 表同构。本册只用其编号与名称，不用其代码。 用途：第 4 章逐槽对账的外部依据。
- [5]（WEB）**微软开源博客：Continuing the story of early DOS development（2026-04-28）** — Stacey Haffner（微软 OSPO 主任）、Scott Hanselman。opensource.microsoft.com/blog/2026/04/28/...。发布说明：45 周年、「迄今为止发现的最早 DOS 源码」、「可以把它看成一个 Git 仓库的打印版提交历史」、考证团队名单、MIT 许可、清单捐给 Interim Computer Museum。 用途：第 0 章的发布事实与官方比喻。
- [6]（DOC）**打印清单原件扫描（archive.org item: paterson_listings）** — 微软发布；扫描件由考证团队制作，其中工位照片摄影 Rich Cini。archive.org/details/paterson_listings。含十捆清单的 PDF/PNG 扫描与四张现场照片。本册引用的三处：6/16 内核清单页（页脚元数据 + 手写批注）、7/8 打印页（手写批注，文件创建于 7/7）、以及 1981 年工位照片。 用途：第 0、1、6 章的影像证据；手写批注的原文均经裁切放大逐字确认。
- [7]（SRC）**MS-DOS 官方源码仓库（1.25 / 2.0 / 4.0）** — Microsoft，MIT 许可。github.com/microsoft/MS-DOS。本册只用 v1.25 的 MSDOS.ASM 作为快照链末端，其修订台账含 20 个版本锚点，其中 1.20 一行写着 Kill SMALLDIR。 用途：第 6 章的链末端与 SMALLDIR 的最终命运。
- [8]（SRC）**86-DOS 0.11 源码重建（社区逆向工程）** — DOS-History 社区；README 自述为 reverse engineering，「法律地位可能不明确」。github.com/DOS-History/86-DOS-0.11。重建后可汇编回 SCP 出厂二进制。本册只在第 8 章把它作为系列后续对照，引用其行数（2,259 行，脚本实算）。 用途：仅作旁证，不作本册行号基准。法律灰色状态已在正文声明。
- [9]（WEB）**phyous/DOS-1.0：从 Paterson 的源码构建并启动 86-DOS 1.00** — phyous（项目作者）；汇编器来自 pts/pts-86-dos-reconstruction。github.com/phyous/DOS-1.0。用 `asm244l`（对 SCP ASM v2.44 的重新实现）把 86DOS.ASM 汇编成 5,446 字节的内核，并将与现代 IBM PC 适配盘的 49 字节级差异写回后在 DOSBox-X 中启动；仓库提供 build.sh 与 run.sh。这里的构建项目是现代复现流程，其中使用的 IBM PC 适配盘来自 TheBrokenPipe/86-DOS_PCAdaptation，不是 1981 年历史发行物。 用途：第 8 章：编译出的内核大小；完整的执行证据和限制见第四册。
