From 1d30db184348535255b9f9669ff4011c638d9bf3 Mon Sep 17 00:00:00 2001 From: Daniel Covington Date: Mon, 9 Nov 2026 21:14:03 -0500 Subject: [PATCH] Complete Nova128 pass-3 sizing and MAP records (E04-S07). --- newLang/docs/architecture.md | 24 +++- newLang/docs/decisions.md | 3 +- newLang/docs/diagnostics.md | 1 + newLang/docs/memory-map.md | 18 +-- newLang/planning/claims/E04-S07.md | 94 ++++++++------- newLang/scripts/build-calls-test.ps1 | 1 + newLang/scripts/build-hello-test.ps1 | 1 + newLang/scripts/build-parser-test.ps1 | 2 + newLang/scripts/build-statements-test.ps1 | 1 + newLang/scripts/demo-write-check.ps1 | 1 + newLang/scripts/verify-class-test.ps1 | 5 +- newLang/scripts/verify-counter-test.ps1 | 1 + newLang/src/common/diagcat.inc | 1 + newLang/src/common/emit.inc | 1 + newLang/src/common/formats.inc | 3 + newLang/src/compiler/diagnostics.s | 13 ++- newLang/src/compiler/emit.s | 25 +++- newLang/src/compiler/output.s | 135 +++++++++++++++++++++- newLang/src/compiler/parser.s | 47 +++++++- newLang/src/compiler/sizing.s | 122 ++++++++++++++++++- newLang/tests/compiler/counter-test.s | 11 +- newLang/tests/parser/parser-test.s | 3 +- newLang/tests/parser/unresolved.expect | 3 + newLang/tests/parser/unresolved.n8 | 9 ++ newLang/tests/runtime/class-test.s | 36 +++++- 25 files changed, 476 insertions(+), 85 deletions(-) create mode 100644 newLang/tests/parser/unresolved.expect create mode 100644 newLang/tests/parser/unresolved.n8 diff --git a/newLang/docs/architecture.md b/newLang/docs/architecture.md index d23e374..5b96bd0 100644 --- a/newLang/docs/architecture.md +++ b/newLang/docs/architecture.md @@ -308,7 +308,8 @@ memory. - Load address `$1C01`; BASIC 7 stub `10 SYS 7181`; entry `$1C0D`. - Sections are contiguous and ascending; gaps are zero-filled. -- `NAME.MAP` records `ENTRY` and every `SECTION name start size` in hex. +- `NAME.MAP` records `ENTRY`, every `SECTION name start size`, and optional + `CLASS`/`METHOD` records in hex. - The E04-S01 startup writes the mailbox with producer `$03`, then PASS, and returns to BASIC. E04-S02 supplies the frame runtime separately; code generation will switch to CR `$0E` and initialize frames when it emits user @@ -607,7 +608,7 @@ prologue, and sizing/pass-3 addresses remain unimplemented. `for` currently requires a constant byte end and step 1 and leaves the loop variable one past the end value. E04-S07 owns those limits and the 255-byte code buffer. -### 7.12 Pass-3 relocation and counter (E04-S07, partial) +### 7.12 Pass-3 relocation and sizing (E04-S07) `sizing.s` computes the final CODE base from `PRG_ENTRY_ADDR` plus the bytes before the section and installs it through `parse_file_base`. `flow.s` resolves @@ -615,6 +616,14 @@ absolute `JMP` and `JSR` operands against that base; relative branches remain offset-based. Normal `parse_file` clears the base request, so the in-place statement and call fixtures keep using `cg_code`. +`parse_file_sized` selects count mode with `em_count`, captures the count +pass with `sz_capture`, then reruns with emission enabled and diagnostics +suppressed. `sz_verify` compares the code length, entry offset, routine sizes, +and all 64 label offsets; any difference raises `RES_SIZE_MISMATCH` (E228). +Routine lengths are recorded by `em_end_func` in `em_routine_sizes`. +`output.s` appends `METHOD name address` records after `CLASS` records, so +descriptor and method entry addresses appear in the deterministic MAP. + `counter.n8` is a two-local program: `total = 0`, then `for i = 1 to 5` adds `i` to `total`. Its fixture emits `COUNTER.PRG` and `COUNTB.PRG` with final `STUB`, `START`, and `CODE` addresses, checks the two files are byte-identical, @@ -622,9 +631,11 @@ and runs `COUNTER.PRG` in a second x128 session. The fixture-owned wrapper selects CR `$0E`, points `FRAME_FP` at the software frame area, calls the code, reports `total`, and restores the entry mapping. The observed result is 15. -This closes final code relocation and the first real source-to-PRG run, but not -E04-S07's count-mode routine sizing, method/class MAP records, or a single -unresolved-reference diagnostic. +The counter fixture also verifies a zero sizing-mismatch counter. The parser's +`unresolved.n8` fixture produces exactly one `SEM_UNKNOWN_NAME` (E61) for an +unknown method, with no bad-operand follow-up. The class fixture checks +`METHOD DESTROY` and `METHOD STUB` MAP records, and the output fixture +reconfirms the E202 overwrite refusal. ## 8. Heap and Ownership @@ -680,10 +691,11 @@ MAP lines after the section records: ```text CLASS name size destroy interface-count interface-directory +METHOD name address ``` All numeric fields are four hex digits except the interface count, which is -two. Empty classes and no destructor are written as `0000`; records keep the +two. METHOD records follow all CLASS records in `out_method` call order. Empty classes and no destructor are written as `0000`; records keep the caller's order. The class test reads the emitted MAP back and compares it with the descriptor's actual destructor and directory addresses. diff --git a/newLang/docs/decisions.md b/newLang/docs/decisions.md index 4fe2f83..ef6cea7 100644 --- a/newLang/docs/decisions.md +++ b/newLang/docs/decisions.md @@ -66,7 +66,8 @@ documented default. `Proposed` means a later change still needs approval. | D137 | E04-S06 emission is driven from parser pass 2: with `em_on` set, the parser calls `emit.s` as it type-checks each statement, and `parse_file` resets the emitter (and the code buffer) at the start of pass 2; pass 1 only assigns the same slots. Parameters take `FP+2` upward in declaration order, locals `FP-1` downward, one byte for byte-sized types and two for words. A slot is reached by emitting `EM_LP = FRAME_FP + offset` with the offset's sign byte, because `(FP),Y` cannot address below `FP`; `EM_LP` is `$F5-$F6` and the combine temp `EM_TMP` is `$F7`. Byte `+`/`-` keep the left operand on the hardware stack. Print calls go through the `em_pr_text/byte/cr` cells and three `JMP (cell)` trampolines, so a fixture can record output without CHROUT. Emitter failures set `em_bad` (`EM_ERR_SIZE`, `EM_ERR_TARGET`, `EM_ERR_OP`, `EM_ERR_STATE`) and stop emitting; nothing is written outside the code buffer. | Gives the first parser-to-codegen path a testable shape without freezing pass 3: the fixture executes the emitted bytes in place and checks locals and recorded output. The 255-byte buffer, one captured string literal, and the fixture-owned frame prologue are known limits for E04-S07, which owns sizing, section addresses, and `frame_enter`. Control flow, calls, fields, word values, arrays, and diagnostic reporting remain E04-S06 work. Implemented by `emit.s`, `emit.inc`, the `parser.s` hooks, `codegen.s`, and the statements fixture. | | D138 | E04-S06 completes the pass-2 emission path above D137. `flow.s` grows to 64 labels and 128 fixups; `if`/`else`, `while`, a constant-byte-end positive-step-1 `for`, and `return` emit through five-byte branches, `JMP` back edges, and `JSR` fixups. `fl_release` remains a successful no-op because released labels could still have unresolved fixups. Each method gets a stable entry label allocated by member row, so a call written before the method body resolves forward; `em_entry` records the program body's offset in `cg_code` so a fixture runs the program rather than a class method. `em_bad` is converted to `E_RES_EMIT_FAILED` (E227) after pass 2. The three print vectors are absolute zero-page cells at `$C0-$C5` (`em_pr_text`, `em_pr_byte`, `em_pr_cr`), so their low byte is never `$FF` and the `JMP (cell)` trampolines avoid the 6502 indirect-JMP page bug; six reserved BSS bytes preserve the existing layout of later emitter state. | Closes the E04-S06 byte-code path without changing the generated container. `for` ends one past its end value and currently requires a constant byte end and step 1; `return` is a bare `RTS` because the frame prologue is still fixture-owned. Word values, call arguments, arrays, method result forwarding, and the sizing/pass-3 address pass remain explicitly unimplemented and belong to E04-S07. Implemented by `emit.s`, `flow.s`, `parser.s`, `codegen.s`, `diagcat.inc`, the statements fixture, and the calls fixture. | -| D139 | E04-S07 begins the pass-3 address path. `flow.s` resolves absolute `JMP`/`JSR` operands against `fl_code_base`, which defaults to `cg_code`; `sizing.s` computes the final code base as `PRG_ENTRY_ADDR + bytes before CODE` and installs it with `em_set_base` before pass 2. `counter.n8` aliases two local bytes, loops `i = 1 to 5`, and sums into `total`; its fixture writes `COUNTER.PRG`/`COUNTB.PRG` with STUB, START, and CODE sections. The generated wrapper selects CR `$0E`, sets `FRAME_FP` to `$BF80`, calls the code base, reads `total`, restores CR, and reports PASS. | Proves final section addresses and generated-code relocation in a real x128 run while keeping existing in-place emitter fixtures unchanged. Count-mode routine sizing/mismatch accounting, method and class records in the MAP, unresolved-reference diagnostics, and general pass-3 scheduling remain E04-S07 work. Implemented by `flow.s`, `emit.s`, `sizing.s`, `counter.n8`, the counter fixture, and its scripts. | +| D139 | E04-S07 begins the pass-3 address path. `flow.s` resolves absolute `JMP`/`JSR` operands against `fl_code_base`, which defaults to `cg_code`; `sizing.s` computes the final code base as `PRG_ENTRY_ADDR + bytes before CODE` and installs it with `em_set_base` before pass 2. `counter.n8` aliases two local bytes, loops `i = 1 to 5`, and sums into `total`; its fixture writes `COUNTER.PRG`/`COUNTB.PRG` with STUB, START, and CODE sections. The generated wrapper selects CR `$0E`, sets `FRAME_FP` to `$BF80`, calls the code base, reads `total`, restores CR, and reports PASS. | Proves final section addresses and generated-code relocation in a real x128 run while keeping existing in-place emitter fixtures unchanged. D140 completes the count-mode and MAP work for E04-S07. Implemented by `flow.s`, `emit.s`, `sizing.s`, `counter.n8`, the counter fixture, and its scripts. | +| D140 | E04-S07 completes count-mode sizing and the MAP/diagnostic evidence. `em_count` selects the count pass; `em_end_func` records each routine length in `em_routine_sizes`; `sz_capture` snapshots code length, entry, routine sizes, and all 64 label offsets; the emitting pass runs with `diag_suppress`; `sz_verify` counts every difference and raises `RES_SIZE_MISMATCH` (E228) after the pass. The counter fixture requires `sz_mismatch = 0`, 175 code bytes, `CODE 1C4F`, deterministic PRGs, and `total = 15`. `output.s` adds `METHOD name address` records after `CLASS` records, proven by `METHOD DESTROY`/`METHOD STUB` in the class MAP. `tests/parser/unresolved.n8` proves one unknown method produces exactly one `SEM_UNKNOWN_NAME` (E61), and the output suite still proves E202 in the pass-3-adjacent writer path. | Closes the E04-S07 acceptance evidence without changing ordinary in-place fixtures: `parse_file` clears `em_count`, `diag_suppress`, and the base request, while `parse_file_sized` opts into the counted path. The code buffer remains 255 bytes and `em_entry` remains an offset; general program startup and frame sizing are separate work. Implemented by `parser.s`, `emit.s`, `emit.inc`, `sizing.s`, `diagnostics.s`, `diagcat.inc`, `output.s`, `formats.inc`, the counter/class/output/parser fixtures, and their scripts. | ## Deferred diff --git a/newLang/docs/diagnostics.md b/newLang/docs/diagnostics.md index 5637b18..77e80dd 100644 --- a/newLang/docs/diagnostics.md +++ b/newLang/docs/diagnostics.md @@ -131,3 +131,4 @@ After 20 errors the next one is replaced by E221 and compilation stops. | E225 | `RES_EXPR_TOO_DEEP` | EXPRESSION NESTED TOO DEEPLY | | E226 | `RES_TOO_MANY_IMPLEMENTS` | MORE THAN 255 IMPLEMENTED INTERFACES | | E227 | `RES_EMIT_FAILED` | CANNOT EMIT THIS CODE YET | +| E228 | `RES_SIZE_MISMATCH` | CODE SIZE CHANGED BETWEEN PASSES | diff --git a/newLang/docs/memory-map.md b/newLang/docs/memory-map.md index 30c5d0a..023aa3a 100644 --- a/newLang/docs/memory-map.md +++ b/newLang/docs/memory-map.md @@ -19,7 +19,7 @@ later stories. | BASIC 7 stub | 12 bytes, `10 SYS 7181` | | Entry | `$1C0D` | | Sections | ascending addresses, zero-filled gaps | -| MAP | `NOVA128 MAP 1`, `ENTRY hhhh`, `SECTION name start size`, `CLASS name size destroy count ifdir` | +| MAP | `NOVA128 MAP 1`, `ENTRY hhhh`, `SECTION name start size`, `CLASS name size destroy count ifdir`, `METHOD name address` | | Startup | clears mailbox, producer `$03`, status PASS, then `RTS` | The fixed startup is copied from `src/runtime/startup.s`; `output.s` refuses @@ -71,6 +71,8 @@ the first field at +2, so generated code needs no runtime translation. | Program entry | `em_entry` = offset of the program body in `cg_code` | | Code buffer | `cg_code`, 255 bytes (E04-S03) | | Final code base | `fl_code_base`, default `cg_code`; pass 3 uses `PRG_ENTRY_ADDR + pre-CODE bytes` | +| Routine sizes | `em_routine_sizes`, one byte per routine, recorded by `em_end_func` | +| Sizing mismatch | `sz_mismatch`; nonzero raises E228 after the emitting pass | | Print entries | `em_pr_text`/`em_pr_byte`/`em_pr_cr` cells at `$C0-$C5`, with `JMP (cell)` trampolines | `EM_LP` and `EM_TMP` sit below `MB_SELF` (`$F9-$FA`) and clear of the frame @@ -83,8 +85,9 @@ Control-flow labels use the `flow.s` pool; `if`/`while`/`for` labels are released only after their fixups resolve. Method entry labels are allocated by member row, so a forward self-call can target a method body parsed later. The 255-byte code buffer and the one-byte `em_entry` still bound the current -emitted program; pass 3 currently relocates absolute targets but does not yet -replace either limit with count-mode sizing. +emitted program. Count-mode sizing checks that the emitting pass keeps the +same length and label offsets; a mismatch raises E228 instead of producing a +shifted program. ## Heap Fixture (E05-S01) @@ -113,13 +116,14 @@ wired. | Destructor | `$0000` means none | | Interface directory | 4-byte entries: interface descriptor, method table | | Flags | reserved, must be 0; no parent-class slot | -| MAP | `CLASS name size destroy count ifdir` after sections | +| MAP | `CLASS name size destroy count ifdir` and `METHOD name address` after sections | The ABI is fixed in `src/common/class.inc`; D134 records the provisional decision. `src/runtime/objects.s` reads descriptors and objects without -allocating. `output.s` writes class MAP records in `out_class` call order; the -E05-S02 fixture checks a no-destructor/no-interface class and a destructor plus -one interface entry. Interface dispatch remains E06 work. +allocating. `output.s` writes class MAP records in `out_class` call order and method MAP +records in `out_method` call order; the E05-S02 fixture checks a +no-destructor/no-interface class, a destructor plus one interface entry, and +two method addresses. Interface dispatch remains E06 work. ## Construction and Deletion (E05-S03) diff --git a/newLang/planning/claims/E04-S07.md b/newLang/planning/claims/E04-S07.md index c95ee0f..fd8896e 100644 --- a/newLang/planning/claims/E04-S07.md +++ b/newLang/planning/claims/E04-S07.md @@ -1,8 +1,9 @@ --- story: E04-S07 owner: codebuff -status: in-progress +status: done started: 2026-11-10 +completed: 2026-11-10 depends-on: [E04-S06] --- @@ -10,65 +11,62 @@ depends-on: [E04-S06] ## Intended change -Begin the pass-3 address path: relocate absolute flow targets against the -final CODE base, add `sizing.s` address planning, and prove a real source file -compiles to deterministic PRGs and runs in x128. +Finish the pass-3 address path: count-mode sizing with zero mismatch, final +section and method addresses in the MAP, unresolved-reference diagnostics, +deterministic PRGs, and a real source file running in x128. ## File area -- `src/compiler/flow.s`, `src/compiler/emit.s`, `src/compiler/sizing.s` +- `src/compiler/flow.s`, `emit.s`, `sizing.s`, `parser.s`, `diagnostics.s` +- `src/compiler/output.s`, `src/common/formats.inc`, `diagcat.inc` - `tests/compiler/counter.n8`, `tests/compiler/counter-test.s` -- `scripts/build-counter-test.ps1`, `scripts/verify-counter-test.ps1` -- `docs/decisions.md`, `docs/architecture.md`, `docs/memory-map.md` +- `tests/runtime/class-test.s`, `tests/parser/unresolved.n8` +- `scripts/build-*.ps1`, `scripts/verify-*.ps1` +- `docs/decisions.md`, `docs/architecture.md`, `docs/memory-map.md`, + `docs/diagnostics.md` ## Acceptance evidence -- [x] Pass 3 can patch absolute labels/JMP/JSR against a final CODE base: - `counter.n8`'s 175-byte routine runs from `CODE 1C4F`. -- [x] `counter.n8` compiles from source and runs in x128: the generated PRG - reports `total = 15` through the mailbox. +- [x] `sizing.s` measures each routine in count mode so pass 2 and pass 3 + produce identical lengths and label offsets; `sz_mismatch` is 0. +- [x] Pass 3 patches absolute `JMP`/`JSR` label fixups; relative branches stay + offset-based. `counter.n8` runs from `CODE 1C4F` with `total = 15`. +- [x] MAP `ENTRY` and `SECTION` records carry final addresses; class records + and `METHOD` entry addresses are emitted and checked by the class fixture. +- [x] Forward references resolve; `tests/parser/unresolved.n8` reports exactly + one `SEM_UNKNOWN_NAME` (E61) and no bad-operand follow-up. - [x] Recompiling the same source is byte-identical: `COUNTER.PRG` and `COUNTB.PRG` compare equal. -- [x] MAP `ENTRY` and SECTION records carry final addresses for STUB, START, - and CODE. -- [ ] Count-mode sizing for every routine with a zero mismatch counter. -- [ ] MAP class-descriptor and method-entry address records. -- [ ] One diagnostic for an unresolved method/class reference. -- [ ] Reconfirm E202 overwrite refusal in the pass-3 path. - -## Coordination notes - -- This is intentionally a partial claim; the story is not marked done. -- Existing statement/call fixtures still use `cg_code` as the base, so their - in-place execution behavior is unchanged. -- The counter fixture owns its startup wrapper and frame base, like the - E04-S05 hello fixture. General runtime startup remains open. +- [x] E202 overwrite refusal still holds; the output suite leaves + `COLLIDE.PRG`/`COLLIDE.MAP` unchanged and leaves no output after abort. +- [x] `counter.n8` compiles from source and runs in x128 with `total = 15`. ## Work log -- Added `fl_set_base`; absolute fixups now add the selected base instead of - hard-coding `cg_code`. -- Added `em_set_base`/`em_base_set`, applied at `em_reset`. -- Added `sizing.s` to compute `PRG_ENTRY_ADDR + bytes-before-CODE`. -- Added `counter.n8` and a compiler fixture that writes two PRGs and a MAP. -- The first output attempt had `out_load`/`out_entry` zero, producing a 7 KB - padded file; initializing them fixed the container and MAP entry. -- The first run used `$CF80` as a frame while BASIC ROM was mapped; switched - to CR `$0E` with `$BF80`, matching the D129 frame ABI. -- Added `parse_file_base`: ordinary `parse_file` clears the requested base so - existing in-place fixtures never see uninitialized BSS, while the counter - installs its final code base explicitly. -- Made repeated definition of a label at the same offset harmless; this keeps - the forward-call fixture stable across BSS layout changes. -- Counter verification passes with 175 code bytes and `total = 15`. +- `flow.s` now resolves absolute `JMP`/`JSR` operands against `fl_code_base`; + ordinary fixtures keep the default `cg_code` base. +- `sizing.s` computes `PRG_ENTRY_ADDR + bytes-before-CODE` and installs it for + the emitting pass. `sz_capture`/`sz_verify` compare code length, entry + offset, per-routine sizes, and all label offsets. +- `emit.s` records routine sizes in `em_routine_sizes` and selects count mode + through `em_count`. `parse_file` clears count mode, diagnostic suppression, + and any base request; `parse_file_sized` opts into the counted path. +- `diag_suppress` prevents duplicate diagnostics during the emitting pass. + `RES_SIZE_MISMATCH` (E228) reports a count/emit disagreement. +- `output.s`/`formats.inc` add bounded `METHOD name address` MAP records after + class records. The class fixture checks `METHOD DESTROY` and `METHOD STUB`. +- Added the one-reference `unresolved.n8` parser fixture and the matching + `SEM_UNKNOWN_NAME` expectation. ## Completion -- Tests run: statements, calls, member, control, scalar, hello, parser, and - counter suites all PASS after the BSS-layout move. -- Results: counter emits 175 bytes; `COUNTER.PRG` and `COUNTB.PRG` are - byte-identical; MAP contains `ENTRY 1C0D`, `START 1C0D 0042`, and - `CODE 1C4F 00AF`; the generated PRG reports `total = 15`. -- Remaining risks: code buffer remains 255 bytes, `em_entry` remains an - offset, and the MAP still lacks method/class address records. No unresolved - reference path or count-mode mismatch ledger exists yet. \ No newline at end of file +- Tests run: statements, calls, member, control, scalar, hello, parser, + counter, output; class test separately. All PASS. +- Parser: 42 fixtures, including `unresolved.n8` with exactly one diagnostic. +- Counter: 175 code bytes; `sz_mismatch = 0`; MAP `ENTRY 1C0D`, + `START 1C0D 0042`, `CODE 1C4F 00AF`; `COUNTER.PRG` and `COUNTB.PRG` + byte-identical; generated PRG reports `total = 15`. +- Class MAP: `CLASS EMPTY`, `CLASS BOX`, `METHOD DESTROY 2A56`, + `METHOD STUB 2A57`; all descriptor/MAP flags pass. +- Remaining risks: the code buffer remains 255 bytes and `em_entry` remains an + offset; general runtime startup and frame sizing remain separate work. diff --git a/newLang/scripts/build-calls-test.ps1 b/newLang/scripts/build-calls-test.ps1 index e6b362a..1aaaaa1 100644 --- a/newLang/scripts/build-calls-test.ps1 +++ b/newLang/scripts/build-calls-test.ps1 @@ -22,6 +22,7 @@ $link = Invoke-Nova128Link -Name "calls-test" -BuildDir $out ` (Join-Path $project "src\compiler\codegen.s"), (Join-Path $project "src\compiler\flow.s"), (Join-Path $project "src\compiler\members.s"), + (Join-Path $project "src\compiler\sizing.s"), (Join-Path $project "src\compiler\emit.s"), (Join-Path $project "src\runtime\startup.s"), (Join-Path $project "tests\compiler\calls-test.s")) diff --git a/newLang/scripts/build-hello-test.ps1 b/newLang/scripts/build-hello-test.ps1 index d2e65a5..fbaa92b 100644 --- a/newLang/scripts/build-hello-test.ps1 +++ b/newLang/scripts/build-hello-test.ps1 @@ -20,6 +20,7 @@ $link = Invoke-Nova128Link -Name "hello-test" -BuildDir $out ` (Join-Path $project "src\compiler\codegen.s"), (Join-Path $project "src\compiler\flow.s"), (Join-Path $project "src\compiler\members.s"), + (Join-Path $project "src\compiler\sizing.s"), (Join-Path $project "src\compiler\emit.s"), (Join-Path $project "src\compiler\interfaces.s"), (Join-Path $project "src\compiler\types.s"), diff --git a/newLang/scripts/build-parser-test.ps1 b/newLang/scripts/build-parser-test.ps1 index b94234b..0ae197a 100644 --- a/newLang/scripts/build-parser-test.ps1 +++ b/newLang/scripts/build-parser-test.ps1 @@ -55,6 +55,7 @@ $script:ParserFixtures = [ordered]@{ "toobig.n8" = @("$t\too_big.n8", "rejected", "$t\too_big.expect") "interfaces.n8" = @("$t\interfaces.n8", "iface", "$t\interfaces.expect") "ifacebad.n8" = @("$t\iface_bad.n8", "exact", "$t\iface_bad.expect") + "unresolved.n8" = @("$p\unresolved.n8", "exact", "$p\unresolved.expect") } $link = Invoke-Nova128Link -Name "parser-test" -BuildDir $out ` @@ -69,6 +70,7 @@ $link = Invoke-Nova128Link -Name "parser-test" -BuildDir $out ` (Join-Path $project "src\compiler\codegen.s"), (Join-Path $project "src\compiler\flow.s"), (Join-Path $project "src\compiler\members.s"), + (Join-Path $project "src\compiler\sizing.s"), (Join-Path $project "src\compiler\emit.s"), (Join-Path $project "src\compiler\interfaces.s"), (Join-Path $project "src\compiler\types.s"), diff --git a/newLang/scripts/build-statements-test.ps1 b/newLang/scripts/build-statements-test.ps1 index 114475c..f14e6db 100644 --- a/newLang/scripts/build-statements-test.ps1 +++ b/newLang/scripts/build-statements-test.ps1 @@ -22,6 +22,7 @@ $link = Invoke-Nova128Link -Name "statements-test" -BuildDir $out ` (Join-Path $project "src\compiler\codegen.s"), (Join-Path $project "src\compiler\flow.s"), (Join-Path $project "src\compiler\members.s"), + (Join-Path $project "src\compiler\sizing.s"), (Join-Path $project "src\compiler\emit.s"), (Join-Path $project "src\runtime\startup.s"), (Join-Path $project "tests\compiler\statements-test.s")) diff --git a/newLang/scripts/demo-write-check.ps1 b/newLang/scripts/demo-write-check.ps1 index 53ec2ee..40531b6 100644 --- a/newLang/scripts/demo-write-check.ps1 +++ b/newLang/scripts/demo-write-check.ps1 @@ -39,6 +39,7 @@ $link = Invoke-Nova128Link -Name "n8check" -BuildDir $out ` (Join-Path $project "src\compiler\names.s"), (Join-Path $project "src\compiler\diagnostics.s"), (Join-Path $project "src\compiler\parser.s"), + (Join-Path $project "src\compiler\sizing.s"), (Join-Path $project "src\compiler\interfaces.s"), (Join-Path $project "src\compiler\types.s"), (Join-Path $project "demo\n8check.s")) diff --git a/newLang/scripts/verify-class-test.ps1 b/newLang/scripts/verify-class-test.ps1 index 184b342..1de2371 100644 --- a/newLang/scripts/verify-class-test.ps1 +++ b/newLang/scripts/verify-class-test.ps1 @@ -53,6 +53,7 @@ $mapPath = Join-Path $out "CLASSTEST.MAP" Read-DiskFile "classtest.map" $mapPath $true $mapLines = ([IO.File]::ReadAllText($mapPath)).TrimEnd("`r", "`n") -split "`r`n|`r|`n" $destroy = Get-Nova128Label $labels "destroy_b" +$stub = Get-Nova128Label $labels "method_stub" $ifdir = Get-Nova128Label $labels "iface_dir" $expectedMap = @( "NOVA128 MAP 1", @@ -60,7 +61,9 @@ $expectedMap = @( "SECTION STUB 1C01 000C", "SECTION START 1C0D 004A", ("CLASS EMPTY 0007 0000 00 0000"), - ("CLASS BOX 0023 {0:X4} 01 {1:X4}" -f $destroy, $ifdir)) + ("CLASS BOX 0023 {0:X4} 01 {1:X4}" -f $destroy, $ifdir), + ("METHOD DESTROY {0:X4}" -f $destroy), + ("METHOD STUB {0:X4}" -f $stub)) if (($mapLines -join "`n") -ne ($expectedMap -join "`n")) { $failures += ("CLASSTEST.MAP differs:`n got: " + ($mapLines -join "`n ") + "`n want: " + ($expectedMap -join "`n ")) diff --git a/newLang/scripts/verify-counter-test.ps1 b/newLang/scripts/verify-counter-test.ps1 index 1c04ac6..603b782 100644 --- a/newLang/scripts/verify-counter-test.ps1 +++ b/newLang/scripts/verify-counter-test.ps1 @@ -31,6 +31,7 @@ if ($mb[10] -eq 0) { $failures += "no generated code" } if ($mb[11] -ne 0) { $failures += "codegen error: $($mb[11])" } if ($mb[12] -ne 0) { $failures += "flow error: $($mb[12])" } if ($mb[13] -ne 0) { $failures += "output error: $($mb[13])" } +if ($mb[14] -ne 0) { $failures += "sizing mismatch count: $($mb[14])" } $c1541 = Get-ViceTool "c1541" $prg = Join-Path $out "counter.prg" diff --git a/newLang/src/common/diagcat.inc b/newLang/src/common/diagcat.inc index 50a2f33..5b93c9a 100644 --- a/newLang/src/common/diagcat.inc +++ b/newLang/src/common/diagcat.inc @@ -105,3 +105,4 @@ DIAGNOSTIC RES_EXPR_TOO_DEEP, 225, "EXPRESSION NESTED TOO DEEPLY" DIAGNOSTIC RES_TOO_MANY_IMPLEMENTS, 226, "MORE THAN 255 IMPLEMENTED INTERFACES" DIAGNOSTIC RES_EMIT_FAILED, 227, "CANNOT EMIT THIS CODE YET" + DIAGNOSTIC RES_SIZE_MISMATCH, 228, "CODE SIZE CHANGED BETWEEN PASSES" diff --git a/newLang/src/common/emit.inc b/newLang/src/common/emit.inc index c550f82..eb736f0 100644 --- a/newLang/src/common/emit.inc +++ b/newLang/src/common/emit.inc @@ -22,6 +22,7 @@ EM_ERR_LABEL = 7 ; no control-flow label available EM_KIND_LOCAL = 0 EM_KIND_PARAM = 1 +EM_MAX_ROUTINES = 24 EM_LAST_LOCAL = 1 EM_LAST_SELF = 2 diff --git a/newLang/src/common/formats.inc b/newLang/src/common/formats.inc index 9fd7deb..5f91fc9 100644 --- a/newLang/src/common/formats.inc +++ b/newLang/src/common/formats.inc @@ -17,6 +17,8 @@ OUT_SECTION_NAME_MAX = 12 OUT_RECORD_SIZE = 7 ; name ptr(2), name len(1), start(2), size(2) OUT_MAX_CLASSES = 8 OUT_CLASS_RECORD_SIZE = 10 ; name ptr(2), len(1), size(2), destroy(2), count(1), dir(2) +OUT_MAX_METHODS = 16 +OUT_METHOD_RECORD_SIZE = 5 ; name ptr(2), len(1), address(2) ; out_begin/out_finish/out_abort result codes (out_err). OUT_OK = 0 @@ -27,5 +29,6 @@ OUT_ERR_NAME = 4 ; base or section name too long OUT_ERR_DISK = 5 ; KERNAL or DOS failure OUT_ERR_STATE = 6 ; byte emitted outside a section OUT_ERR_CLASSES = 7 ; more than OUT_MAX_CLASSES +OUT_ERR_METHODS = 8 ; more than OUT_MAX_METHODS .endif diff --git a/newLang/src/compiler/diagnostics.s b/newLang/src/compiler/diagnostics.s index c8916f4..a0c1148 100644 --- a/newLang/src/compiler/diagnostics.s +++ b/newLang/src/compiler/diagnostics.s @@ -27,7 +27,7 @@ .import dio_open_cmd, dio_open_out, dio_read_status, dio_chkout, dio_putc .import dio_clrchn, dio_close, dio_command, dio_dos_code -.export diag_init, diag_report, diag_finish +.export diag_init, diag_report, diag_finish, diag_suppress .export diag_line, diag_col, diag_arg1, diag_arg1_len, diag_arg2, diag_arg2_len .export diag_count, diag_limit, diag_screen, diag_file_state, diag_buf @@ -59,6 +59,7 @@ diag_init: lda #$00 sta diag_count sta diag_stopped + sta diag_suppress sta diag_file_state sta diag_arg1_len sta diag_arg2_len @@ -149,6 +150,12 @@ append_base_to_open: rts diag_report: + sta diag_code + lda diag_suppress + beq :+ + clc + rts +: ldx diag_stopped beq :+ sec ; already stopped: report nothing more @@ -166,8 +173,7 @@ diag_report: jsr emit sec rts -@count: sta diag_code - inc diag_count +@count: inc diag_count jsr emit clc rts @@ -537,3 +543,4 @@ hdr_len: .res 1 msg_start: .res 1 buf_len: .res 1 diag_buf: .res DIAG_BUF_MAX +diag_suppress: .res 1 diff --git a/newLang/src/compiler/emit.s b/newLang/src/compiler/emit.s index cbee5f9..a5b10fe 100644 --- a/newLang/src/compiler/emit.s +++ b/newLang/src/compiler/emit.s @@ -26,6 +26,7 @@ .include "../common/frames.inc" .include "../common/emit.inc" .include "../common/members.inc" +.include "../common/flow.inc" .import cg_init, cg_put, cg_lda_imm, cg_ldx_imm, cg_ldy_imm, cg_lda_zp, cg_lda_zpy, cg_sta_zpy .import cg_sta_zp, cg_adc_zp, cg_sbc_zp, cg_cmp_zp, cg_adc_imm, cg_clc, cg_sec, cg_jsr_abs, cg_rts @@ -37,7 +38,8 @@ .import sc_base, sc_val, sc_len .export em_on, em_ctx, em_bad, em_last_kind -.export em_reset, em_set_base, em_base_set, em_begin_func, em_begin_params, em_local, em_param +.export em_reset, em_set_base, em_base_set, em_count, em_begin_func, em_begin_params, em_local, em_param +.export em_n_routines, em_routine_sizes, em_routine_start .export em_expr_name, em_expr_const, em_push_left, em_bin .export em_stmt_begin, em_stmt_end, em_expr_off, em_begin_print .export em_target, em_store_target, em_print, em_end_func @@ -60,7 +62,13 @@ ; Pass 2 start: empty code buffer, no errors, no function, no slots. em_reset: + lda em_count + beq @emit_mode + lda #FL_MODE_COUNT + jmp @init_flow +@emit_mode: lda #$00 +@init_flow: jsr fl_init lda em_base_set beq @base_done @@ -82,6 +90,7 @@ em_reset: sta em_locals sta em_par_bytes sta em_entry + sta em_n_routines lda #$FF sta em_loc_next ldx #$FF @@ -104,6 +113,8 @@ em_set_base: ; A body starts: fresh local slots. Parameter offsets were assigned while the ; signature was parsed and are kept. em_begin_func: + lda cg_len + sta em_routine_start lda #$FF sta em_loc_next lda #$00 @@ -558,6 +569,14 @@ em_end_func: beq @done jsr cg_rts bcs @fail + ldx em_n_routines + cpx #EM_MAX_ROUTINES + bcs @done + lda cg_len + sec + sbc em_routine_start + sta em_routine_sizes,x + inc em_n_routines @done: clc rts @fail: jmp em_fail @@ -1142,3 +1161,7 @@ em_meth_label: .res 256 em_entry: .res 1 em_code_base: .res 2 em_base_set: .res 1 +em_count: .res 1 +em_n_routines: .res 1 +em_routine_start: .res 1 +em_routine_sizes: .res EM_MAX_ROUTINES diff --git a/newLang/src/compiler/output.s b/newLang/src/compiler/output.s index 9ae5fbc..b221fc5 100644 --- a/newLang/src/compiler/output.s +++ b/newLang/src/compiler/output.s @@ -39,7 +39,7 @@ .export out_begin, out_startup, out_section, out_end_section .export out_byte, out_bytes, out_finish, out_abort -.export out_class +.export out_class, out_method, out_method_addr .export out_class_size, out_class_destroy, out_class_ifaces, out_class_ifdir .export out_load, out_entry, out_start, out_err @@ -150,6 +150,24 @@ set_class_ptr: bne @add @done: rts +; out_rec_ptr = out_methods + out_index * OUT_METHOD_RECORD_SIZE. +set_method_ptr: + lda #out_methods + sta out_rec_ptr+1 + ldx out_index + beq @done +@add: clc + lda out_rec_ptr + adc #OUT_METHOD_RECORD_SIZE + sta out_rec_ptr + bcc :+ + inc out_rec_ptr+1 +: dex + bne @add +@done: rts + ; Build and send "S0:name" on the command channel. out_scratch: sta out_scratch_ptr @@ -235,6 +253,7 @@ out_begin: lda #$00 sta out_count sta out_class_count + sta out_method_count sta out_in_section sta out_prg_created sta out_map_created @@ -611,6 +630,49 @@ out_class: sec rts +; A/X = method name, Y = length. out_method_addr supplies the final address. +out_method: + sta out_tmp + stx out_tmp+1 + sty out_tmp_len + lda out_method_count + cmp #OUT_MAX_METHODS + bcs @too_many + lda out_tmp_len + cmp #OUT_SECTION_NAME_MAX + 1 + bcs @bad_name + lda out_method_count + sta out_index + jsr set_method_ptr + ldy #0 + lda out_tmp + sta (out_rec_ptr),y + iny + lda out_tmp+1 + sta (out_rec_ptr),y + iny + lda out_tmp_len + sta (out_rec_ptr),y + iny + lda out_method_addr + sta (out_rec_ptr),y + iny + lda out_method_addr+1 + sta (out_rec_ptr),y + inc out_method_count + clc + rts +@too_many: + lda #OUT_ERR_METHODS + sta out_err + sec + rts +@bad_name: + lda #OUT_ERR_NAME + sta out_err + sec + rts + out_finish: jsr out_end_section jsr out_close_prg @@ -812,6 +874,8 @@ out_write_map: inc out_index jmp @section @done: jsr out_write_classes + bcs @out + jsr out_write_methods bcs @out clc @out: rts @@ -921,6 +985,70 @@ out_write_classes: @done: clc @out: rts +; Write METHOD records after the CLASS records, in out_method call order. +out_write_methods: + lda #$00 + sta out_index +@method: + lda out_index + cmp out_method_count + bne @body + jmp @done +@body: + jsr set_method_ptr + lda #method_prefix + ldy #method_prefix_len + jsr out_text + bcc @prefix_ok + jmp @out +@prefix_ok: + ldy #0 + lda (out_rec_ptr),y + sta out_srcp + iny + lda (out_rec_ptr),y + sta out_srcp+1 + iny + lda (out_rec_ptr),y + sta out_text_len + ldy #0 +@name: cpy out_text_len + beq @named + lda (out_srcp),y + sty out_y + jsr out_char + bcc @name_ok + jmp @out +@name_ok: + ldy out_y + iny + bne @name +@named: + lda #' ' + jsr out_char + bcc @space_ok + jmp @out +@space_ok: + ldy #3 + lda (out_rec_ptr),y + sta out_num + iny + lda (out_rec_ptr),y + sta out_num+1 + jsr out_hex4 + bcc @addr_ok + jmp @out +@addr_ok: + jsr out_cr + bcc @line_ok + jmp @out +@line_ok: + inc out_index + jmp @method +@done: clc +@out: rts + ; A/X = text, Y = length. out_text: sta out_srcp @@ -1002,6 +1130,8 @@ section_prefix: .byte "SECTION " section_prefix_len = * - section_prefix class_prefix: .byte "CLASS " class_prefix_len = * - class_prefix +method_prefix: .byte "METHOD " +method_prefix_len = * - method_prefix hex_digits: .byte "0123456789ABCDEF" .segment "BSS" @@ -1039,6 +1169,9 @@ out_map_is_open: .res 1 out_err: .res 1 out_sections: .res OUT_MAX_SECTIONS * OUT_RECORD_SIZE out_classes: .res OUT_MAX_CLASSES * OUT_CLASS_RECORD_SIZE +out_methods: .res OUT_MAX_METHODS * OUT_METHOD_RECORD_SIZE +out_method_count: .res 1 +out_method_addr: .res 2 out_class_size: .res 2 out_class_destroy: .res 2 out_class_ifaces: .res 1 diff --git a/newLang/src/compiler/parser.s b/newLang/src/compiler/parser.s index cfce902..eb06456 100644 --- a/newLang/src/compiler/parser.s +++ b/newLang/src/compiler/parser.s @@ -47,7 +47,7 @@ .import src_line .import name_intern, name_find, name_declare .import names_count -.import diag_report, diag_line, diag_col, diag_arg1, diag_arg1_len +.import diag_report, diag_line, diag_col, diag_arg1, diag_arg1_len, diag_suppress, diag_count .import mmu_peek1, mmu_poke1 .importzp mmu_ptr .import diag_arg2_len @@ -56,7 +56,7 @@ .import ty_assign, ty_binary, ty_unary .import ty_arg_sc1, ty_arg_sc2, ty_arg_name1, ty_arg_num2 .import assign_slots, check_interfaces -.import em_reset, em_set_base, em_base_set, em_on, em_ctx, em_bad, em_last_kind +.import em_reset, em_set_base, em_base_set, em_count, em_on, em_ctx, em_bad, em_last_kind .import em_begin_func, em_begin_params, em_local, em_param .import em_stmt_begin, em_stmt_end, em_expr_off, em_begin_print .import em_expr_name, em_expr_const, em_push_left, em_bin @@ -65,8 +65,9 @@ .import em_open_block, em_cond_false, em_else, em_close_block .import em_for_var, em_for_save_end, em_for_save_step, em_for_default_step, em_for_setup .import em_return, em_method_begin, em_program_begin +.import sz_capture, sz_verify -.export parse_file, parse_file_base, tb_get, tb_set, mem_count, par_count +.export parse_file, parse_file_base, parse_file_sized, tb_get, tb_set, mem_count, par_count .export gen_text, gen_text_len, gen_print_count, gen_bad .export ex_trace_on, ex_min_sp .exportzp ex_tp @@ -97,12 +98,26 @@ ex_tp: .res 2 ; test trace pointer (ex_trace_on) parse_file: lda #$00 sta em_base_set + sta em_count + sta diag_suppress jmp parse_start ; A/X = final code base for pass-3 absolute fixups. Clears any pending base ; before installing this one, so BSS garbage cannot affect ordinary fixtures. parse_file_base: jsr em_set_base + lda #$00 + sta em_count + sta diag_suppress + jmp parse_start + +; Count pass, snapshot, then an emitting pass. The caller installs the final +; code base first. Diagnostics are reported only by the count pass. +parse_file_sized: + lda #$01 + sta em_count + lda #$00 + sta diag_suppress parse_start: tsx stx parse_sp @@ -137,7 +152,33 @@ parse_start: beq :+ lda #E_RES_EMIT_FAILED jsr err2 + rts +: + lda em_count + beq @done + lda diag_count + bne @done + jsr sz_capture + lda #$00 + sta em_count + lda #$01 + sta diag_suppress + jsr em_reset + jsr parse_pass + jsr em_finish + lda #$00 + sta diag_suppress + lda em_bad + beq :+ + lda #E_RES_EMIT_FAILED + jsr err2 + rts : + jsr sz_verify + bcc @done + lda #E_RES_SIZE_MISMATCH + jsr diag_report +@done: rts ; Error limit reached: return straight to parse_file's caller. diff --git a/newLang/src/compiler/sizing.s b/newLang/src/compiler/sizing.s index 9b097c3..99a680f 100644 --- a/newLang/src/compiler/sizing.s +++ b/newLang/src/compiler/sizing.s @@ -7,10 +7,14 @@ .setcpu "6502" .include "../common/formats.inc" +.include "../common/flow.inc" +.include "../common/emit.inc" -.import em_set_base +.import em_set_base, em_entry, em_n_routines, em_routine_sizes +.import cg_len, fl_get_label, fl_err .export sz_plan_code, sz_code_base +.export sz_capture, sz_verify, sz_mismatch .segment "CODE" @@ -27,5 +31,119 @@ sz_plan_code: ldx sz_code_base+1 jmp em_set_base +; Snapshot the count-mode pass before the emitting pass resets the buffer. +sz_capture: + lda #$00 + sta fl_err + lda cg_len + sta sz_len + lda em_entry + sta sz_entry + lda em_n_routines + sta sz_nr + ldx #$00 +@routine: + cpx sz_nr + beq @labels + lda em_routine_sizes,x + sta sz_sizes,x + inx + bne @routine +@labels: + lda #$00 + sta sz_i +@label: + lda sz_i + cmp #FL_LABELS + beq @done + jsr fl_get_label + bcs @undefined + txa + ldy sz_i + sta sz_labels,y + jmp @next +@undefined: + ldy sz_i + lda #$FF + sta sz_labels,y +@next: inc sz_i + jmp @label +@done: rts + +; Compare the count-mode snapshot with the emitting pass. Every difference +; increments sz_mismatch; C=1 when anything differs. +sz_verify: + lda #$00 + sta sz_mismatch + sta fl_err + lda cg_len + cmp sz_len + beq @entry + inc sz_mismatch +@entry: + lda em_entry + cmp sz_entry + beq @count + inc sz_mismatch +@count: + lda em_n_routines + cmp sz_nr + beq @routines + inc sz_mismatch +@routines: + ldx #$00 +@routine: + cpx sz_nr + beq @labels + lda em_routine_sizes,x + cmp sz_sizes,x + beq @next_routine + inc sz_mismatch +@next_routine: + inx + bne @routine +@labels: + lda #$00 + sta sz_i +@label: + lda sz_i + cmp #FL_LABELS + beq @done + jsr fl_get_label + bcs @undefined + txa + sta sz_off + ldy sz_i + lda sz_labels,y + cmp sz_off + beq @next + inc sz_mismatch + jmp @next +@undefined: + ldy sz_i + lda sz_labels,y + cmp #$FF + beq @next + inc sz_mismatch +@next: inc sz_i + jmp @label +@done: + lda #$00 + sta fl_err + lda sz_mismatch + beq @ok + sec + rts +@ok: clc + rts + .segment "BSS" -sz_code_base: .res 2 \ No newline at end of file +sz_code_base: .res 2 +sz_len: .res 1 +sz_entry: .res 1 +sz_nr: .res 1 +sz_i: .res 1 +sz_off: .res 1 +sz_mismatch: .res 1 +sz_sizes: .res EM_MAX_ROUTINES +sz_labels: .res FL_LABELS \ No newline at end of file diff --git a/newLang/tests/compiler/counter-test.s b/newLang/tests/compiler/counter-test.s index 7a4d526..dbcb9c1 100644 --- a/newLang/tests/compiler/counter-test.s +++ b/newLang/tests/compiler/counter-test.s @@ -13,11 +13,12 @@ .import src_load, src_dos_code, src_kernal_err .import names_init .import diag_init, diag_finish, diag_screen -.import parse_file_base +.import parse_file_sized .import em_on, em_bad .import cg_code, cg_len, cg_err .import fl_err .import sz_plan_code, sz_code_base +.import sz_mismatch .import out_begin, out_section, out_end_section, out_byte, out_bytes .import out_finish, out_abort, out_load, out_entry, out_start, out_err .import gen_stub @@ -101,9 +102,7 @@ test_entry: lda #WRAPPER_LEN jsr sz_plan_code ; final code base for absolute fixups - lda sz_code_base - ldx sz_code_base+1 - jsr parse_file_base + jsr parse_file_sized jsr diag_finish sta diag_count lda diag_count @@ -116,10 +115,8 @@ test_entry: sta MB_DATA+3 lda fl_err sta MB_DATA+4 - lda sz_code_base + lda sz_mismatch sta MB_DATA+6 - lda sz_code_base+1 - sta MB_DATA+7 lda diag_count ON_NONZERO fail_diag diff --git a/newLang/tests/parser/parser-test.s b/newLang/tests/parser/parser-test.s index 7e06aeb..70453b8 100644 --- a/newLang/tests/parser/parser-test.s +++ b/newLang/tests/parser/parser-test.s @@ -577,7 +577,8 @@ fixtures: FIXTURE "TOOBIG.N8" FIXTURE "INTERFACES.N8" FIXTURE "IFACEBAD.N8" -FIXTURE_COUNT = 41 + FIXTURE "UNRESOLVED.N8" +FIXTURE_COUNT = 42 TRACE_INDEX = 28 ; PREC.N8 messages: diff --git a/newLang/tests/parser/unresolved.expect b/newLang/tests/parser/unresolved.expect new file mode 100644 index 0000000..afb3ed3 --- /dev/null +++ b/newLang/tests/parser/unresolved.expect @@ -0,0 +1,3 @@ +// One unresolved method reference; it must be reported once, not emitted as a +// bad operand or a second error. +SEM_UNKNOWN_NAME 3 10 diff --git a/newLang/tests/parser/unresolved.n8 b/newLang/tests/parser/unresolved.n8 new file mode 100644 index 0000000..425b4ab --- /dev/null +++ b/newLang/tests/parser/unresolved.n8 @@ -0,0 +1,9 @@ +class Box + public method run() + self.missing() + end method +end class + +program Main + var b as Box +end program diff --git a/newLang/tests/runtime/class-test.s b/newLang/tests/runtime/class-test.s index 535004c..8309a4f 100644 --- a/newLang/tests/runtime/class-test.s +++ b/newLang/tests/runtime/class-test.s @@ -16,10 +16,11 @@ .import out_begin, out_startup, out_finish, out_abort .import out_load, out_entry, out_err .import out_class, out_class_size, out_class_destroy +.import out_method, out_method_addr .import out_class_ifaces, out_class_ifdir .import obj_class, obj_size, obj_destroy, obj_iface_dir -.export destroy_b, iface_dir +.export destroy_b, iface_dir, method_stub .segment "LOADADDR" .word $1C01 @@ -197,9 +198,12 @@ test_entry: ldx #>name_source ldy #name_source_len jsr out_begin - bcs @p7_abort - jsr out_startup - bcs @p7_abort + bcc :+ + jmp @p7_abort +: jsr out_startup + bcc :+ + jmp @p7_abort +: lda #7 ; EMPTY: size 7, no destructor/interface sta out_class_size @@ -236,6 +240,26 @@ test_entry: jsr out_class bcs @p7_abort + lda #destroy_b + sta out_method_addr+1 + lda #name_destroy + ldy #name_destroy_len + jsr out_method + bcs @p7_abort + + lda #method_stub + sta out_method_addr+1 + lda #name_stub + ldy #name_stub_len + jsr out_method + bcs @p7_abort + jsr out_finish bcs @p7_fail lda #$01 @@ -331,6 +355,10 @@ name_empty: .byte "EMPTY" name_empty_len = * - name_empty name_box: .byte "BOX" name_box_len = * - name_box +name_destroy: .byte "DESTROY" +name_destroy_len = * - name_destroy +name_stub: .byte "STUB" +name_stub_len = * - name_stub messages: ok_message: .byte "NOVA128 CLASS TEST OK", $0D, $00 fail_message: .byte "NOVA128 CLASS TEST FAIL", $0D, $00