Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

4.9KB


story: E04-S02 owner: e04_frames status: done started: 2026-11-09 depends-on: [E01-S05, E03-S04]

E04-S02 Claim

Intended change

Add the generated-program software frame and calling convention in src/runtime/frames.s. The frame stack grows downward from $C000 over $B800-$BFFF; SP and FP use the free zero-page bytes $FB-$FE. A caller pushes arguments right-to-left, JSRs the callee, the callee enters a frame (old FP, then locals below FP), returns one byte in A or two in A/X, and the caller discards its arguments. frame_enter checks the stack bound and, on overflow, restores SP/FP and returns FRAME_ERR_STACK without corrupting the caller's frame.

The x128 ABI fixture runs with CR $0E (RAM through $BFFF), exercises a nested recursive function to a bounded depth, a one-byte-return function, and a deliberate overflow that unwinds cleanly, then restores the entry mapping and reports through the mailbox.

File area

  • src/runtime/frames.s (new), src/common/frames.inc (new)
  • tests/codegen/frame-test.s
  • scripts/build-frame-test.ps1, scripts/verify-frame-test.ps1
  • cfg/compiler.cfg (COMMON optional for runtime-only links)
  • docs/decisions.md, docs/architecture.md, docs/memory-map.md
  • planning/claims/E04-S02.md
  • generated files under build/

Acceptance evidence

  • Arguments and locals use the documented software frame: the recursive fixture copies its depth argument from FP+2 into a local, uses locals below FP, and preserves a local sentinel across nested calls.
  • Nested calls preserve required registers and frame state: 50 nested frames return the correct sum and restore SP to $C000; the one-byte helper also returns with SP/FP restored.
  • One- and two-byte returns follow the ABI: sum_bytes returns 12 in A; recurse(50) returns 1275 ($04FB) in A/X.
  • Stack overflow follows a stable runtime-fault path: the overflow run returns FRAME_ERR_STACK, restores SP=$C000 and FP=0, and leaves no partial frame. A direct check lowers the 6502 stack pointer below $40, calls frame_enter, verifies the refusal, and restores the hardware stack.
  • ABI tests include recursion to a bounded successful depth and overflow: depth 50 succeeds; depth 1000 fails through the guarded path.

Coordination notes

  • E01-S05 and E03-S04 are done; E04-S01's container is complete.
  • D129 records the provisional frame layout, stack region, hardware guard, and mapping rule.
  • The test switches CR to $0E itself and restores the entry CR before returning to BASIC. Code generation will install the same mapping and call frame_init when it emits user code (E04-S03); this story provides the runtime and ABI evidence, not codegen integration.
  • cfg/compiler.cfg now marks COMMON optional so runtime-only fixtures can link without compiler startup; compiler builds still provide it.

Work log

  • The first overflow run jammed: 2 KB of software frames allowed about 256 small frames, but the 6502 hardware stack overflowed near 128 JSR levels first. Added the $40 hardware-stack guard and gave the overflow fixture 24-byte locals so the software bound is exercised before the hardware one.
  • The guard initially executed tsx before saving the caller's X, replacing the locals high byte with the stack pointer. Locals are now saved first.
  • The recursive sum initially returned 0 because each parent reused a global depth that deeper frames overwrote. The parent now reads its depth back from its frame local after the nested return.
  • A bounded-cycle screenshot confirmed the pre-fix hang was in the overflow recursion, not the mapping switch or output path.

Completion

  • Tests run:
    • scripts/build-frame-test.ps1
    • scripts/verify-frame-test.ps1 -Port 6530
    • scripts/verify-output-test.ps1 -Port 6530 -RunPort 6531
    • scripts/verify-diag-test.ps1 -Port 6530
    • scripts/verify-parser-test.ps1 -Port 6530
    • git diff --check
  • Results: frame mailbox PASS with recursive=1275, one-byte=12, overflow=1, final SP=$C000, frame_error=1, final FP=0. The map switch and restore complete and the fixture prints/returns to BASIC.
  • Files changed: src/runtime/frames.s, src/common/frames.inc, tests/codegen/frame-test.s, scripts/build-frame-test.ps1, scripts/verify-frame-test.ps1, cfg/compiler.cfg, docs/decisions.md, docs/architecture.md, docs/memory-map.md, this claim.
  • Remaining risks: the 2 KB software stack and $40 hardware-stack threshold are provisional; the 6502 stack still limits deep recursion even when frames are small; expression spills beyond the tested locals are not yet exercised; the frame stack occupies fixed bank 0 and is not banked; codegen must install CR $0E, call frame_init, and restore the entry CR; runtime fault codes are not yet mapped to the future R-code namespace (E04-S05); no ABI test covers four-byte interface results or self yet.

Powered by TurnKey Linux.