# C128 KERNAL Mapping Wrapper Probe Story: E01-S03 Status: observed in VICE x128 3.10 with a true-emulation 1541; not the final compiler map ## Wrapper Contract (`kcall`) ```text in: kc_target = KERNAL entry address; A/X/Y = routine arguments out: A/X/Y and N/V/Z/C exactly as the routine returned them I flag = the caller's I flag CR, RCR, P0L/H, P1L/H = their values at the call ``` Sequence (`src/platform/kernal-probe.s`): 1. `PHP`/`SEI`, save A and the caller's P. 2. Save the compiler's CR and RCR. 3. Write the KERNAL CR first (bank 0, so the wrapper stays visible), then the KERNAL RCR. The KERNAL CR/RCR are the values BASIC had when the program started. 4. `CLI` (the KERNAL runs in its own default mapping with IRQs enabled), then `JSR` through an indirect jump to the routine. 5. `PHP`/`SEI`, save A and the result P. 6. Write the compiler RCR first (restoring bottom common), then the compiler CR (which may select bank 1). 7. Return the result N/V/Z/C merged with the caller's I flag. The ordering in steps 3 and 6 matters: whenever CR selects bank 1, bottom common RAM must already be enabled, or the wrapper's own code disappears. The wrapper code and its variables must be in bottom common RAM. Hardware stack cost is the caller's return address plus one more (`kc_jump`), on top of the routine's own usage. `kc_target` must not sit at `$xxFF` (6502 `JMP ($xxFF)` bug); the source has a link-time `.assert`. The probe also has `kcall_inline`, a compact call form (`JSR kcall_inline` followed by `.word target`, 5 bytes per call site) that preserves A/X/Y. It was needed to fit the probe inside the 8K common window. ## Compiler Mappings Tested | State | CR | RCR (low bits) | Meaning | |---|---|---|---| | 0 | `$0E` | `%0110` | RAM bank 0 to `$BFFF`, system ROM high, I/O, 8K bottom common | | 1 | `$4E` | `%0110` | Same with RAM bank 1 | RCR bits 6–7 (VIC bank) are carried over from the entry value. ## Results All checks pass; both per-state result bytes are `$FF`. | Bit | Check | State 0 | State 1 | |---|---|---|---| | `$01` | Every CHROUT returns C=0 | pass | pass | | `$02` | `OPEN 15,8,15` + `CHKIN 15` return C=0 | pass | pass | | `$04` | Status text starts with two digits and ends in CR | `00, OK,00,00` | `00, OK,00,00` | | `$08` | `LDTND` (`$98`) = 0 after CLRCHN/CLOSE on the success path | pass | pass | | `$10` | `CHKIN` on absent device 30 returns C=1, A=5 | pass | pass | | `$20` | `LDTND` = 0 after CLRCHN/CLOSE on the failure path | pass | pass | | `$40` | CR/RCR/P0/P1 identical after **every** wrapper call | pass | pass | | `$80` | `$4000` still shows this bank's marker after all calls | pass | pass | `READST` after the failed `CHKIN` returns `$80` (device not present) from both states, so ST also survives the round trip. Note that `OPEN` with no filename sends nothing on the serial bus, so the device-not-present error surfaces at `CHKIN`, not `OPEN`. Compiler disk code must check both. The x128 screenshot (`build/kernal-probe-vicii.png`) shows one CHROUT line per state, both drive-8 status lines, and `NOVA128 KERNAL PROBE OK`. ### Negative control Changing the wrapper's flag merge from `AND #$FB` to `AND #$FA` (dropping the routine's carry) makes both state results `$EF` and the verifier fails with `CHKIN carry was clear`. The checks are therefore not vacuous. ## Open Risks - **NMI:** RESTORE-key NMIs cannot be masked. An NMI taken in compiler state 1 has not been tested. E01-S05 should either prove the KERNAL NMI path under bank 1 or install a common-RAM NMI handler. - **IRQ in compiler mappings:** still masked by policy (see `docs/mmu-probe.md`). - **Fast serial / other drives:** only a 1541 on device 8 was tested; the 1571/1581 burst protocol is not exercised. - VICE-only evidence; physical hardware is E08-S06. ## Reproducing ```powershell scripts/build-kernal-probe.ps1 # PRG + fresh build/kernal-probe.d64 scripts/verify-kernal-probe.ps1 # true-drive autostart, remote checks, screenshot ``` The verifier polls `kprobe_complete` over the remote monitor because loading through true drive emulation takes a variable number of seconds. The monitor transcript is saved to `build/kernal-probe-remote.txt`.