Add member emission above the scalar emitter and control-flow helpers:
src/compiler/members.s records the incoming self in zero-page MB_SELF
($F9-$FA), loads and stores fields at their E03-S05 offsets through
LDA/STA (MB_SELF),Y, points MB_SELF at any two-byte object reference, and
emits a direct concrete call as self load, JSR, then frame_discard for the
argument bytes. codegen.s gains the zero-page and indexed-indirect forms this
needs.
The story is limited to emission and its ABI: parser-to-codegen integration,
counter.n8 as a real compile, and interface dispatch remain open.
src/compiler/members.s, src/common/members.inc (new)src/compiler/codegen.s (cg_ldy_imm, cg_lda_zp, cg_ldx_zp, cg_sta_zp,
cg_stx_zp, cg_lda_zpy, cg_sta_zpy)tests/codegen/member-test.sscripts/build-member-test.ps1, scripts/verify-member-test.ps1docs/decisions.md (D136), docs/architecture.md (7.10),
docs/memory-map.md (Member Access)planning/claims/E05-S04.mdbuild/self.field loads/stores correct offsets: the fixture generates a
writer that stores self.count (offset 2) and self.total (offset 3..4)
and byte/word readers, runs them on two objects, and checks the raw bytes at
+2, +3, and +4 rather than only the returned values.JSR and preserve the caller ABI: generated
callers push a word and a byte argument, call a real method with self in A/X,
and the callee reads the arguments at FP+2..4 and stores them into the
receiver's fields. FRAME_SP is identical before the pushes and after the
emitted discard, so the caller cleaned up exactly three argument bytes.tests/parser/private.n8
still matches private.expect exactly, including the ACC_PRIVATE_MEMBER
(E120) rows for private fields and methods reached outside their class, so
no private member reaches members.s.count=3, total=$1234 to object A and count=7, total=$5678 to object
B, the emitted readers return 3/$1234 and 7/$5678, and object A's bytes
are unchanged by the writes to B.counter.n8 compiles and runs with the expected result: deferred. There
is still no parser-to-codegen path (D131, E04-S04) and no pass-3 address
assignment, so no source file compiles end to end. The fixture proves the
emitted member and call shapes the parser will need; wiring them is the
backend story that follows sizing.s.members.s
with mb_self_store, mb_self_load, mb_point, mb_ld_field,
mb_st_field, and mb_call/mb_call_byte/mb_call_word. MB_SELF is
$F9-$FA, directly below the frame ABI, asserted at link time.LDX (zp),Y, which
the 6502 does not have ($B6 is LDX zp,Y, so it read zero page instead of
the object). Word loads now use PHA, LDA (MB_SELF),Y for the +1 byte,
TAX, then PLA; the invalid cg_ldx_zpy wrapper was removed so no later
story can emit it by mistake.set_ptr in the ZEROPAGE segment: ca65 rejects
(zp),Y for a 16-bit BSS symbol.run_plain
was called without setting A to the code offset (it jumped into the middle of
the snippet), and a temporary trace wrote A before setter saved self. Both
were fixture bugs, not layer bugs; the traces were removed before the final
runs.bne/bcs paths use the project's branch-to-JMP macro form.scripts/verify-member-test.ps1scripts/verify-control-test.ps1scripts/verify-scalar-test.ps1scripts/verify-parser-test.ps1git diff --check$A5, code 0, all
four flags set, result $5678, and shows MEMBER OK; the PRG ends $2B9D.
The control fixture still passes (11 05 0A 04 5A 00 00 03), the scalar
golden stream is unchanged at 231 bytes, and all 41 parser fixtures pass,
including the exact private.n8 expectations.src/compiler/members.s, src/common/members.inc,
src/compiler/codegen.s, tests/codegen/member-test.s,
scripts/build-member-test.ps1, scripts/verify-member-test.ps1,
docs/decisions.md, docs/architecture.md, docs/memory-map.md, this
claim.counter.n8 cannot
compile yet. Field access handles scalar fields only; arrays and interface
references are E05-S05/E06. A byte field costs four bytes per access and a
word eight, because only one zero-page receiver is cached. MB_SELF is
fixed for the whole generated program, so nested expressions that need two
receivers at once will need a spill rule when the parser drives this layer.
The fixture runs in x128 against a fresh true-drive D81; physical C128
testing remains P2 validation.Powered by TurnKey Linux.