boot2

Playing with the boostrap
git clone https://git.ryansepassi.com/git/boot2.git
Log | Files | Refs

commit 84ebd78783a6bbc55059ea0cac96188ac7334c3d
parent fbf22f4bdb7af44f88f457580832a4d6b925229d
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Thu, 23 Apr 2026 15:37:46 -0700

Merge branch 'worktree-agent-aecb01d58b541506d' into integrate-m1pp

# Conflicts:
#	m1pp/m1pp.M1

Diffstat:
Mm1pp/m1pp.M1 | 421++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 418 insertions(+), 3 deletions(-)

diff --git a/m1pp/m1pp.M1 b/m1pp/m1pp.M1 @@ -3709,9 +3709,97 @@ DEFINE EXPR_INVALID 1100000000000000 ## 2i, 2i+1). Emit as a synthesized TOK_WORD via append_text + emit_token. ## Oracle: m1pp.c:emit_hex_value. :emit_hex_value - la_br &err_not_implemented + enter_0 + + # ehv_value = value; ehv_bytes = byte_count + la_a2 &ehv_value + st_a0,a2,0 + la_a2 &ehv_bytes + st_a1,a2,0 + + # i = 0 + li_t0 %0 %0 +:emit_hex_value_loop + # if (i == bytes) done + la_a1 &ehv_bytes + ld_t1,a1,0 + la_br &emit_hex_value_emit + beq_t0,t1 + + # byte = ehv_value & 0xFF + la_a1 &ehv_value + ld_t2,a1,0 + andi_a3,t2,255 + + # high = (byte >> 4) & 0x0F (byte is already in a3) + shri_a2,a3,4 + andi_a2,a2,15 + + # low = byte & 0x0F + andi_a3,a3,15 + + # scratch[2*i] = hex_chars[high] + la_a1 &hex_chars + add_a1,a1,a2 + lb_a2,a1,0 + la_a1 &ehv_scratch + shli_a3,t0,1 + add_a1,a1,a3 + sb_a2,a1,0 + + # scratch[2*i+1] = hex_chars[low] (reload low from byte & 0x0F) + la_a1 &ehv_value + ld_t2,a1,0 + andi_a3,t2,255 + andi_a3,a3,15 + la_a1 &hex_chars + add_a1,a1,a3 + lb_a2,a1,0 + la_a1 &ehv_scratch + shli_a3,t0,1 + add_a1,a1,a3 + addi_a1,a1,1 + sb_a2,a1,0 + + # ehv_value >>= 8 + la_a1 &ehv_value + ld_t2,a1,0 + shri_t2,t2,8 + st_t2,a1,0 + + # i++ + addi_t0,t0,1 + la_br &emit_hex_value_loop b +:emit_hex_value_emit + # text_ptr = append_text(&ehv_scratch, 2 * ehv_bytes) + la_a0 &ehv_scratch + la_a1 &ehv_bytes + ld_a1,a1,0 + shli_a1,a1,1 + la_br &append_text + call + + # ehv_token.kind = TOK_WORD; ehv_token.text_ptr = text_ptr; + # ehv_token.text_len = 2 * ehv_bytes + la_a2 &ehv_token + li_a3 TOK_WORD + st_a3,a2,0 + st_a0,a2,8 + la_a1 &ehv_bytes + ld_a1,a1,0 + shli_a1,a1,1 + st_a1,a2,16 + + # emit_token(&ehv_token) + la_a0 &ehv_token + la_br &emit_token + call + + leave + ret + ## ============================================================================ ## --- Phase 8-9 STUB: builtin dispatcher ( ! @ % $ %select ) ------------------ ## ============================================================================ @@ -3738,9 +3826,294 @@ DEFINE EXPR_INVALID 1100000000000000 ## Any other text under a builtin slot -> fatal "bad builtin". ## Oracle: m1pp.c:expand_builtin_call. :expand_builtin_call - la_br &err_not_implemented + enter_0 + + # ebc_stream = stream_ptr; also stash builtin_tok via a register reload path + la_a2 &ebc_stream + st_a0,a2,0 + + # lparen = builtin_tok + 24; if (lparen >= stream->end) fatal + addi_t0,a1,24 + ld_t1,a0,8 # stream->end + la_br &err_bad_macro_header + beq_t0,t1 + la_br &err_bad_macro_header + blt_t1,t0 + + # if (lparen->kind != TOK_LPAREN) fatal + ld_a3,t0,0 + li_a2 TOK_LPAREN + la_br &err_bad_macro_header + bne_a3,a2 + + # parse_args(lparen, stream->end) + mov_a0,t0 + la_a2 &ebc_stream + ld_a2,a2,0 + ld_a1,a2,8 # stream->end + la_br &parse_args + call + + # snapshot call_end_pos -> ebc_call_end_pos + la_a0 &call_end_pos + ld_t0,a0,0 + la_a1 &ebc_call_end_pos + st_t0,a1,0 + + # dispatch on builtin_tok->text. We've lost a1 (builtin_tok) across calls, + # so reload from stream: builtin_tok = lparen - 24 = (parse_args's a0 input) + # — easier path: builtin_tok was passed in originally; it's not snapshotted. + # Solution: snapshot builtin_tok at function entry too (but we don't have a + # BSS slot for it). We DO know stream->pos still points at the builtin_tok + # because expand_builtin_call hasn't moved it yet. Use that. + la_a0 &ebc_stream + ld_a0,a0,0 + ld_t0,a0,16 # stream->pos -> builtin_tok + + # if tok_eq_const(tok, "!", 1) -> bytes=1 + mov_a0,t0 + la_a1 &const_bang + li_a2 %1 %0 + la_br &tok_eq_const + call + la_br &ebc_arg_set_1 + bnez_a0 + + # if tok_eq_const(tok, "@", 1) -> bytes=2 + la_a0 &ebc_stream + ld_a0,a0,0 + ld_a0,a0,16 + la_a1 &const_at + li_a2 %1 %0 + la_br &tok_eq_const + call + la_br &ebc_arg_set_2 + bnez_a0 + + # if tok_eq_const(tok, "%", 1) -> bytes=4 + la_a0 &ebc_stream + ld_a0,a0,0 + ld_a0,a0,16 + la_a1 &const_pct + li_a2 %1 %0 + la_br &tok_eq_const + call + la_br &ebc_arg_set_4 + bnez_a0 + + # if tok_eq_const(tok, "$", 1) -> bytes=8 + la_a0 &ebc_stream + ld_a0,a0,0 + ld_a0,a0,16 + la_a1 &const_dlr + li_a2 %1 %0 + la_br &tok_eq_const + call + la_br &ebc_arg_set_8 + bnez_a0 + + # if tok_eq_const(tok, "%select", 7) -> select path + la_a0 &ebc_stream + ld_a0,a0,0 + ld_a0,a0,16 + la_a1 &const_select + li_a2 %7 %0 + la_br &tok_eq_const + call + la_br &ebc_select + bnez_a0 + + # else: fatal + la_br &err_bad_macro_header b +:ebc_arg_set_1 + li_a0 %1 %0 + la_a1 &ebc_bytes + st_a0,a1,0 + la_br &ebc_arg_path + b +:ebc_arg_set_2 + li_a0 %2 %0 + la_a1 &ebc_bytes + st_a0,a1,0 + la_br &ebc_arg_path + b +:ebc_arg_set_4 + li_a0 %4 %0 + la_a1 &ebc_bytes + st_a0,a1,0 + la_br &ebc_arg_path + b +:ebc_arg_set_8 + li_a0 %8 %0 + la_a1 &ebc_bytes + st_a0,a1,0 + la_br &ebc_arg_path + b + +:ebc_arg_path + # require arg_count == 1 + la_a0 &arg_count + ld_t0,a0,0 + li_t1 %1 %0 + la_br &err_bad_macro_header + bne_t0,t1 + + # snapshot arg_starts[0], arg_ends[0] + la_a0 &arg_starts + ld_t0,a0,0 + la_a1 &ebc_arg0_start + st_t0,a1,0 + la_a0 &arg_ends + ld_t0,a0,0 + la_a1 &ebc_arg0_end + st_t0,a1,0 + + # value = eval_expr_range(arg0_start, arg0_end) + la_a0 &ebc_arg0_start + ld_a0,a0,0 + la_a1 &ebc_arg0_end + ld_a1,a1,0 + la_br &eval_expr_range + call + + # ebc_value = a0 + la_a1 &ebc_value + st_a0,a1,0 + + # stream->pos = ebc_call_end_pos; stream->line_start = 0 + la_a0 &ebc_stream + ld_a0,a0,0 + la_a1 &ebc_call_end_pos + ld_t0,a1,0 + st_t0,a0,16 + li_t1 %0 %0 + st_t1,a0,24 + + # emit_hex_value(ebc_value, ebc_bytes) + la_a0 &ebc_value + ld_a0,a0,0 + la_a1 &ebc_bytes + ld_a1,a1,0 + la_br &emit_hex_value + call + + leave + ret + +:ebc_select + # require arg_count == 3 + la_a0 &arg_count + ld_t0,a0,0 + li_t1 %3 %0 + la_br &err_bad_macro_header + bne_t0,t1 + + # snapshot arg_starts[0..2] / arg_ends[0..2] + la_a0 &arg_starts + ld_t0,a0,0 + la_a1 &ebc_arg0_start + st_t0,a1,0 + la_a0 &arg_starts + ld_t0,a0,8 + la_a1 &ebc_then_start + st_t0,a1,0 + la_a0 &arg_starts + ld_t0,a0,16 + la_a1 &ebc_else_start + st_t0,a1,0 + + la_a0 &arg_ends + ld_t0,a0,0 + la_a1 &ebc_arg0_end + st_t0,a1,0 + la_a0 &arg_ends + ld_t0,a0,8 + la_a1 &ebc_then_end + st_t0,a1,0 + la_a0 &arg_ends + ld_t0,a0,16 + la_a1 &ebc_else_end + st_t0,a1,0 + + # value = eval_expr_range(arg0_start, arg0_end) + la_a0 &ebc_arg0_start + ld_a0,a0,0 + la_a1 &ebc_arg0_end + ld_a1,a1,0 + la_br &eval_expr_range + call + + # if (value != 0) chosen = then; else chosen = else + la_br &ebc_select_then + bnez_a0 + + # chosen = else + la_a0 &ebc_else_start + ld_t0,a0,0 + la_a1 &ebc_arg0_start + st_t0,a1,0 + la_a0 &ebc_else_end + ld_t0,a0,0 + la_a1 &ebc_arg0_end + st_t0,a1,0 + la_br &ebc_select_after_pick + b + +:ebc_select_then + # chosen = then + la_a0 &ebc_then_start + ld_t0,a0,0 + la_a1 &ebc_arg0_start + st_t0,a1,0 + la_a0 &ebc_then_end + ld_t0,a0,0 + la_a1 &ebc_arg0_end + st_t0,a1,0 + +:ebc_select_after_pick + # stream->pos = ebc_call_end_pos; stream->line_start = 0 + la_a0 &ebc_stream + ld_a0,a0,0 + la_a1 &ebc_call_end_pos + ld_t0,a1,0 + st_t0,a0,16 + li_t1 %0 %0 + st_t1,a0,24 + + # if (chosen_start == chosen_end) return + la_a0 &ebc_arg0_start + ld_t0,a0,0 + la_a1 &ebc_arg0_end + ld_t1,a1,0 + la_br &ebc_select_done + beq_t0,t1 + + # mark = pool_used + la_a0 &pool_used + ld_t0,a0,0 + la_a1 &ebc_mark + st_t0,a1,0 + + # copy_span_to_pool(chosen_start, chosen_end) + la_a0 &ebc_arg0_start + ld_a0,a0,0 + la_a1 &ebc_arg0_end + ld_a1,a1,0 + la_br &copy_span_to_pool + call + + # push_pool_stream_from_mark(mark) + la_a0 &ebc_mark + ld_a0,a0,0 + la_br &push_pool_stream_from_mark + call + +:ebc_select_done + leave + ret + ## --- Error paths ------------------------------------------------------------- ## Each err_* loads a (msg, len) pair for fatal; fatal writes "m1pp: <msg>\n" ## to stderr and exits 1. Error labels are branched to from range/overflow @@ -3891,6 +4264,9 @@ DEFINE EXPR_INVALID 1100000000000000 :op_gt ">" :op_ge ">=" +## Nibble-to-hex lookup table for emit_hex_value. +:hex_chars "0123456789ABCDEF" + :msg_prefix "m1pp: " :msg_newline " " @@ -3992,7 +4368,6 @@ ZERO8 :eval_value ZERO8 -<<<<<<< HEAD ## Phase 6 paste-pass spill slots. Both append_pasted_token and ## paste_pool_range call other functions, so all locals must round-trip ## through BSS across the call. @@ -4087,6 +4462,46 @@ ZERO8 :aeo_i ZERO8 +## Layer 8 (Phase 8/9) scratch. +## emit_hex_value: ehv_value/bytes hold the args; ehv_scratch is a 16-byte +## buffer (max 8 bytes -> 16 hex chars); ehv_token is a synthesized 24-byte +## Token { kind, text_ptr, text_len }. +:ehv_value +ZERO8 +:ehv_bytes +ZERO8 +:ehv_scratch +ZERO8 ZERO8 +:ehv_token +ZERO8 ZERO8 ZERO8 + +## expand_builtin_call: snapshots the stream pointer, the post-call resume +## position, the byte count for !@%$, the eval_expr_range result, the chosen +## arg span (start/end), the unchosen-side spans for %select, and the +## pool mark used to push the chosen-stream slice. +:ebc_stream +ZERO8 +:ebc_call_end_pos +ZERO8 +:ebc_bytes +ZERO8 +:ebc_value +ZERO8 +:ebc_arg0_start +ZERO8 +:ebc_arg0_end +ZERO8 +:ebc_then_start +ZERO8 +:ebc_then_end +ZERO8 +:ebc_else_start +ZERO8 +:ebc_else_end +ZERO8 +:ebc_mark +ZERO8 + ## arg_starts[16] / arg_ends[16]: 16 × 8 = 128 bytes each, i.e. 4 ZERO32. ## Written by parse_args; read by expand_macro_tokens and expand_builtin_call. :arg_starts