Byte-by-byte loader diff: 2.20 vs 2.23 differ in 74% of bytes
Detail for Part 9 — Every Difference: A Complete Inventory.
For the byte-level inventory in Part 9, I needed a clean byte-by-byte diff of the two loaders. Both are 3072 bytes (12 sectors of track 0/1) at Apple $1000-$1BFF. Here’s the diff structure:
Total runs: 40
Total bytes differing: 2280 (74.2% of 3072)
The runs by Apple address (offset from $1000):
$1060 160 bytes - includes copyright string + slot scanner (with the
11-byte Pascal 1.1 detection branch INSIDE this run)
$1200 51 bytes - early loader code (disk-prep state)
$1234 90 bytes
$128F 11 bytes
$129B 166 bytes - slot-handling code
$1342 36 bytes
$1367 1048 bytes - MASSIVE: most of the loader's middle, including
LOAD_CPM setup ($1407: LDA #$1D), post-load page
copies, install-fragment staging
$1782 49 bytes
$17B4 76 bytes
$1806 12 bytes
$1813 1 byte
$1815 3 bytes
$1819 2 bytes
$181C 1 byte
$181E 39 bytes
$1846 10 bytes
$1851 10 bytes
$185C 18 bytes
$186F 1 byte
$1871 31 bytes
$1891 1 byte
$1893 44 bytes
$18C0 18 bytes
$18D3 49 bytes
$1905 1 byte
$1907 85 bytes - boot-finalization code
$195D 163 bytes
$1B4A 76 bytes - constants/data tables
$1BA6 2 bytes
$1BB8 4 bytes
$1BBE 1 byte
$1BC7 2 bytes
$1BCD 2 bytes
$1BD0 2 bytes
$1BD5 7 bytes
$1BDF 1 byte
$1BE1 1 byte
$1BE3 4 bytes
$1BE9 1 byte
$1BEB 1 byte
The largest run is the 1048-byte block at $1367. That’s 34% of the entire loader changed in a single contiguous region. It contains the LOAD_CPM call site at $1407+ in 2.23 (LDA #$1D for 29 sectors, then JMP $BBE9).
Update (2026-04-29): The earlier draft of this paragraph claimed both versions have the same bytes at
$1407. They don’t. 2.20 has different code at that offset, calls a different LOAD_CPM-equivalent address ($0E10vs$BBEB), and reads 28 sectors (vs 29). Both versions have two LOAD_CPM calls (the dual-load mechanism is shared), but the addresses, parameters, and code shape around them all differ. See the 2.20-also-has-second-load devlog for the corrected picture.
Notable inside the diffs:
-
$1060run starts with a copyright string in 2.23:COPYRIGHT (C) 1982 MICROSOFT - CPfollowed by null bytes. 2.20 has$FFfiller in the same range. This explains ~35 bytes of the 160-byte run; the rest of the run is the slot-scanner code (where the 11-byte Pascal 1.1 detection lives) plus small offset-shifts. -
The 11-byte Pascal 1.1 detection branch (
E0 04 D0 0A A0 0B B1 3C C9 01 D0 02plusA2 06) is inside the$1060run. So the visible Videx fix is one specific 13-byte pattern in a 160-byte run that’s in turn one of 40 differing runs. -
The slot-scanner outer loop (the part that loops X = 4, 3, 2, 1 over the 4 signature entries) is functionally identical between versions, but it’s repositioned — 2.20 has it at
$1097-$10D6, 2.23 has it at$1099-$10E5. The shift comes from the inserted Pascal 1.1 branch and other code rearrangements in the surrounding region. -
The
$190785-byte run is the boot-finalization code I traced earlier. It contains the secondJSR $BBEB, the page copies that put CCP+BDOS in its final position, and theJMP $03D2that triggers the SoftCard CPU switch. The fact that this whole region differs by 85 bytes means the 2.20 version of the boot finalization is structurally different — something to disassemble next, since that may inform the open question of how the SoftCard switch is triggered (different mechanism in 2.20 vs 2.23 perhaps). -
The
$195D163-byte run at the end of the active loader code holds string tables and constants. 2.23 has Microsoft’s release-line strings; 2.20 has different string content (including the boot diagnostic messages “DC SOMETHING” or whatever they were in 1980). -
The trailing changes at
$1B4A-$1BEBare small constant edits — single bytes here and there in lookup tables. Probably parameters that shifted because surrounding code shifted.
What the diff shape suggests about Microsoft’s process. The boot stub at Apple $0801-$083C is byte-identical (60 bytes). The Pascal-signature table at $1176-$117D (2.20) / $11BE-$11C5 (2.23) is byte-identical (8 bytes). Everything else is mostly different. So Microsoft preserved the external interfaces (the boot PROM contract, the signature table format) but rewrote the internals. That’s consistent with a “we’re shipping a 2.2 release based on the new CP/M base, take the opportunity to rewrite our loader to a cleaner structure” engineering decision.
Implications for the rest of the investigation:
- The
$1907boot-finalization region’s 85-byte diff is the next natural target. 2.20 must do something equivalent to 2.23’s three page copies + warm-boot jump, but with different code. That comparison may reveal where the SoftCard CPU-switch trigger lives in 2.20 (and by analogy, in 2.23). - The 1048-byte block at
$1367is too big to fully disassemble for this article, but it’s where most of the post-load setup lives. Spot-checking pieces of it (like the LOAD_CPM call site at$1407) shows the differences are mostly internal restructuring, not new functionality.
Status: loader-wide byte diff complete. ~21 bytes of the diff are Videx-specific; the rest is internal rewrite tied to the 2.20→2.23 release cycle. Part 9 ships with this inventory; further investigation will continue closing the open mechanism questions.