mirror of
https://github.com/imjasonh/nescript
synced 2026-07-10 17:52:51 +00:00
codegen: reuse analyzer's local allocations so inline asm {param} works
Fixes compiler-bugs.md #1 — the inline-asm `{name}` resolver looks parameters up in the analyzer's `VarAllocation` table (because that's the only address map it has), but `IrCodeGen::new` was minting a parallel `$0300+` range for every function-local and ignoring what the analyzer had picked. The spill prologue wrote the param to the codegen's private address, the inline asm read from the analyzer's zero-page address, and nothing ever bridged the two — `LDA {param}` would silently load whatever the RAM clear left at the stale slot (always `0`). Fix: drop the `local_ram_next` loop and just look each local up in `allocations` by the analyzer's qualified name (`__local__{scope}__{local}`). The scope string that `gen_function` already computed for `substitute_asm_vars` is now shared with the new address-seeding loop via a `scope_prefix_for_fn(&str)` helper, so the two call sites can't drift. The analyzer's layout already satisfies the "no overlapping live locals" invariant the codegen was relying on — it scopes every local under `__local__<scope>__<name>` so two functions with a parameter named `x` land in different slots. Updated `gen_function_prologue_spills_params_to_local_ram`: the regression test for the War-era param clobbering bug was asserting the spill's destination specifically had to be an absolute address at `$0300+`. That's no longer the mechanism — the spill lands in whatever slot the analyzer assigned, which is zero page when there's room. The test now asserts the destination is *any* address outside `$04-$07`, which is the actual invariant. Reverted the `LDX $04` / `LDY $05` workaround in `examples/sha256/sha_core.ne` — every primitive there now uses `{dst}` / `{src}` / `{w_ofs}` / `{h_ofs}` / `{k_ofs}` substitution as originally intended. The "Parameter convention" comment that documented the workaround is gone. Regenerated `tests/emulator/goldens/inline_asm_demo.png`: that example's `times_four(input)` was previously returning `input` verbatim because the inline asm's `LDA {result}` / `ASL A` / `ASL A` / `STA {result}` operated on a zero-page byte that was disconnected from the NEScript-level `result` variable. With the fix, `times_four` correctly returns `input * 4`, so the smiley-tracker's frame-180 position shifts by the expected `(frame_count * 4) mod 256` delta. The other 33 ROMs remain byte-identical. Verified: - `cargo clippy --all-targets -- -D warnings` clean on both rustc 1.94.1 and 1.95.0. - `cargo test --all-targets`: 616 + 3 + 75 tests pass. - `cargo fmt --check` clean. - Full emulator harness: 34/34 ROMs match goldens. - SHA-256 of "NES" still computes to `AE9145DB5CABC41FE34B54E34AF8881F462362EA20FD8F861B26532FFBB84E0D`. - `--memory-map` output now reflects what the generated code actually reads and writes (previously the codegen's $0300+ override was invisible to the dump). https://claude.ai/code/session_01FRmSBruVWCufm3LsUVMs8v
This commit is contained in:
parent
f128170abf
commit
76d0fd0d28
6 changed files with 179 additions and 191 deletions
Binary file not shown.
Binary file not shown.
|
|
@ -21,28 +21,21 @@
|
|||
//
|
||||
// K[i] and H_INIT[i] live in RAM as `var` arrays loaded from
|
||||
// the init_array initialiser at reset time (see constants.ne).
|
||||
//
|
||||
// ── Parameter convention ────────────────────────────────────
|
||||
//
|
||||
// NEScript passes the first two function parameters via
|
||||
// zero-page slots $04 and $05 before the JSR. The compiler's
|
||||
// standard prologue immediately spills those slots into a
|
||||
// per-function local in high RAM so nested calls don't step on
|
||||
// them — but the inline-asm `{name}` resolver looks parameters
|
||||
// up in the analyzer's allocation table, which doesn't see the
|
||||
// codegen's spill. Rather than double-copy through a global,
|
||||
// every primitive below reads its parameters straight out of
|
||||
// the transport slots with `LDX $04` / `LDY $05`. Our
|
||||
// primitives never JSR from inside the `asm` block, so the
|
||||
// transport slots are still live when we read them.
|
||||
|
||||
// ── 32-bit byte primitives ──────────────────────────────────
|
||||
//
|
||||
// Every primitive reads its destination and source offsets
|
||||
// via `{dst}` / `{src}` / `{w_ofs}` / … substitutions, which
|
||||
// resolve to the analyzer's per-function local slots. The
|
||||
// codegen's function prologue spills the `$04`/`$05` transport
|
||||
// slots into those same addresses on entry, so the values are
|
||||
// already live by the time the asm block runs.
|
||||
|
||||
// wk[dst..dst+4] = wk[src..src+4]
|
||||
fun cp_wk(dst: u8, src: u8) {
|
||||
asm {
|
||||
LDX $04
|
||||
LDY $05
|
||||
LDX {dst}
|
||||
LDY {src}
|
||||
LDA {wk},Y
|
||||
STA {wk},X
|
||||
INX
|
||||
|
|
@ -63,8 +56,8 @@ fun cp_wk(dst: u8, src: u8) {
|
|||
// wk[dst..dst+4] ^= wk[src..src+4]
|
||||
fun xor_wk(dst: u8, src: u8) {
|
||||
asm {
|
||||
LDX $04
|
||||
LDY $05
|
||||
LDX {dst}
|
||||
LDY {src}
|
||||
LDA {wk},X
|
||||
EOR {wk},Y
|
||||
STA {wk},X
|
||||
|
|
@ -89,8 +82,8 @@ fun xor_wk(dst: u8, src: u8) {
|
|||
// wk[dst..dst+4] &= wk[src..src+4]
|
||||
fun and_wk(dst: u8, src: u8) {
|
||||
asm {
|
||||
LDX $04
|
||||
LDY $05
|
||||
LDX {dst}
|
||||
LDY {src}
|
||||
LDA {wk},X
|
||||
AND {wk},Y
|
||||
STA {wk},X
|
||||
|
|
@ -115,8 +108,8 @@ fun and_wk(dst: u8, src: u8) {
|
|||
// wk[dst..dst+4] += wk[src..src+4] (chained ADC for carry)
|
||||
fun add_wk(dst: u8, src: u8) {
|
||||
asm {
|
||||
LDX $04
|
||||
LDY $05
|
||||
LDX {dst}
|
||||
LDY {src}
|
||||
CLC
|
||||
LDA {wk},X
|
||||
ADC {wk},Y
|
||||
|
|
@ -142,7 +135,7 @@ fun add_wk(dst: u8, src: u8) {
|
|||
// wk[dst..dst+4] = ~wk[dst..dst+4] (bitwise NOT, in place)
|
||||
fun not_wk(dst: u8) {
|
||||
asm {
|
||||
LDX $04
|
||||
LDX {dst}
|
||||
LDA {wk},X
|
||||
EOR #$FF
|
||||
STA {wk},X
|
||||
|
|
@ -170,7 +163,7 @@ fun not_wk(dst: u8) {
|
|||
// previous byte's bit 0 into the next byte's bit 7.
|
||||
fun rotr1_wk(dst: u8) {
|
||||
asm {
|
||||
LDX $04
|
||||
LDX {dst}
|
||||
LDA {wk},X
|
||||
LSR A
|
||||
INX
|
||||
|
|
@ -191,7 +184,7 @@ fun rotr1_wk(dst: u8) {
|
|||
// new[2] = old[3], new[3] = old[0]
|
||||
fun byte_rotr_wk(dst: u8) {
|
||||
asm {
|
||||
LDX $04
|
||||
LDX {dst}
|
||||
LDY {wk},X
|
||||
INX
|
||||
LDA {wk},X
|
||||
|
|
@ -233,7 +226,7 @@ fun rotr_wk(dst: u8, n: u8) {
|
|||
// becomes 0).
|
||||
fun shr1_wk(dst: u8) {
|
||||
asm {
|
||||
LDX $04
|
||||
LDX {dst}
|
||||
INX
|
||||
INX
|
||||
INX
|
||||
|
|
@ -251,7 +244,7 @@ fun shr1_wk(dst: u8) {
|
|||
// byte becomes 0.
|
||||
fun byte_shr_wk(dst: u8) {
|
||||
asm {
|
||||
LDX $04
|
||||
LDX {dst}
|
||||
INX
|
||||
LDA {wk},X
|
||||
DEX
|
||||
|
|
@ -290,8 +283,8 @@ fun shr_wk(dst: u8, n: u8) {
|
|||
// wk[dst..dst+4] = w[w_ofs..w_ofs+4]
|
||||
fun cp_w_to_wk(dst: u8, w_ofs: u8) {
|
||||
asm {
|
||||
LDX $04
|
||||
LDY $05
|
||||
LDX {dst}
|
||||
LDY {w_ofs}
|
||||
LDA {w},Y
|
||||
STA {wk},X
|
||||
INX
|
||||
|
|
@ -312,8 +305,8 @@ fun cp_w_to_wk(dst: u8, w_ofs: u8) {
|
|||
// wk[dst..dst+4] += w[w_ofs..w_ofs+4]
|
||||
fun add_w_to_wk(dst: u8, w_ofs: u8) {
|
||||
asm {
|
||||
LDX $04
|
||||
LDY $05
|
||||
LDX {dst}
|
||||
LDY {w_ofs}
|
||||
CLC
|
||||
LDA {wk},X
|
||||
ADC {w},Y
|
||||
|
|
@ -339,8 +332,8 @@ fun add_w_to_wk(dst: u8, w_ofs: u8) {
|
|||
// w[w_ofs..w_ofs+4] = wk[src..src+4]
|
||||
fun cp_wk_to_w(w_ofs: u8, src: u8) {
|
||||
asm {
|
||||
LDX $05
|
||||
LDY $04
|
||||
LDX {src}
|
||||
LDY {w_ofs}
|
||||
LDA {wk},X
|
||||
STA {w},Y
|
||||
INX
|
||||
|
|
@ -361,8 +354,8 @@ fun cp_wk_to_w(w_ofs: u8, src: u8) {
|
|||
// h_state[h_ofs..h_ofs+4] += wk[src..src+4]
|
||||
fun add_wk_to_h(h_ofs: u8, src: u8) {
|
||||
asm {
|
||||
LDX $04
|
||||
LDY $05
|
||||
LDX {h_ofs}
|
||||
LDY {src}
|
||||
CLC
|
||||
LDA {h_state},X
|
||||
ADC {wk},Y
|
||||
|
|
@ -388,8 +381,8 @@ fun add_wk_to_h(h_ofs: u8, src: u8) {
|
|||
// wk[dst..dst+4] += _K_BYTES[k_ofs..k_ofs+4]
|
||||
fun add_k_to_wk(dst: u8, k_ofs: u8) {
|
||||
asm {
|
||||
LDX $04
|
||||
LDY $05
|
||||
LDX {dst}
|
||||
LDY {k_ofs}
|
||||
CLC
|
||||
LDA {wk},X
|
||||
ADC {_K_BYTES},Y
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue