The 6502 plants the Z-80's reset vector
Detail for Part 2 — From the Disk II ROM to the Z-80’s First Instruction.
Worked through the full 6502 boot loader for SoftCard CP/M 2.23. The loader occupies Apple ][ memory $0800-$13FF after the Disk II P6 PROM and the boot stub finish loading 11 sectors of track 0. Goal: produce an annotated assembly file like docs/DiskII_BootROM.asm showing every reachable instruction and what it does.
Most of it was mechanical — the slot scanner was already understood from the previous loader analysis, the boot stub from the disk-triage entry. The interesting find came from working through the post-scan dispatch at $10FA-$1106:
$10FA: LDA #$C3 / STA $1000 ; A = $C3
$10FF: LDA #$00 / STA $1001 ; A = $00
$1104: LDA #$FA / STA $1002 ; A = $FA
Those bytes self-modify Apple ][ RAM at $1000-$1002 — which is the same address as the loader’s stage-2 entry point. After modification: $C3 $00 $FA. As 6502 instructions: nonsense ($C3 is an undocumented opcode). As Z-80 instructions: JP $FA00.
The SoftCard maps Z-80 addresses to Apple addresses with a single XOR on bit 12 (apple_addr = z80_addr XOR $1000). Apple $1000 is therefore Z-80 $0000 — the Z-80’s reset vector, where the Z-80 fetches its first instruction after a reset.
The 6502 is planting the Z-80 reset vector. JP $FA00 jumps directly into the 2.23 CP/M BIOS cold-boot entry (the BIOS sits at $FAB8 per the BIOS investigation; $FA00 is right next to its jump table). When the 6502 eventually flips the SoftCard switch, the Z-80 starts up, reads its $0000, and jumps directly into CP/M.
Cross-checked against 2.20: at the equivalent address in the 2.20 loader ($10F2-$10FE), 2.20 plants $C3 $00 $DA = Z-80 JP $DA00. Exactly consistent with its BIOS load address at $DACC. The pattern (load-immediate, store) is byte-identical between versions; only the high byte of the JP target differs — $FA in 2.23, $DA in 2.20. Same $2000 shift the BIOS jump-table scan turned up earlier, surfaced from the 6502 side this time as an independent confirmation.
This also incidentally explains why the BIOS extraction got blocked: the loader doesn’t lay the BIOS bytes contiguously into Apple memory ahead of the switch. The 6502 only stages the Z-80 reset vector — three bytes — at Apple $1000. The BIOS bytes themselves get pulled in by Z-80 code that runs after the switch, presumably from the disk routines installed at Apple $0200-$03FF (Z-80 $1200-$13FF). To extract the BIOS without booting, you’d have to simulate the Z-80 loader.
Documented the architecture in docs/CPM_BootLoader.md and the per-instruction annotations in docs/CPM223_BootLoader.asm in the Orchard repo.
Status: boot stub, stage-2 entry, install, slot scanner, post-scan dispatch, and Z-80 reset planting all annotated. Z-80 reset target verified for both 2.20 and 2.23. Open: the disk I/O block at $0A00-$0FFF (standard RWTS-style routines), the subroutines and strings at $1100-$11AF, and the $1200-$13FF install images that are mostly Z-80 code needing Z-80 disassembly.