Projects /
Microsoft SoftCard CP/M on a Videx
Reverse-engineering why Microsoft SoftCard CP/M 2.20 won't boot on a Videx Videoterm 80-column card and 2.23 does. 11 bytes of 6502 code that read the Pascal 1.1 signature byte the Videx ROM has exposed since 1980 — a 1982 fix that's Videx-shaped but not Videx-specific.
Microsoft’s Z-80 SoftCard was the peripheral that made CP/M software run on the Apple ][. The Videx Videoterm was the 80-column card that made the Apple ][ a serious business machine. They were contemporaries, sold to overlapping customers in the same era. They should work together. Microsoft SoftCard CP/M 2.20 doesn’t boot when a Videx is installed; 2.23 does. Why?
What was broken
When you boot Microsoft CP/M 2.20 on an Apple ][+ with a Z-80 SoftCard and a Videx Videoterm in slot 3, the load sequence hangs partway through. The 6502 boot loader gets started, reads sectors from the disk, hands off to the Z-80, and then nothing. No A> prompt. No Z-80 panic. Just stop.
The same hang happens on real Videx hardware and on the A2FPGA emulation of one — the emulation is faithful; the operating system is the broken side. Microsoft fixed it in CP/M 2.23 (1982). The fix has been there ever since, but the why of it has not been documented anywhere I could find.
What the research found
The change is 11 bytes of new 6502 code in the boot loader’s slot scanner. Functionally:
- CP/M 2.20 looks for the Apple Pascal 1.0 firmware ID bytes (
$Cn05=$38,$Cn07=$18). When it finds them, it routes the slot through the Pascal 1.0 device I/O path. - CP/M 2.23 looks for the Apple Pascal 1.1 firmware ID bytes (
$Cn05=$38,$Cn07=$18, and$Cn0B=$01). When it finds those, it routes the slot through the Pascal 1.1 device I/O path.
The Pascal protocol is additive: a 1.1 card declares both the 1.0 ID bytes and the 1.1 ones. But Pascal 1.1 cards do not implement the Pascal 1.0 I/O entry points. They speak only the 1.1 calling convention. So when 2.20 sees a Videx, it sees a Pascal 1.0 card (because the Videx truthfully declares $38, $18) and tries to call a 1.0 entry point the Videx doesn’t expose. The streams get crossed. Fortunately, instead of total protonic reversal, the computer just hangs.
2.23 fixed it by reading the third ID byte and routing Pascal 1.1 cards through the 1.1 path that they actually implement. Not strictly Videx-specific — any Pascal 1.1 card benefits — but the Videx is the dominant 1.1 card on the Apple ][ in 1982, so it’s the visible beneficiary.
What’s settled and what’s open
Settled (and documented in the Orchard repo):
- The 6502 boot stub at
$0801-$083C(byte-identical between 2.20 and 2.23) - The CP/M sector skew used by the boot stub
- The stage-2 language card switch and Apple monitor calls at
$1000 - The install loops that stage Z-80 code into Apple
$0200-$03FF - The slot scanner including the 11-byte Pascal 1.1 detection branch
- The Z-80 reset vector planting at Apple
$1000-$1002(JP $FA00for 2.23,JP $DA00for 2.20) - The Z-80 BIOS load addresses for both versions (
$FAB8vs$DACC)
Open (in priority order):
- The actual SoftCard CPU-switch instruction. The 6502 loader contains no writes to
$C0Bx— the switch happens elsewhere, likely in the disk-load callee at Apple$0E36or in the Z-80 fragments that run after the switch. - Annotation of the disk I/O block at Apple
$0A00-$0FFF(RWTS-style routines). - A Z-80 disassembler — needed to crack open the install fragments at
$0200-$03FF(Z-80$1200-$13FF) and the BIOS code itself. - The Z-80 BIOS for both versions — its
CONOUTandBOOTroutines are where the device-code consumer logic lives. - The complete diff: enumerate every difference between 2.20 and 2.23, not just the Videx-related ones. The 8 KB BIOS shift suggests substantial reorganization beyond a focused fix.
Articles
The investigation is being written up as a multi-part article series — each part covers a distinct phase of the work and is published as it’s drafted. The ultimate destination is a complete description and disassembly of CP/M 2.20 and 2.23 from the boot sector (6502) to the A> command prompt (Z-80), with both versions diffed throughout.
- Part 1 — Why Microsoft CP/M Didn’t Recognize an 80-Column Card (published) — The starting question. Why does 2.20 hang on a Videx and 2.23 boot? Identifies the 11-byte detection delta in the 6502 slot scanner and the Pascal 1.0 vs 1.1 calling-convention mismatch.
- Part 2 — From the Disk II ROM to the Z-80’s First Instruction (published) — Beginning-to-end narrative of the 6502 boot stage. From the Disk II PROM through the boot stub’s CP/M-skewed sector loads, the stage-2 language card switch, the slot scanner, the Z-80 reset vector planting, and the SoftCard handoff.
- Part 3 — Apple Memory, Through Z-80 Eyes (published) — The SoftCard memory model (the address-line XOR), CP/M’s CCP/BDOS/BIOS layering, the 2.23 BIOS jump table, and the per-device dispatch table that the slot scanner ultimately feeds.
- Part 4 — The Handoff: 6502 to Z-80 (published) — What the 6502 does in its final breath: the boot-finalization sequence, page copies that put CCP+BDOS in their final position, the embedded Z-80 install fragments inside the loader (270 bytes at
$143A-$1547, 2.23-specific), and the still-open SoftCard CPU-switch trigger. - Part 5 — The BIOS That Half-Exists (published) — The Z-80 has woken up. About half the BIOS isn’t on the disk at all. The other half is runtime-generated by a BIOS factory — the code-generation pattern that explains why 2.20 and 2.23 differ so structurally.
- Part 6 — The BIOS Factory (published) — The cold-boot generator at
$FB3A(2.23) /$DB6E(2.20). 2.23 dispatches device codes 3, 4, and 6 (Pascal 1.1); 2.20 only handles 3 and 4. The structural reason an 11-byte slot-scanner fix isn’t enough on its own — and how the rest of the fix lives in the generator. - Part 7 — From the Reset Vector to the Device Scan (published) — The Z-80 boot path from
JP $FA00through cold-boot setup at$FB70, the BIOS factory generator at$FB3A, and into the device-scan dispatch. Honest accounting of which mechanisms are still open frontiers. - Part 8 — The Cooperative-CPU Round-Trip (published) — How the SoftCard’s two CPUs share a disk drive. The Z-80 sync polling loop at
$1E39(six instructions); the$E000/$E010flag pair; the connection back to the original Videx hang viaJSR $Cn07on the 6502 side. - Part 9 — Every Difference: A Complete Inventory (published) — The 21-byte Videx fix inside ~8 KB of total change. Categorical inventory across boot stub, loader, BIOS, and CCP+BDOS, including the underlying CP/M 2.0 → 2.2 base bump that drives most of the diff.
- Part 10 — The CPU Switch, and What’s Left (published) — The last open mechanism: the 6502’s perpetual 24-byte loop at Apple
$03C0and theJSR $0E36instruction the SoftCard hardware uses as the CPU-switch trigger. Also the project’s status snapshot — what’s settled and what stays open.
Dev Logs
The investigation journal runs in parallel with the articles — one entry per topic / approach / area of investigation, including the dead ends. The cpm-videx dev logs document the path as it actually happened, mistakes and pivots included.
Reference
The Apple II Pascal 1.1 firmware protocol (Apple Technical Note Misc #8 — the only canonical document on this protocol) is captured here, with the source PDF preserved for offline reference.
Repository
All the disassembly artifacts, the side-by-side annotated diff, the extraction scripts, and the source disk images live in the Orchard repository under cpm-investigation/ and docs/CPM_Videx_Difference.md. The 6502 disassembler used is nibbler, also in the same repo.
The per-physical-sector reference map — for every sector of CPMV233.DSK, what it contains and how it reaches memory — is at docs/CPM_DiskSectorMap.md.
The annotated .asm source listings for both versions — the artifact form of this investigation — and the build tool that packs them back into a byte-identical .DSK are tracked under the companion project Building Microsoft SoftCard CP/M from Source.