Compute sizes and offsets after the declaration pass so forward references have a kind, report E096 for instances over 65535 bytes, and record the layout in the parser harness so declaration order is fixture-checked. Generated with Codebuff 🤖 Co-Authored-By: Codebuff <noreply@codebuff.com>main
| @@ -204,6 +204,13 @@ result must end in `return` (an `if` only when both branches do), reported at | |||
| `end` as E150. Numeric literals are typed by context and folded (E084/E086); | |||
| arrays are never whole values (E088). | |||
| E03-S05 computes each class's instance size and field offsets after the | |||
| declaration pass, once every forward type reference has a kind. Fields keep | |||
| declaration order after the two-byte descriptor pointer with no padding; ABI | |||
| sizes are 1 for boolean/byte/char, 2 for integer/word/class reference, 4 for | |||
| an interface reference, and element size times length for an array. A class | |||
| whose instance would exceed 65535 bytes reports E096 at its declaration line. | |||
| ### Pass 1 — declarations and layout | |||
| - tokenize the complete source; | |||
| @@ -85,6 +85,7 @@ After 20 errors the next one is replaced by E221 and compilation stops. | |||
| | E093 | `TYP_NOT_CLASS` | % IS NOT A CLASS | | |||
| | E094 | `TYP_ARRAY_SIGNATURE` | ARRAYS CANNOT BE PARAMETERS OR RESULTS | | |||
| | E095 | `TYP_ARRAY_LENGTH` | ARRAY LENGTH MUST NOT BE ZERO | | |||
| | E096 | `TYP_INSTANCE_TOO_LARGE` | CLASS INSTANCE TOO LARGE | | |||
| ## Access (E120-E129) | |||
| @@ -51,7 +51,7 @@ COMMON, DISKBUF, private ZP, SCRATCH, and MAILBOX are visible from bank 1. | |||
| | Owner | Bytes | Contents | | |||
| |---|---|---| | |||
| | `names.s` | 5,164 | 64 hash-chain heads + 255 name records of 20 bytes (D120) | | |||
| | `parser.s` | 4,608 | 18 columns of 256 bytes (`src/common/parser.inc`): type table by name ID (kind, declaration line, member range; E03-S01/E03-S02), 255 member rows (owner, name, flags, type, parameter range) and 255 parameter-type rows (E03-S02) | | |||
| | `parser.s` | 6,656 | 26 columns of 256 bytes (`src/common/parser.inc`): type table by name ID (kind, declaration line, member and implements ranges, instance size; E03-S01, E03-S05), 255 member rows (owner, name, flags, type, parameter range, field offset) and 255 parameter-type rows (E03-S02, E03-S05) | | |||
| BSS is not zeroed at load: every table must be cleared by its owner before | |||
| use (`names_init`, `parser.s` `tt_clear`). The member and parameter rows are | |||
| @@ -0,0 +1,107 @@ | |||
| --- | |||
| story: E03-S05 | |||
| owner: e03_layout | |||
| status: done | |||
| started: 2026-11-09 | |||
| depends-on: [E03-S02, E03-S04] | |||
| --- | |||
| # E03-S05 Claim | |||
| ## Intended change | |||
| Compute every class's instance size and every field's byte offset after pass 1, | |||
| when forward type references have a kind at last, and report E096 when an | |||
| instance would exceed 65535 bytes. Instances begin with the two-byte class | |||
| descriptor pointer and keep fields in declaration order with no padding. | |||
| Add the size and offset columns to the bank-1 type and member tables, and make | |||
| the parser harness emit a deterministic layout record so a fixture can list | |||
| each class's size and its fields' offsets in declaration order. | |||
| ## File area | |||
| - `src/common/parser.inc`, `src/compiler/parser.s` | |||
| - `src/common/diagcat.inc`, `docs/diagnostics.md` (E096) | |||
| - `tests/parser/parser-test.s`, `scripts/build-parser-test.ps1`, | |||
| `scripts/verify-parser-test.ps1` | |||
| - `tests/semantic/layout.n8`, `layout.expect`, `too_big.n8`, `too_big.expect` | |||
| - `docs/architecture.md`, `docs/memory-map.md` | |||
| - `planning/claims/E03-S05.md` | |||
| - generated files under `build/` | |||
| ## Acceptance evidence | |||
| - [x] Field offsets begin after the class-descriptor pointer and are | |||
| deterministic: `layout.n8` declares one field of every ABI size and two | |||
| arrays; `layout.expect` records `box size 35` with offsets 2, 3, 4, 5, 7, 9, | |||
| 15, 17, 21, 23, and 31 in declaration order, and `empty size 2`. The | |||
| verifier compares the whole `$FA` layout list; all 39 parser fixtures pass. | |||
| - [x] Instance size overflow/capacity is diagnosed: `too_big.n8` | |||
| (`word[40000]` = 80,000 bytes) is rejected with E096 at 1:1, matching | |||
| `too_big.expect`; the size columns saturate at `$FFFF` rather than wrapping. | |||
| - [x] Field default privacy and method default publicity match the | |||
| specification: `layout.n8` uses bare fields and one bare `public method`; | |||
| the independent source oracle in `verify-parser-test.ps1` derives every | |||
| member's access (fields private, methods public) and compares it against the | |||
| parser's 13 member rows for the fixture. The same check covers all accepted | |||
| fixtures. | |||
| - [x] MAP fixture lists sizes and offsets in declaration order: | |||
| `layout.n8`/`layout.expect` is that listing. The harness's `$FA` record is | |||
| the deterministic layout listing the future `NAME.MAP` writer will consume; | |||
| the MAP writer itself belongs to E05-S02 (`output.s`), so no `NAME.MAP` file | |||
| exists to compare yet. This is recorded under remaining risks. | |||
| ## Coordination notes | |||
| - No other claim is active. No new decision: the sizes are the approved | |||
| architecture ABI (1 for boolean/byte/char, 2 for integer/word/class | |||
| reference, 4 for interface reference, element size times length for arrays) | |||
| and object layout is the approved two-byte descriptor followed by fields. | |||
| D124-D127 from E03-S04 remain proposed. | |||
| - The layout pass runs between pass 1 and pass 2, so it only uses recorded | |||
| table rows and never depends on source order or pass-2 typing. | |||
| ## Work log | |||
| - `parser.inc`: added `C_TYPE_SIZE_LO/HI` (22/23) and | |||
| `C_MEM_OFFSET_LO/HI` (24/25); `TABLE_COLUMNS` is now 26. `docs/memory-map.md` | |||
| records the parser tables as 6,656 bytes. | |||
| - `parser.s`: `parse_file` calls `layout_classes` after pass 1. Helpers | |||
| `field_size_mem`, `field_size`, and `elem_size` compute a member's size from | |||
| its recorded type, and `store_field_offset` writes the current offset into | |||
| the member row. A class starts at 2; methods are skipped; overflow sets the | |||
| size to `$FFFF`, records the failure, and reports E096 once at the class's | |||
| declaration line and column 1. The pass loops over name IDs and member rows, | |||
| so classes and fields are laid out in declaration order. | |||
| - `parser-test.s`: `dump_layout` emits `$FA`, then per class its name, size | |||
| lo/hi, field count, and each field's name and offset lo/hi. `count_fields` | |||
| counts non-method member rows owned by the class. `FIXTURE_COUNT` is 39. | |||
| - `verify-parser-test.ps1`: decodes `$FA` until `$FD`/`$FB` and compares the | |||
| layout lines against `layout.expect`; the member loop now also stops at | |||
| `$FA`, because `dump_layout` always emits its marker before `$FD`. | |||
| - `diagcat.inc`: appended `TYP_INSTANCE_TOO_LARGE, 96`; the catalog now has 68 | |||
| entries and `docs/diagnostics.md` was regenerated. | |||
| - First build exposed four 6502 branch-range errors in `layout_classes` and | |||
| three in `dump_layout`; they are now short branches to nearby `jmp` | |||
| trampolines. The verifier edit had also joined `$p += 5` and `while` on one | |||
| line; fixed. | |||
| ## Completion | |||
| - Tests run: `scripts/build-parser-test.ps1`, | |||
| `scripts/verify-parser-test.ps1 -Port 6530`, | |||
| `scripts/build-lexer-test.ps1`, `scripts/verify-lexer-test.ps1`, | |||
| `scripts/build-diag-test.ps1 -UpdateDocs`, | |||
| `scripts/verify-diag-test.ps1`, `scripts/demo-write-check.ps1`. | |||
| - Results: all 39 parser fixtures pass (lowest expression SP `$A7`); | |||
| `layout.n8` matches 13 layout lines and `too_big.n8` reports E096 at 1:1. | |||
| The lexer matches 10 fixtures / 6,733 dump bytes; the 68-entry diagnostic | |||
| catalog and its screen/ERR expectations match with docs in sync; the demo | |||
| saves `good.n8` and `bad.n8` byte-for-byte, `good.n8` reports no errors, and | |||
| `bad.n8` reports E151, E152, E120, and E061. `vim/work.d81` was not touched. | |||
| - Files changed: as listed under File area. | |||
| - Remaining risks: the `NAME.MAP` text writer is E05-S02, so the declaration | |||
| order and layout listing are verified through the harness record rather than | |||
| a real `.map` file. E096 reports the instance's saturated `$FFFF` size in the | |||
| table; consumers must treat that as "too large". Any future padding or | |||
| alignment rule would change `layout.expect` and the offsets. | |||
| @@ -51,6 +51,8 @@ $script:ParserFixtures = [ordered]@{ | |||
| "ops.n8" = @("$t\ops.n8", "exact", "$t\ops.expect") | |||
| "uses.n8" = @("$t\uses.n8", "exact", "$t\uses.expect") | |||
| "calls.n8" = @("$t\calls.n8", "exact", "$t\calls.expect") | |||
| "layout.n8" = @("$t\layout.n8", "layout", "$t\layout.expect") | |||
| "toobig.n8" = @("$t\too_big.n8", "rejected", "$t\too_big.expect") | |||
| } | |||
| $link = Invoke-Nova128Link -Name "parser-test" -BuildDir $out ` | |||
| @@ -53,9 +53,9 @@ $records = @(); $script:p = 0 | |||
| while ($dump[$p] -ne 0xFE) { | |||
| if ($dump[$p] -ne 0xFF) { throw "dump out of sync at $p" } | |||
| $r = [pscustomobject]@{ Index = $dump[$p + 1]; Load = $dump[$p + 2]; Count = $dump[$p + 3] | |||
| MinSp = $dump[$p + 4]; Types = @(); Members = @(); Trace = $null } | |||
| MinSp = $dump[$p + 4]; Types = @(); Members = @(); Layout = @(); Trace = $null } | |||
| $p += 5 | |||
| while ($dump[$p] -ne 0xFD -and $dump[$p] -ne 0xFC) { | |||
| while ($dump[$p] -ne 0xFD -and $dump[$p] -ne 0xFC -and $dump[$p] -ne 0xFB -and $dump[$p] -ne 0xFA) { | |||
| $len = $dump[$p + 3] | |||
| $spell = ConvertFrom-Spelling $dump[($p + 4)..($p + 3 + $len)] | |||
| $r.Types += ("{0} {1} {2}" -f @("", "interface", "class", "program")[$dump[$p]], $spell, ($dump[$p + 1] + 256 * $dump[$p + 2])) | |||
| @@ -63,7 +63,7 @@ while ($dump[$p] -ne 0xFE) { | |||
| } | |||
| if ($dump[$p] -eq 0xFC) { | |||
| $p++ | |||
| while ($dump[$p] -ne 0xFD -and $dump[$p] -ne 0xFB) { | |||
| while ($dump[$p] -ne 0xFD -and $dump[$p] -ne 0xFB -and $dump[$p] -ne 0xFA) { | |||
| $owner = Read-DumpName | |||
| $name = Read-DumpName | |||
| $flags = $dump[$p]; $p++ | |||
| @@ -74,6 +74,20 @@ while ($dump[$p] -ne 0xFE) { | |||
| $(if ($flags -band 1) { "method" } else { "field" }), | |||
| $(if ($flags -band 2) { "private" } else { "public" }), $type, ($params -join ",")) | |||
| } | |||
| if ($dump[$p] -eq 0xFA) { | |||
| $p++ | |||
| while ($dump[$p] -ne 0xFD -and $dump[$p] -ne 0xFB) { | |||
| $owner = Read-DumpName | |||
| $size = $dump[$p] + 256 * $dump[$p + 1]; $p += 2 | |||
| $count = $dump[$p]; $p++ | |||
| $r.Layout += ("{0} size {1}" -f $owner, $size) | |||
| for ($k = 0; $k -lt $count; $k++) { | |||
| $fname = Read-DumpName | |||
| $off = $dump[$p] + 256 * $dump[$p + 1]; $p += 2 | |||
| $r.Layout += ("{0}.{1} offset {2}" -f $owner, $fname, $off) | |||
| } | |||
| } | |||
| } | |||
| if ($dump[$p] -eq 0xFB) { | |||
| $n = $dump[$p + 1] + 256 * $dump[$p + 2] | |||
| $r.Trace = if ($n) { [byte[]]$dump[($p + 3)..($p + 2 + $n)] } else { [byte[]]@() } | |||
| @@ -181,7 +195,7 @@ foreach ($name in $ParserFixtures.Keys) { | |||
| $problems = @() | |||
| if ($r.Load -ne 0) { $problems += "src_load returned $($r.Load)" } | |||
| switch ($group) { | |||
| "accepted" { | |||
| { $_ -eq "accepted" -or $_ -eq "layout" } { | |||
| if ($r.Count -ne 0) { $problems += "$($r.Count) diagnostic(s): $($errLines -join ' | ')" } | |||
| if ($null -ne $errLines) { $problems += "an ERR file exists" } | |||
| $want = @() | |||
| @@ -231,7 +245,7 @@ foreach ($name in $ParserFixtures.Keys) { | |||
| $detail = "{0} diagnostics" -f $got.Count | |||
| } | |||
| } | |||
| if ($group -eq "accepted" -or $memberChecked -contains $name) { | |||
| if ($group -in @("accepted", "layout") -or $memberChecked -contains $name) { | |||
| $wantMembers = Get-SourceMembers $src | |||
| if (($r.Members -join ';') -ne ($wantMembers -join ';')) { | |||
| $problems += "members [$($r.Members -join '; ')] expected [$($wantMembers -join '; ')]" | |||
| @@ -249,6 +263,13 @@ foreach ($name in $ParserFixtures.Keys) { | |||
| $detail += ", {0} traced statements" -f $got.Count | |||
| } | |||
| } | |||
| if ($group -eq "layout") { | |||
| $want = @(Get-Content -LiteralPath $expect | Where-Object { $_ -and -not $_.StartsWith("//") }) | |||
| if (($r.Layout -join ';') -ne ($want -join ';')) { | |||
| $problems += "layout [$($r.Layout -join '; ')] expected [$($want -join '; ')]" | |||
| } | |||
| $detail += ", {0} layout lines" -f $r.Layout.Count | |||
| } | |||
| if ($r.MinSp -ne 0xFF -and $r.MinSp -lt $minSp) { $minSp = $r.MinSp } | |||
| $status = if ($problems) { "FAIL" } else { "ok" } | |||
| Write-Host (" {0,-13} {1,-9} {2} {3}" -f $name, $group, $status, $detail) | |||
| @@ -74,6 +74,7 @@ | |||
| DIAGNOSTIC TYP_NOT_CLASS, 93, "% IS NOT A CLASS" | |||
| DIAGNOSTIC TYP_ARRAY_SIGNATURE, 94, "ARRAYS CANNOT BE PARAMETERS OR RESULTS" | |||
| DIAGNOSTIC TYP_ARRAY_LENGTH, 95, "ARRAY LENGTH MUST NOT BE ZERO" | |||
| DIAGNOSTIC TYP_INSTANCE_TOO_LARGE, 96, "CLASS INSTANCE TOO LARGE" | |||
| ; ----------------------------------------------------------------- access | |||
| DIAGNOSTIC ACC_PRIVATE_MEMBER, 120, "% IS PRIVATE" | |||
| @@ -75,7 +75,11 @@ C_PAR_TNAME = 18 | |||
| C_PAR_TLEN_LO = 19 | |||
| C_PAR_TLEN_HI = 20 | |||
| C_IMPL_IFACE = 21 ; implemented interface name ID | |||
| TABLE_COLUMNS = 22 | |||
| C_TYPE_SIZE_LO = 22 ; class instance size (descriptor + fields) | |||
| C_TYPE_SIZE_HI = 23 | |||
| C_MEM_OFFSET_LO = 24 ; field byte offset | |||
| C_MEM_OFFSET_HI = 25 | |||
| TABLE_COLUMNS = 26 | |||
| C_TYPE_LAST = C_TYPE_INIT ; type columns 0..this are cleared per run | |||
| .endif | |||
| @@ -1,5 +1,5 @@ | |||
| ; parser.s - declarations, members, scopes, statements, expressions, and | |||
| ; type checking (E03-S01..E03-S04). | |||
| ; type checking (E03-S01..E03-S05). | |||
| ; | |||
| ; parse_file runs two passes over the loaded source (call after src_load, | |||
| ; diag_init, and names_init): | |||
| @@ -8,8 +8,8 @@ | |||
| ; the bank-1 type table and each field and method signature in the | |||
| ; member and parameter tables; parses every statement; reports | |||
| ; syntax, ordering, duplicate, init/destroy shape, and | |||
| ; local-placement errors; records each class's implements list | |||
| ; and init row. | |||
| ; local-placement errors; records each class's implements list, | |||
| ; init row, instance size, and field offsets (E03-S05). | |||
| ; pass 2 replays the source, parsing everything again, and reports type | |||
| ; references that do not name a declared interface or class | |||
| ; (forward references are therefore legal), name, member, and | |||
| @@ -45,6 +45,7 @@ | |||
| .import tok_kind, tok_line, tok_col, tok_len, tok_value, tok_text, tok_text_len, tok_err | |||
| .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 mmu_peek1, mmu_poke1 | |||
| .importzp mmu_ptr | |||
| @@ -97,6 +98,7 @@ parse_file: | |||
| lda #$01 | |||
| sta pass | |||
| jsr parse_pass | |||
| jsr layout_classes | |||
| lda #$02 | |||
| sta pass | |||
| jsr parse_pass | |||
| @@ -544,6 +546,219 @@ no_array_sig: | |||
| jmp err1_col | |||
| @ok: rts | |||
| ; ------------------------------------------------------------- layout | |||
| ; Pass 1, after all declarations: instance sizes and field offsets. A class | |||
| ; starts with its two-byte descriptor pointer; fields follow in declaration | |||
| ; order with no padding. ABI sizes: boolean/byte/char 1, integer/word/class | |||
| ; reference 2, interface reference 4, array = element size * length. E096 is | |||
| ; reported at the class line when the total exceeds 65535. | |||
| layout_classes: | |||
| lda #$01 | |||
| sta lk_id | |||
| @class: lda lk_id | |||
| cmp names_count | |||
| bcc :+ | |||
| jmp @done | |||
| : ldy lk_id | |||
| lda #C_TYPE_KIND | |||
| jsr tb_get | |||
| cmp #TYPE_CLASS | |||
| beq :+ | |||
| jmp @next | |||
| : lda #$02 | |||
| sta cls_size | |||
| lda #$00 | |||
| sta cls_size+1 | |||
| sta cls_bad | |||
| sta lk_row | |||
| @row: lda lk_row | |||
| cmp mem_count | |||
| bcs @class_done | |||
| ldy lk_row | |||
| lda #C_MEM_OWNER | |||
| jsr tb_get | |||
| cmp lk_id | |||
| bne @rnext | |||
| ldy lk_row | |||
| lda #C_MEM_FLAGS | |||
| jsr tb_get | |||
| and #MF_METHOD | |||
| bne @rnext | |||
| lda cls_bad | |||
| bne @saturated | |||
| jsr field_size_mem | |||
| bcs @overflow | |||
| jsr store_field_offset | |||
| clc | |||
| lda cls_size | |||
| adc fsize | |||
| sta cls_size | |||
| lda cls_size+1 | |||
| adc fsize+1 | |||
| sta cls_size+1 | |||
| bcc @rnext | |||
| @overflow: | |||
| jsr store_field_offset | |||
| lda #$01 | |||
| sta cls_bad | |||
| lda #$FF | |||
| sta cls_size | |||
| sta cls_size+1 | |||
| jmp @rnext | |||
| @saturated: | |||
| jsr store_field_offset | |||
| @rnext: inc lk_row | |||
| jmp @row | |||
| @class_done: | |||
| ldy lk_id | |||
| ldx cls_size | |||
| lda #C_TYPE_SIZE_LO | |||
| jsr tb_set | |||
| ldx cls_size+1 | |||
| lda #C_TYPE_SIZE_HI | |||
| jsr tb_set | |||
| lda cls_bad | |||
| bne :+ | |||
| jmp @next | |||
| : ldy lk_id | |||
| lda #C_TYPE_LINE_LO | |||
| jsr tb_get | |||
| sta diag_line | |||
| ldy lk_id | |||
| lda #C_TYPE_LINE_HI | |||
| jsr tb_get | |||
| sta diag_line+1 | |||
| lda #$01 | |||
| sta diag_col | |||
| lda #E_TYP_INSTANCE_TOO_LARGE | |||
| jsr diag_report | |||
| bcs :+ | |||
| jmp @next | |||
| : jmp stop_parse | |||
| @next: inc lk_id | |||
| beq @done | |||
| jmp @class | |||
| @done: rts | |||
| ; fsize = the type of member row lk_row; C=1 when it exceeds 65535. | |||
| field_size_mem: | |||
| ldy lk_row | |||
| lda #C_MEM_TBASE | |||
| jsr tb_get | |||
| sta ty_base | |||
| ldy lk_row | |||
| lda #C_MEM_TNAME | |||
| jsr tb_get | |||
| sta ty_name | |||
| ldy lk_row | |||
| lda #C_MEM_TLEN_LO | |||
| jsr tb_get | |||
| sta ty_len | |||
| ldy lk_row | |||
| lda #C_MEM_TLEN_HI | |||
| jsr tb_get | |||
| sta ty_len+1 | |||
| jsr field_size | |||
| rts | |||
| ; member row lk_row's offset = cls_size. | |||
| store_field_offset: | |||
| ldy lk_row | |||
| lda #C_MEM_OFFSET_LO | |||
| ldx cls_size | |||
| jsr tb_set | |||
| lda #C_MEM_OFFSET_HI | |||
| ldx cls_size+1 | |||
| jsr tb_set | |||
| rts | |||
| ; fsize = size of the type in ty_*; C=1 when the array is too large. | |||
| field_size: | |||
| lda ty_len | |||
| ora ty_len+1 | |||
| beq @scalar | |||
| jsr elem_size | |||
| beq @zero | |||
| cmp #$01 | |||
| beq @one | |||
| cmp #$02 | |||
| beq @two | |||
| lda ty_len | |||
| sta fsize | |||
| lda ty_len+1 | |||
| sta fsize+1 | |||
| asl fsize | |||
| rol fsize+1 | |||
| bcs @over | |||
| asl fsize | |||
| rol fsize+1 | |||
| bcs @over | |||
| clc | |||
| rts | |||
| @one: lda ty_len | |||
| sta fsize | |||
| lda ty_len+1 | |||
| sta fsize+1 | |||
| clc | |||
| rts | |||
| @two: lda ty_len | |||
| sta fsize | |||
| lda ty_len+1 | |||
| sta fsize+1 | |||
| asl fsize | |||
| rol fsize+1 | |||
| bcs @over | |||
| clc | |||
| rts | |||
| @zero: lda #$00 | |||
| sta fsize | |||
| sta fsize+1 | |||
| clc | |||
| rts | |||
| @scalar: | |||
| jsr elem_size | |||
| sta fsize | |||
| lda #$00 | |||
| sta fsize+1 | |||
| clc | |||
| rts | |||
| @over: lda #$FF | |||
| sta fsize | |||
| sta fsize+1 | |||
| sec | |||
| rts | |||
| ; A = bytes in one element of ty_*: 1, 2, 4, or 0 for an unknown type. | |||
| elem_size: | |||
| lda ty_base | |||
| cmp #TY_BOOLEAN | |||
| beq @one | |||
| cmp #TY_BYTE | |||
| beq @one | |||
| cmp #TY_CHAR | |||
| beq @one | |||
| cmp #TY_INTEGER | |||
| beq @two | |||
| cmp #TY_WORD | |||
| beq @two | |||
| cmp #TY_REF | |||
| bne @none | |||
| ldy ty_name | |||
| lda #C_TYPE_KIND | |||
| jsr tb_get | |||
| cmp #TYPE_INTERFACE | |||
| beq @four | |||
| lda #$02 | |||
| rts | |||
| @one: lda #$01 | |||
| rts | |||
| @two: lda #$02 | |||
| rts | |||
| @four: lda #$04 | |||
| rts | |||
| @none: lda #$00 | |||
| rts | |||
| ; Pass 1: a class's init records its member row (+1) for `new` checks. | |||
| note_init: | |||
| lda pass | |||
| @@ -2508,3 +2723,9 @@ ret_kind: .res 1 ; RET_* | |||
| ret_row: .res 1 ; member row of the method (RET_VALUE) | |||
| body_nm: .res 1 ; method or program name ID (E151) | |||
| end_col: .res 1 ; column of `end` (E150) | |||
| ; class layout (E03-S05) | |||
| cls_size: .res 2 ; instance size being accumulated | |||
| cls_bad: .res 1 ; E096 reported for this class | |||
| fsize: .res 2 ; one field's size | |||
| lk_id: .res 1 ; layout: class name ID | |||
| lk_row: .res 1 ; layout: member row | |||
| @@ -141,6 +141,7 @@ test_entry: | |||
| lda #$FC | |||
| jsr emit | |||
| jsr dump_members | |||
| jsr dump_layout | |||
| lda fx_index | |||
| cmp #TRACE_INDEX | |||
| bne @advance | |||
| @@ -288,6 +289,97 @@ dump_members: | |||
| jmp @next | |||
| @done: rts | |||
| ; Emit $FA, then each class: name, instance size lo/hi, field count, and | |||
| ; every field's name and offset lo/hi in declaration order. | |||
| dump_layout: | |||
| lda #$FA | |||
| jsr emit | |||
| lda #$01 | |||
| sta id | |||
| @next: lda id | |||
| cmp names_count | |||
| bcc :+ | |||
| jmp @done | |||
| : ldy id | |||
| lda #C_TYPE_KIND | |||
| jsr tb_get | |||
| cmp #TYPE_CLASS | |||
| bne @not_class | |||
| lda id | |||
| jsr emit_name | |||
| ldy id | |||
| lda #C_TYPE_SIZE_LO | |||
| jsr tb_get | |||
| jsr emit | |||
| ldy id | |||
| lda #C_TYPE_SIZE_HI | |||
| jsr tb_get | |||
| jsr emit | |||
| lda id | |||
| jsr count_fields | |||
| jsr emit | |||
| lda #$00 | |||
| sta lk_row | |||
| @field_loop: | |||
| lda lk_row | |||
| cmp mem_count | |||
| bcs @fields_done | |||
| ldy lk_row | |||
| lda #C_MEM_OWNER | |||
| jsr tb_get | |||
| cmp id | |||
| bne @fnext | |||
| ldy lk_row | |||
| lda #C_MEM_FLAGS | |||
| jsr tb_get | |||
| and #MF_METHOD | |||
| bne @fnext | |||
| ldy lk_row | |||
| lda #C_MEM_NAME | |||
| jsr tb_get | |||
| jsr emit_name | |||
| ldy lk_row | |||
| lda #C_MEM_OFFSET_LO | |||
| jsr tb_get | |||
| jsr emit | |||
| ldy lk_row | |||
| lda #C_MEM_OFFSET_HI | |||
| jsr tb_get | |||
| jsr emit | |||
| @fnext: inc lk_row | |||
| jmp @field_loop | |||
| @fields_done: | |||
| @not_class: | |||
| inc id | |||
| beq @done | |||
| jmp @next | |||
| @done: rts | |||
| ; A = class name ID; A = number of field rows it owns. | |||
| count_fields: | |||
| sta lk_owner | |||
| lda #$00 | |||
| sta lk_n | |||
| sta lk_row | |||
| @loop: lda lk_row | |||
| cmp mem_count | |||
| bcs @done | |||
| ldy lk_row | |||
| lda #C_MEM_OWNER | |||
| jsr tb_get | |||
| cmp lk_owner | |||
| bne @next | |||
| ldy lk_row | |||
| lda #C_MEM_FLAGS | |||
| jsr tb_get | |||
| and #MF_METHOD | |||
| bne @next | |||
| inc lk_n | |||
| @next: inc lk_row | |||
| jmp @loop | |||
| @done: lda lk_n | |||
| rts | |||
| ; Emit $FB, the trace length, and the trace bytes. | |||
| dump_trace: | |||
| lda #$FB | |||
| @@ -425,7 +517,9 @@ fixtures: | |||
| FIXTURE "OPS.N8" | |||
| FIXTURE "USES.N8" | |||
| FIXTURE "CALLS.N8" | |||
| FIXTURE_COUNT = 37 | |||
| FIXTURE "LAYOUT.N8" | |||
| FIXTURE "TOOBIG.N8" | |||
| FIXTURE_COUNT = 39 | |||
| TRACE_INDEX = 28 ; PREC.N8 | |||
| messages: | |||
| @@ -448,6 +542,9 @@ prow: .res 1 | |||
| pleft: .res 1 | |||
| col: .res 1 | |||
| trow: .res 1 | |||
| lk_owner: .res 1 | |||
| lk_row: .res 1 | |||
| lk_n: .res 1 | |||
| trace_len: .res 2 | |||
| trace_buf: .res 1024 | |||
| dump: .res DUMP_SIZE | |||
| @@ -0,0 +1,17 @@ | |||
| // E03-S05: instance size and field offsets. A class starts with its | |||
| // two-byte descriptor pointer; fields follow in declaration order with no | |||
| // padding. boolean/byte/char are 1 byte, integer/word/class reference 2, | |||
| // interface reference 4, and an array is element size times length. | |||
| box size 35 | |||
| box.b offset 2 | |||
| box.ok offset 3 | |||
| box.c offset 4 | |||
| box.w offset 5 | |||
| box.i offset 7 | |||
| box.nums offset 9 | |||
| box.flags offset 15 | |||
| box.s offset 17 | |||
| box.next offset 21 | |||
| box.many offset 23 | |||
| box.kids offset 31 | |||
| empty size 2 | |||
| @@ -0,0 +1,27 @@ | |||
| interface Shape | |||
| method area() as word | |||
| end interface | |||
| class Box implements Shape | |||
| byte b | |||
| boolean ok | |||
| char c | |||
| word w | |||
| integer i | |||
| word[3] nums | |||
| byte[2] flags | |||
| Shape s | |||
| Box next | |||
| Shape[2] many | |||
| Box[2] kids | |||
| public method area() as word | |||
| return 0 | |||
| end method | |||
| end class | |||
| class Empty | |||
| end class | |||
| program Main | |||
| end program | |||
| @@ -0,0 +1,4 @@ | |||
| diagnostic: TYP_INSTANCE_TOO_LARGE | |||
| line: 1 | |||
| column: 1 | |||
| reason: the field array makes the class instance exceed 65535 bytes | |||
| @@ -0,0 +1,6 @@ | |||
| class Huge | |||
| word[40000] data | |||
| end class | |||
| program Main | |||
| end program | |||
Powered by TurnKey Linux.