2.20 also makes two LOAD_CPM calls — not unique to 2.23
Detail for Part 4 — The Handoff: 6502 to Z-80.
Earlier I found that 2.23’s loader does a second JSR $BBEB (LOAD_CPM-equivalent) at $191E, after the main 29-sector read at $1416. I’d hypothesized this might be 2.23-specific — the mechanism by which it loads handler templates that 2.20 doesn’t need (since 2.20 has static handlers in BIOS).
Today I went looking for the analogous mechanism in 2.20 to verify. The result is more nuanced.
2.20’s LOAD_CPM-equivalent is at $0E10, not $BBEB. The address differs because 2.20 has a different staging layout (PREP_HANDOFF puts disk routines at different positions). Searching the 2.20 loader for JSR $0E10:
$1608: JSR $0E10
$17D0: JSR $0E10
Two calls. Both versions of the loader do two LOAD_CPM calls.
The 2.20 call at $17D0 is the “main” load (analog of 2.23’s $1416):
$17C6: LDA #$0B ; sector start = trk0:$0B
$17C8: STA $03E1
$17CB: LDA #$1C ; ← 28 sectors (2.23 reads 29)
$17CD: PHA
$17CE: PHP
$17CF: SEI
$17D0: JSR $0E10 ; LOAD_CPM
So 2.20 reads 28 sectors in its main load, vs 2.23’s 29. And it has a second call at $1608, wrapped in language-card bank-switching:
$1603: LDA $C083 ; bank in LC RAM (write+read)
$1606: PHP
$1607: SEI
$1608: JSR $0E10 ; LOAD_CPM
$160B: LDA $C081 ; switch back to LC ROM/RAM bank 2
$160E: PLP
$160F: RTS
The bank-switch wrap is interesting — $1608’s caller has parameters set up such that the call reads into LC RAM. That’s where the BIOS at Z-80 $FAB8 would live (Z-80 high addresses are served by Apple LC RAM under SoftCard’s hardware). So $1608’s second load may be specifically loading the BIOS bytes into LC RAM.
This changes the interpretation of 2.23’s second load. In 2.23, I had hypothesized the second JSR $BBEB at $191E was loading runtime-handler templates that 2.20 wouldn’t need. But 2.20 also does a second load — wrapped in LC bank-switching — which is consistent with both versions doing the same architectural thing: the main LOAD_CPM populates Apple staging; a second LOAD_CPM populates LC RAM with BIOS bytes.
What might still differ: what the second load actually puts in LC RAM. If 2.20’s second load is loading the static-handler-containing 2 KB BIOS, and 2.23’s second load is loading the smaller (1.35 KB) BIOS plus separately loaded handler templates, the sector counts and source positions differ. But the mechanism — bank-switch + LOAD_CPM call — is shared.
Sector counts so far:
| Version | Main load ($1416/$17D0) | Second load ($191E/$1608) | Total |
|---|---|---|---|
| 2.20 | 28 sectors (LDA #$1C) | unknown count ($1608 setup is in caller) | unknown |
| 2.23 | 29 sectors (LDA #$1D) | unknown count ($191E uses LDA #$80 as parameter — meaning unclear) | unknown |
Implication for the inventory in Part 9. The “static vs runtime handler” architectural shift is still real (2.20 has BIOS page 6 of static handlers, 2.23 doesn’t). But the loading mechanism may not be the place where that shift shows up — both versions load via two LOAD_CPM calls. The shift shows up in the BIOS content (static handler code in 2.20’s code page 6 vs not present in 2.23) and in the cold-boot generator’s dispatch (static handlers in 2.20 dispatch directly to BIOS code, runtime-generated handlers in 2.23 dispatch into trap-marker pages).
So the loading mechanism is shared between versions; the what gets loaded differs. That’s a more accurate framing.
Status: 2.20’s two LOAD_CPM calls confirmed. Both versions share the dual-load mechanism. The architectural difference between versions is in the BIOS content, not in the boot-pipeline shape. The inventory in Part 9 still holds; this devlog refines its claim about the second load being 2.23-specific.