Bladeren bron

Complete Nova128 pass-3 sizing and MAP records (E04-S07).

main
Daniel Covington 4 dagen geleden
bovenliggende
commit
1d30db1843
25 gewijzigde bestanden met toevoegingen van 476 en 85 verwijderingen
  1. +18
    -6
      newLang/docs/architecture.md
  2. +2
    -1
      newLang/docs/decisions.md
  3. +1
    -0
      newLang/docs/diagnostics.md
  4. +11
    -7
      newLang/docs/memory-map.md
  5. +46
    -48
      newLang/planning/claims/E04-S07.md
  6. +1
    -0
      newLang/scripts/build-calls-test.ps1
  7. +1
    -0
      newLang/scripts/build-hello-test.ps1
  8. +2
    -0
      newLang/scripts/build-parser-test.ps1
  9. +1
    -0
      newLang/scripts/build-statements-test.ps1
  10. +1
    -0
      newLang/scripts/demo-write-check.ps1
  11. +4
    -1
      newLang/scripts/verify-class-test.ps1
  12. +1
    -0
      newLang/scripts/verify-counter-test.ps1
  13. +1
    -0
      newLang/src/common/diagcat.inc
  14. +1
    -0
      newLang/src/common/emit.inc
  15. +3
    -0
      newLang/src/common/formats.inc
  16. +10
    -3
      newLang/src/compiler/diagnostics.s
  17. +24
    -1
      newLang/src/compiler/emit.s
  18. +134
    -1
      newLang/src/compiler/output.s
  19. +44
    -3
      newLang/src/compiler/parser.s
  20. +120
    -2
      newLang/src/compiler/sizing.s
  21. +4
    -7
      newLang/tests/compiler/counter-test.s
  22. +2
    -1
      newLang/tests/parser/parser-test.s
  23. +3
    -0
      newLang/tests/parser/unresolved.expect
  24. +9
    -0
      newLang/tests/parser/unresolved.n8
  25. +32
    -4
      newLang/tests/runtime/class-test.s

+ 18
- 6
newLang/docs/architecture.md Bestand weergeven

@@ -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.



+ 2
- 1
newLang/docs/decisions.md Bestand weergeven

@@ -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



+ 1
- 0
newLang/docs/diagnostics.md Bestand weergeven

@@ -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 |

+ 11
- 7
newLang/docs/memory-map.md Bestand weergeven

@@ -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)



+ 46
- 48
newLang/planning/claims/E04-S07.md Bestand weergeven

@@ -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.

+ 1
- 0
newLang/scripts/build-calls-test.ps1 Bestand weergeven

@@ -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"))


+ 1
- 0
newLang/scripts/build-hello-test.ps1 Bestand weergeven

@@ -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"),


+ 2
- 0
newLang/scripts/build-parser-test.ps1 Bestand weergeven

@@ -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"),


+ 1
- 0
newLang/scripts/build-statements-test.ps1 Bestand weergeven

@@ -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"))


+ 1
- 0
newLang/scripts/demo-write-check.ps1 Bestand weergeven

@@ -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"))


+ 4
- 1
newLang/scripts/verify-class-test.ps1 Bestand weergeven

@@ -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 "))


+ 1
- 0
newLang/scripts/verify-counter-test.ps1 Bestand weergeven

@@ -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"


+ 1
- 0
newLang/src/common/diagcat.inc Bestand weergeven

@@ -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"

+ 1
- 0
newLang/src/common/emit.inc Bestand weergeven

@@ -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


+ 3
- 0
newLang/src/common/formats.inc Bestand weergeven

@@ -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

+ 10
- 3
newLang/src/compiler/diagnostics.s Bestand weergeven

@@ -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

+ 24
- 1
newLang/src/compiler/emit.s Bestand weergeven

@@ -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

+ 134
- 1
newLang/src/compiler/output.s Bestand weergeven

@@ -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


+ 44
- 3
newLang/src/compiler/parser.s Bestand weergeven

@@ -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.


+ 120
- 2
newLang/src/compiler/sizing.s Bestand weergeven

@@ -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

+ 4
- 7
newLang/tests/compiler/counter-test.s Bestand weergeven

@@ -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


+ 2
- 1
newLang/tests/parser/parser-test.s Bestand weergeven

@@ -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:


+ 3
- 0
newLang/tests/parser/unresolved.expect Bestand weergeven

@@ -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

+ 9
- 0
newLang/tests/parser/unresolved.n8 Bestand weergeven

@@ -0,0 +1,9 @@
class Box
public method run()
self.missing()
end method
end class

program Main
var b as Box
end program

+ 32
- 4
newLang/tests/runtime/class-test.s Bestand weergeven

@@ -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


Laden…
Annuleren
Opslaan

Powered by TurnKey Linux.