您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

4.8KB


story: E05-S03 owner: e05_objects status: done started: 2026-11-09 depends-on: [E05-S02, E04-S02]

E05-S03 Claim

Intended change

Add the runtime construction/deletion layer above the E05-S01 heap and E05-S02 descriptor. obj_new allocates the descriptor's instance size, stores the descriptor pointer in the object header, and calls a caller-supplied init entry once after a successful allocation. Constructor arguments stay on the software frame stack, so init can enter its normal frame and read them. Allocation failure returns OBJ_ERR_HEAP without calling init.

obj_delete treats null as a successful no-op, reads the optional destructor from the descriptor, calls it with the object as self, then returns the whole object to the heap. A small synthetic-return indirect-call helper invokes the runtime-selected destructor without relying on JSR (address), which the 8502 does not have.

File area

  • src/runtime/construct.s (new)
  • src/common/class.inc (construction error codes)
  • tests/runtime/object-test.s
  • scripts/build-object-test.ps1, scripts/verify-object-test.ps1
  • docs/decisions.md, docs/architecture.md, docs/memory-map.md
  • planning/claims/E05-S03.md
  • generated files under build/

Acceptance evidence

  • new allocates, initializes the header, then calls init once: the fixture's five-byte Res object gets a nonzero heap pointer, obj_class reads back desc_res from object+0, and init_count is 1. Both fields are zero at init entry, so the heap's zeroing happens before the header is written and before init runs.
  • Failed allocation does not call init: desc_huge ($FFFF bytes) makes obj_new return C=1 with obj_error = OBJ_ERR_HEAP and leaves init_count at 1.
  • delete null is a no-op: obj_delete(0) returns C=0 with obj_error = OBJ_OK and destroy_count still 0.
  • delete calls destroy once and frees the complete object: the destructor runs exactly once with A/X = the object, reads back tag $2A and word $1234 written by init, and heap_free_total returns to the initial $1000 once the instance is freed. A class with no destructor also returns the full capacity.
  • Constructor arguments and destructor side effects have end-to-end tests: init(value as word, tag as byte) reads its two arguments from FP+2 in the fixture and writes them into the object; the destructor's recorded self and field values are checked after deletion.

Work log

  • Selected E05-S03 after E05-S02; no other claim was active.
  • Added obj_new, obj_delete, and obj_call, plus obj_init_ptr and obj_error, and the OBJ_OK/OBJ_ERR_HEAP/OBJ_ERR_BAD codes in class.inc. Construction lives in its own module so the E05-S02 class test still links objects.s without the heap.
  • First x128 run hung: obj_new handed the object pointer to obj_call instead of the init entry, so it jumped into the zeroed heap and executed a BRK. obj_call now takes its entry from zero-page co_vec and preserves A/X, which also leaves A/X free for the callee's self.
  • Second run failed phase 3: after the null test, obj_delete passed low|high to obj_class, reading the descriptor from the wrong address. Reloading the object pointer before the accessor fixed it.
  • Replaced the branches that outgrew the +/-128-byte range with the project's short-branch plus JMP trampoline pattern.
  • Removed the temporary mailbox breadcrumbs used to locate the two defects before recording the final runs.

Completion

  • Tests run:
    • scripts/build-object-test.ps1 (PRG ends at $28F7, below the $4000 heap base)
    • scripts/verify-object-test.ps1
    • scripts/verify-class-test.ps1
    • scripts/verify-output-test.ps1
    • scripts/verify-heap-test.ps1
    • git diff --check
  • Results: the object fixture reports producer 3, status $A5, code 0, all five construction/deletion flags set, and shows OBJECT OK. The class test still emits CLASS EMPTY 0007 0000 00 0000 and CLASS BOX 0023 2935 01 2A47; the output test still passes with its 133-byte PRG; the heap test still passes with its R005 exhaustion fault.
  • Files changed: src/runtime/construct.s, src/common/class.inc, tests/runtime/object-test.s, scripts/build-object-test.ps1, scripts/verify-object-test.ps1, docs/decisions.md (D135), docs/architecture.md (8.3), docs/memory-map.md, this claim.
  • Remaining risks: no parser-to-codegen path emits new/delete yet, so the R005 mapping for a failed new still belongs to codegen (D133). The heap region stays caller-chosen. obj_call relies on co_vec not ending at $xxFF (link-time assertion) and spends two hardware-stack bytes per call. Constructor argument count is bounded only by the frame stack. The fixture runs against a fresh true-drive D81 in x128; physical C128 testing remains P2 validation.

Powered by TurnKey Linux.