選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

4.1KB

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)

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

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.

Powered by TurnKey Linux.