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.
src/runtime/construct.s (new)src/common/class.inc (construction error codes)tests/runtime/object-test.sscripts/build-object-test.ps1, scripts/verify-object-test.ps1docs/decisions.md, docs/architecture.md, docs/memory-map.mdplanning/claims/E05-S03.mdbuild/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.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.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.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.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.obj_delete passed low|high
to obj_class, reading the descriptor from the wrong address. Reloading the
object pointer before the accessor fixed it.JMP trampoline pattern.scripts/build-object-test.ps1 (PRG ends at $28F7, below the $4000
heap base)scripts/verify-object-test.ps1scripts/verify-class-test.ps1scripts/verify-output-test.ps1scripts/verify-heap-test.ps1git diff --check$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.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.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.