Emit the D129 calling convention from the parser: stage each call argument on
the hardware stack, pop the stages in reverse through frame_push_byte /
frame_push_word so arg0 lands at FP+2, reserve a patched frame prologue at
the start of each body, and return through frame_leave with a 0-, 1- or
2-byte result preserved. Frames stay opt-in so existing fixtures that do not
supply frame entry points keep their previous code shape (D156).
src/common/emit.inc: EM_ERR_ARG, EM_ARG_MAX, EM_PRO_SIZEsrc/compiler/emit.s: em_prologue_start, em_arg_push, em_args_done,
em_call_finish, em_arg_total, patched prologue and result-width returnsrc/compiler/flow.s: fl_push / fl_push_word cellssrc/compiler/parser.s: parse_file_framed, argument staging hooks,
em_call_finish after the argument list, ret_widthtests/compiler/args.n8, tests/compiler/args-test.sscripts/build-args-test.ps1, scripts/verify-args-test.ps1tests/regression/suites.ps1, docs/decisions.md (D156),
docs/architecture.md (7.13), planning/epics-and-stories.mdplanning/claims/E04-S08.mdparse_args
parses each argument with em_ctx on and calls em_arg_push, which emits
PHA for a byte and PHA/TXA/PHA for a word; at ) em_args_done
pops the stages in reverse and pushes each through frame_push_byte or
frame_push_word. In the fixture Calc.scale(n as byte) reads n at
FP+2 and returns 6 for self.scale(3), so the value arrives at the
declared slot.em_prologue_start
reserves LDA #locals-lo, LDX #locals-hi, JSR frame_enter, BCC +1,
RTS (10 bytes); em_end_func patches the local count from em_locals.
A refused frame executes the RTS and never runs frame_leave or the body.
The sizing pass counts the same 10 bytes because every byte goes through
cg_put.ret_width maps the
current method's result type to 0/1/2 and em_return selects fl_ret,
fl_ret_byte or fl_ret_word; fl_call-style result saving keeps A (or
A/X) across the argument discard. The fixture's twice returns
self.scale(3) and the observed result is 6.parse_file_framed sets em_frame_want, which
em_reset copies to em_frame_on; parse_file, parse_file_base and
parse_file_sized clear the request. A fixture that does not set it emits
no prologue.x128:
scripts/verify-args-test.ps1 builds the compiler with frames.s, parses
ARGS.N8, runs Calc.twice with JSR cg_code, and requires result 6 with
no diagnostics, emitter, codegen or flow error. The suite reports PASS
through tests/regression/run-regression.ps1 -Filter args on a fresh build.em_call no longer emits on its own: it records the member row and result
width, and em_call_finish emits after the argument list. Parser call sites
were updated together with the emitter, so concrete calls still target the
same method labels.fl_call/fl_ret_* encodings rather than adding a
second convention; only the push cells (fl_push, fl_push_word) are new.$20 opcode and cg_jsr_abs,
which writes its own $20; the resulting JSR target was garbage and the
fixture hung in the emitted code. Removing the manual opcode fixed it; the
timeout marker ($C0 in the mailbox) is what identified the hang as
runtime rather than compile-time.BCC +1/RTS, which removes a label from the
routine and shrinks the prologue to 10 bytes.return expressions and arguments never enabled em_ctx, so no value bytes
were emitted and the argument width came from stale state. The parser now
brackets both with em_stmt_begin/em_stmt_end.fl_enter, which lives in
uninitialized BSS and made a fixture without frames emit a prologue; it is
now an explicit parse_file_framed request.tests/regression/run-regression.ps1 -Filter args (fresh D81 and
fresh object files, run twice); scripts/verify-args-test.ps1 standalone;
git diff --check; before the story, -Filter driver (1 passed).args pass with result 6 and no emitter,
codegen or flow error; emitted code is 0x78 bytes.src/common/emit.inc, src/compiler/emit.s,
src/compiler/flow.s, src/compiler/parser.s, tests/compiler/args.n8,
tests/compiler/args-test.s, scripts/build-args-test.ps1,
scripts/verify-args-test.ps1, tests/regression/suites.ps1,
docs/decisions.md, docs/architecture.md,
planning/epics-and-stories.md, planning/claims/E04-S08.md.EM_ARG_MAX (8) staged values;
and the prologue's failure path returns without a diagnostic, so a refused
frame is currently silent.A fresh-build failure with EM_ERR_LABEL (7) and FL_ERR_STATE (3) reproduced
on pristine 8a2d8ce and was independent of this story. Its cause was the
method-label table's $FF sentinel: any row whose byte was not $FF was taken
as an already-assigned label 0, so a later method redefined it at a different
offset. em_reset now clears the table to zero, em_method_label stores each
label as id + 1, and a lookup outside 1..FL_LABELS allocates again. Fresh
calls and args runs now pass.
The clear loop itself was also wrong: ldx #$FF / dex / bpl stops after
one row because $FE is negative, so ids from the count pass survived into the
emitting pass after fl_init had freed every flow label. The loop now counts
X up from 0 until it wraps, clearing all 256 rows on every em_reset.
hello failed at HEAD with E227 CANNOT EMIT THIS CODE YET at end program
(verified passing at 221e0c5, failing at 5489ae6). hello-test.s never set
em_on, and the new BSS variables moved it onto RAM that VICE's power-on
pattern leaves non-zero, so the statement emitter ran without print vectors.
The fixture now clears em_on before parse_file, as the emitter contract
requires of every caller.
The parser suite's verification passes, but its baseline hashes in
tests/regression/baseline.json predate 221e0c5 and 5489ae6; pristine
HEAD's parser-test.prg already differs, so that entry needs a reviewed
refresh.
Powered by TurnKey Linux.