Byte-by-byte loader diff: 2.20 vs 2.23 differ in 74% of bytes

5 min read
6502cpmsoftcardreverse-engineeringretrocomputingcpm-videx-series

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 ($0E10 vs $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:

  • $1060 run starts with a copyright string in 2.23: COPYRIGHT (C) 1982 MICROSOFT - CP followed by null bytes. 2.20 has $FF filler 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 02 plus A2 06) is inside the $1060 run. 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 $1907 85-byte run is the boot-finalization code I traced earlier. It contains the second JSR $BBEB, the page copies that put CCP+BDOS in its final position, and the JMP $03D2 that 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 $195D 163-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-$1BEB are 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 $1907 boot-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 $1367 is 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.