| @@ -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. | |||
| @@ -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 | |||
| @@ -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 | | |||
| @@ -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) | |||
| @@ -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. | |||
| - 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. | |||
| @@ -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")) | |||
| @@ -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"), | |||
| @@ -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"), | |||
| @@ -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")) | |||
| @@ -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")) | |||
| @@ -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 ")) | |||
| @@ -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" | |||
| @@ -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" | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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 | |||
| 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 | |||
| ldx #>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 | |||
| @@ -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. | |||
| @@ -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 | |||
| 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 | |||
| @@ -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 | |||
| @@ -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: | |||
| @@ -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 | |||
| @@ -0,0 +1,9 @@ | |||
| class Box | |||
| public method run() | |||
| self.missing() | |||
| end method | |||
| end class | |||
| program Main | |||
| var b as Box | |||
| end program | |||
| @@ -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 | |||
| lda #>destroy_b | |||
| sta out_method_addr+1 | |||
| lda #<name_destroy | |||
| ldx #>name_destroy | |||
| ldy #name_destroy_len | |||
| jsr out_method | |||
| bcs @p7_abort | |||
| lda #<method_stub | |||
| sta out_method_addr | |||
| lda #>method_stub | |||
| sta out_method_addr+1 | |||
| lda #<name_stub | |||
| ldx #>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 | |||
Powered by TurnKey Linux.