lisp.M1 (172121B)
1 ## lisp.M1 — Seed Lisp interpreter (portable across aarch64/amd64/riscv64) 2 ## 3 ## Steps 1-8 of LISP.md §"Staged implementation plan". On top of the 4 ## step-6 evaluator, step 8 replaces the embedded `src_text` blob with 5 ## a runtime file read: _start takes the script path from argv[1], 6 ## openat/read/closes it into :src_buf, and seeds src_base/src_len for 7 ## the reader. argc < 2, open failure, and files ≥ 16 KiB each land on 8 ## a dedicated error() message. 9 ## 10 ## Observable test (`make PROG=lisp run-all`): exit 42 on pass when 11 ## run against `hello.scm`. The script evaluates 12 ## `(define id (lambda (x) x)) (id 42)` — identity applied to 42, 13 ## exit status = decoded fixnum of the final result. 14 ## 15 ## Tag table (low 3 bits, LISP.md §"Value representation"): 16 ## 001 fixnum / 010 pair / 011 vector / 100 string / 101 symbol / 17 ## 110 closure|prim / 111 singleton (nil=0x07) 18 ## 19 ## Headered objects carry a 64-bit header before payload: 20 ## [ 8-bit type | 8-bit gc-flags | 48-bit length-or-arity ] 21 ## Little-endian: byte 0 is the low byte of length, byte 7 is the type. 22 ## make_symbol writes the length with a 64-bit store (high 16 bits = 0) 23 ## and then overwrites byte 7 with type=2 via SB. Type codes: 1=string, 24 ## 2=symbol, 3=vector, 4=closure, 5=primitive. 25 26 27 ## ---- tagged-lisp constants --------------------------------------------- 28 ## Singletons live in the low 6 bits (upper 3 bits distinguish them, low 29 ## 3 = 111 = singleton tag). Named DEFINEs so intent beats "07000000". 30 DEFINE NIL 07000000 31 DEFINE TRUE 0F000000 32 DEFINE FALSE 17000000 33 DEFINE UNSPEC 27000000 34 35 DEFINE TAG_FIXNUM 01000000 36 DEFINE TAG_PAIR 02000000 37 DEFINE TAG_VECTOR 03000000 38 DEFINE TAG_STRING 04000000 39 DEFINE TAG_SYMBOL 05000000 40 DEFINE TAG_PROC 06000000 41 42 DEFINE TYPE_STRING 01000000 43 DEFINE TYPE_SYMBOL 02000000 44 DEFINE TYPE_VECTOR 03000000 45 DEFINE TYPE_CLOSURE 04000000 46 DEFINE TYPE_PRIM_FIXED 05000000 47 DEFINE TYPE_PRIM_VARIADIC 06000000 48 49 DEFINE ZERO32 '0000000000000000000000000000000000000000000000000000000000000000' 50 51 52 ## ---- heap-state -------------------------------------------------------- 53 ## The two arenas are fixed in BSS; only the bump heads are mutable. 54 ## Upper 4 bytes stay zero because all labels fit below 4 GiB. 55 :pair_heap_next &pair_heap_start %0 56 :obj_heap_next &obj_heap_start %0 57 58 59 ## ---- _start ---------------------------------------------------------- 60 ## Step 8 driver: read the .scm file named on the command line into 61 ## src_buf, then loop over its top-level forms. Each form is evaluated 62 ## in an empty local env (so unqualified lookups fall through to the 63 ## global hash) and the last result is kept. Final result is displayed 64 ## with write (closing the step-5 read-print cycle) and its fixnum 65 ## payload becomes the exit status — lets test harnesses assert via $?. 66 ## 67 ## r4 holds the running last_result once the intern loop finishes. It 68 ## survives read_expr/eval across iterations because r4 is callee-saved; 69 ## _start itself has no PROLOGUE and never returns, so its r4 is 70 ## sovereign. Early in _start r4 doubles as the source fd across the 71 ## read_file_all CALL (same callee-saved guarantee). 72 :_start 73 ## Kernel layout at entry: [sp+0]=argc, [sp+8..]=argv[0..argc-1], 74 ## then NULL, then envp. We need argc and argv[1] before any CALL 75 ## (no PROLOGUE on _start — CALL would bury the kernel frame under 76 ## a pushed retaddr on amd64). 77 ld_r0,sp,0 ## r0 = argc 78 li_r1 %2 79 li_br &err_usage 80 blt_r0,r1 ## argc < 2 → err_usage 81 ld_r2,sp,16 ## r2 = argv[1] (survives SYSCALL) 82 83 ## Seed the frame-chain terminator before the first CALL. 84 li_r1 &stack_bottom_fp 85 mov_r0,sp 86 st_r0,r1,0 87 88 ## Align both heap bumps to their natural strides and snapshot 89 ## the aligned starts in :pair_heap_base / :obj_heap_base. The 90 ## :pair_heap_start label may land at any byte offset depending 91 ## on what the assembler emitted before it; if a pair sweep then 92 ## walked from the raw label with a fixed +16 stride it would 93 ## visit gap addresses, never the actual allocations. Pinning 94 ## the bump and the bitmap origin to the same aligned address 95 ## keeps mark and sweep on the same grid. 96 li_r1 &pair_heap_next 97 ld_r0,r1,0 98 addi_r0,r0,15 99 shri_r0,r0,4 100 shli_r0,r0,4 101 st_r0,r1,0 102 li_r1 &pair_heap_base 103 st_r0,r1,0 104 105 li_r1 &obj_heap_next 106 ld_r0,r1,0 107 addi_r0,r0,7 108 shri_r0,r0,3 109 shli_r0,r0,3 110 st_r0,r1,0 111 li_r1 &obj_heap_base 112 st_r0,r1,0 113 114 ## openat(AT_FDCWD=-100, argv[1], O_RDONLY=0, mode=0) → r0 = fd. 115 ## Use openat rather than open because open is amd64-only 116 ## (removed from the asm-generic table used by aarch64/riscv64). 117 ## LI zero-extends; low 32 bits 0xFFFFFF9C decode as -100 in int32. 118 li_r0 sys_openat 119 li_r1 %-100 120 li_r3 %0 121 li_r4 %0 122 syscall 123 124 li_br &err_open 125 bltz_r0 ## fd < 0 → err_open 126 127 ## read_file_all(fd, src_buf, SRC_BUF_SIZE) → r0 = bytes_read. 128 ## Stash fd in r4 so it survives the CALL; r4 is callee-saved. 129 mov_r4,r0 130 mov_r1,r0 131 li_r2 &src_buf 132 li_r3 %16384 ## 16384-byte capacity 133 li_br &read_file_all 134 call 135 136 ## Filling the buffer exactly means the file was ≥ SRC_BUF_SIZE — 137 ## bail out rather than silently truncate. 138 li_r1 %16384 139 li_br &err_src_too_big 140 beq_r0,r1 141 142 ## Publish src_base/src_len so the reader can walk the source, 143 ## and park (src_buf, bytes_read) in callee-saved r6/r7 so the 144 ## tuple survives primitive registration down to the eval_source 145 ## CALL below. Capture r0 into r7 NOW — sys_close below will 146 ## overwrite r0 with its own return value. 147 mov_r7,r0 ## r7 = bytes_read (captured pre-close) 148 li_r6 &src_buf ## r6 = &src_buf 149 li_r1 &src_len 150 st_r7,r1,0 151 li_r1 &src_base 152 st_r6,r1,0 153 154 ## close(fd). r4 still holds fd; SYSCALL clobbers r0 only. 155 li_r0 sys_close 156 mov_r1,r4 157 syscall 158 159 ## Intern the special-form symbols up front. eval_pair compares the 160 ## car of every compound expression against these pointers, so they 161 ## must exist before the first eval call. The intern result (r0 = 162 ## tagged sym) is stashed at &sym_* via ST_R0_R2_0 (the target addr 163 ## in r2); ST_R0_R1_0 isn't in the P1 op table. 164 li_r1 &str_quote 165 li_r2 %5 166 li_br &intern 167 call 168 li_r2 &sym_quote 169 st_r0,r2,0 170 171 li_r1 &str_if 172 li_r2 %2 173 li_br &intern 174 call 175 li_r2 &sym_if 176 st_r0,r2,0 177 178 li_r1 &str_begin 179 li_r2 %5 180 li_br &intern 181 call 182 li_r2 &sym_begin 183 st_r0,r2,0 184 185 li_r1 &str_lambda 186 li_r2 %6 187 li_br &intern 188 call 189 li_r2 &sym_lambda 190 st_r0,r2,0 191 192 li_r1 &str_define 193 li_r2 %6 194 li_br &intern 195 call 196 li_r2 &sym_define 197 st_r0,r2,0 198 199 li_r1 &str_quasiquote 200 li_r2 %10 201 li_br &intern 202 call 203 li_r2 &sym_quasiquote 204 st_r0,r2,0 205 206 li_r1 &str_unquote 207 li_r2 %7 208 li_br &intern 209 call 210 li_r2 &sym_unquote 211 st_r0,r2,0 212 213 li_r1 &str_unquote_splicing 214 li_r2 %16 215 li_br &intern 216 call 217 li_r2 &sym_unquote_splicing 218 st_r0,r2,0 219 220 li_r1 &str_set 221 li_r2 %4 222 li_br &intern 223 call 224 li_r2 &sym_set 225 st_r0,r2,0 226 227 li_r1 &str_let 228 li_r2 %3 229 li_br &intern 230 call 231 li_r2 &sym_let 232 st_r0,r2,0 233 234 li_r1 &str_letstar 235 li_r2 %4 236 li_br &intern 237 call 238 li_r2 &sym_letstar 239 st_r0,r2,0 240 241 li_r1 &str_letrec 242 li_r2 %6 243 li_br &intern 244 call 245 li_r2 &sym_letrec 246 st_r0,r2,0 247 248 li_r1 &str_cond 249 li_r2 %4 250 li_br &intern 251 call 252 li_r2 &sym_cond 253 st_r0,r2,0 254 255 li_r1 &str_else 256 li_r2 %4 257 li_br &intern 258 call 259 li_r2 &sym_else 260 st_r0,r2,0 261 262 ## Register primitives (LISP.md step 10b). Walk prim_table with 263 ## r4 = cursor, r5 = saved sym across make_primitive. Entry is 264 ## 40 bytes; zero name pointer ends the loop. 265 li_r4 &prim_table 266 :_start_reg_prim_loop 267 ld_r1,r4,0 ## r1 = name ptr 268 li_br &_start_reg_prim_done 269 beqz_r1 270 271 ld_r2,r4,8 ## r2 = length 272 li_br &intern 273 call ## r0 = tagged sym 274 mov_r5,r0 275 276 ld_r1,r4,16 ## r1 = code id 277 ld_r2,r4,32 ## r2 = arity 278 ld_r3,r4,24 ## r3 = type 279 li_br &make_primitive 280 call ## r0 = tagged prim 281 282 mov_r2,r0 283 mov_r1,r5 284 li_br &gset 285 call 286 287 addi_r4,r4,40 288 li_br &_start_reg_prim_loop 289 b 290 :_start_reg_prim_done 291 ## Evaluate the script read from argv[1]. The Makefile cats 292 ## src/prelude.scm ahead of the user script before invoking us, 293 ## so map/filter/fold are in scope by the time user code runs. 294 mov_r1,r6 295 mov_r2,r7 296 li_br &eval_source 297 call 298 mov_r4,r0 299 300 :_start_done 301 ## Print the final result (write form) + newline for visibility. 302 mov_r1,r4 303 li_br &write 304 call 305 li_r1 %10 306 li_br &putc 307 call 308 309 ## Exit status = decoded fixnum of last_result (bit-cast to low 8 310 ## bits by the kernel). Non-fixnum results still exit cleanly — 311 ## the low 8 bits of the tagged word are used. 312 li_r0 sys_exit 313 mov_r1,r4 314 sari_r1,r1,3 315 syscall 316 317 318 ## ---- read_file_all(r1=fd, r2=buf, r3=cap) -> r0 = total_bytes ------ 319 ## Read loop. r4-r7 are callee-saved AND preserved across SYSCALL, so 320 ## we can park state there across each read() call. No PROLOGUE needed: 321 ## we only issue syscalls, never CALL. 322 :read_file_all 323 mov_r4,r1 ## r4 = fd 324 mov_r5,r2 ## r5 = cur buf ptr 325 mov_r6,r3 ## r6 = remaining capacity 326 li_r7 %0 ## r7 = total_read 327 328 :read_file_all_loop 329 ## Capacity exhausted → stop; _start checks for the exact-full 330 ## case and escalates to err_src_too_big. 331 li_br &read_file_all_done 332 beqz_r6 333 334 li_r0 sys_read 335 mov_r1,r4 336 mov_r2,r5 337 mov_r3,r6 338 syscall ## r0 = n (0=EOF, <0=err) 339 340 ## n <= 0 → done. Check < 0 first, then == 0. 341 li_br &read_file_all_done 342 bltz_r0 343 li_br &read_file_all_done 344 beqz_r0 345 346 add_r5,r5,r0 ## buf += n 347 sub_r6,r6,r0 ## cap -= n 348 add_r7,r7,r0 ## total += n 349 li_br &read_file_all_loop 350 b 351 352 :read_file_all_done 353 mov_r0,r7 354 ret 355 356 357 ## ---- write_file_all(r1=fd, r2=buf, r3=len) -> r0 = total|-1 ------- 358 ## Mirror of read_file_all: write until len reaches zero or an error 359 ## occurs. Returning -1 lets write-file raise a Lisp-facing error. 360 :write_file_all 361 mov_r4,r1 ## r4 = fd 362 mov_r5,r2 ## r5 = cursor 363 mov_r6,r3 ## r6 = remaining 364 li_r7 %0 ## r7 = total_written 365 366 :write_file_all_loop 367 li_br &write_file_all_done 368 beqz_r6 369 370 li_r0 sys_write 371 mov_r1,r4 372 mov_r2,r5 373 mov_r3,r6 374 syscall ## r0 = n (<0 error, 0 unexpected stall) 375 376 li_br &write_file_all_err 377 bltz_r0 378 li_br &write_file_all_err 379 beqz_r0 380 381 add_r5,r5,r0 382 sub_r6,r6,r0 383 add_r7,r7,r0 384 li_br &write_file_all_loop 385 b 386 387 :write_file_all_done 388 mov_r0,r7 389 ret 390 391 :write_file_all_err 392 li_r0 %0 393 addi_r0,r0,neg1 394 ret 395 396 397 ## ---- eval_source(r1=base, r2=len) -> r0 = last_result -------------- 398 ## Save the current reader globals, point them at (base,len), evaluate 399 ## every top-level form, then restore the outer reader state. 400 :eval_source 401 prologue 402 st_r4,sp,24 ## save caller's r4 403 404 li_r3 &src_base 405 ld_r0,r3,0 406 li_r4 &saved_src_base 407 st_r0,r4,0 408 st_r1,r3,0 409 410 li_r3 &src_len 411 ld_r0,r3,0 412 li_r4 &saved_src_len 413 st_r0,r4,0 414 st_r2,r3,0 415 416 li_r3 &src_cursor 417 ld_r0,r3,0 418 li_r4 &saved_src_cursor 419 st_r0,r4,0 420 li_r0 %0 421 st_r0,r3,0 422 423 li_r3 &src_line 424 ld_r0,r3,0 425 li_r4 &saved_src_line 426 st_r0,r4,0 427 li_r0 %1 428 st_r0,r3,0 429 430 li_r3 &src_col 431 ld_r0,r3,0 432 li_r4 &saved_src_col 433 st_r0,r4,0 434 li_r0 %1 435 st_r0,r3,0 436 437 li_r4 %39 ## running last_result = unspec 438 439 :eval_source_loop 440 li_br &skip_ws 441 call 442 443 li_br &peek_char 444 call 445 li_r1 %0 446 addi_r1,r1,neg1 447 li_br &eval_source_done 448 beq_r0,r1 449 450 li_br &read_expr 451 call 452 mov_r1,r0 453 li_r2 %7 454 li_br &eval 455 call 456 mov_r4,r0 457 li_br &eval_source_loop 458 b 459 460 :eval_source_done 461 li_r3 &saved_src_base 462 ld_r0,r3,0 463 li_r1 &src_base 464 st_r0,r1,0 465 466 li_r3 &saved_src_len 467 ld_r0,r3,0 468 li_r1 &src_len 469 st_r0,r1,0 470 471 li_r3 &saved_src_cursor 472 ld_r0,r3,0 473 li_r1 &src_cursor 474 st_r0,r1,0 475 476 li_r3 &saved_src_line 477 ld_r0,r3,0 478 li_r1 &src_line 479 st_r0,r1,0 480 481 li_r3 &saved_src_col 482 ld_r0,r3,0 483 li_r1 &src_col 484 st_r0,r1,0 485 486 mov_r0,r4 487 ld_r4,sp,24 488 epilogue 489 ret 490 491 492 ## ---- hash(r1=ptr, r2=len) -> r0 = u64 hash -------------------------- 493 ## DJB-style: h' = 31*h + b. Implemented as (h<<5) - h + b to avoid 494 ## stashing 31 in a register. Uses r6 (zero constant) across the loop; 495 ## saves/restores it via a PROLOGUE_N1 slot. 496 :hash 497 prologue 498 st_r6,sp,24 499 500 li_r6 %0 ## zero const 501 li_r0 %0 ## h = 0 502 503 :hash_loop 504 li_br &hash_done 505 beq_r2,r6 ## len == 0 → done 506 507 shli_r3,r0,5 ## r3 = h << 5 508 sub_r3,r3,r0 ## r3 = h*32 - h = h*31 509 lb_r0,r1,0 ## r0 = *ptr (zero-extended byte) 510 add_r0,r0,r3 ## r0 = h*31 + b 511 addi_r1,r1,1 512 addi_r2,r2,neg1 513 li_br &hash_loop 514 b 515 516 :hash_done 517 ld_r6,sp,24 518 epilogue 519 ret 520 521 522 ## ---- sym_name_equal(r1=tagged_sym, r2=cmp_ptr, r3=cmp_len) -> r0 --- 523 ## Returns 1 if the symbol's stored name equals `cmp_len` bytes at 524 ## `cmp_ptr`, else 0. Uses r6 as zero const + r7 as the cmp-byte 525 ## scratch; both are callee-saved, so PROLOGUE_N2 stashes them. 526 :sym_name_equal 527 prologue_n2 528 st_r6,sp,24 529 st_r7,sp,32 530 531 addi_r1,r1,neg5 ## r1 = raw sym ptr 532 ld_r6,r1,0 ## r6 = header word 533 ## Mask low 32 bits: our names fit in 32 bits of the 48-bit length. 534 shli_r6,r6,32 535 shri_r6,r6,32 ## r6 = sym_len 536 537 li_br &sym_name_equal_neq 538 bne_r6,r3 ## lengths differ → not equal 539 540 addi_r1,r1,8 ## r1 = name bytes ptr 541 li_r6 %0 ## zero const 542 543 :sym_name_equal_loop 544 li_br &sym_name_equal_eq 545 beq_r3,r6 ## len == 0 → match 546 547 lb_r0,r1,0 ## r0 = sym byte 548 lb_r7,r2,0 ## r7 = cmp byte 549 li_br &sym_name_equal_neq 550 bne_r0,r7 551 552 addi_r1,r1,1 553 addi_r2,r2,1 554 addi_r3,r3,neg1 555 li_br &sym_name_equal_loop 556 b 557 558 :sym_name_equal_eq 559 li_r0 %1 560 li_br &sym_name_equal_done 561 b 562 563 :sym_name_equal_neq 564 li_r0 %0 565 566 :sym_name_equal_done 567 ld_r6,sp,24 568 ld_r7,sp,32 569 epilogue_n2 570 ret 571 572 573 ## ---- make_symbol(r1=src, r2=len) -> r0 = tagged symbol -------------- 574 ## Lays out header + name bytes inline, returning (raw | SYMBOL_TAG=5). 575 ## Allocation size = 8 (header) + round_up_8(len). 576 ## 577 ## Frame layout: 578 ## [sp + 8] saved r6 (caller's copy) 579 ## [sp + 16] saved r7 580 ## [sp + 24] raw_ptr across the copy loop 581 :make_symbol 582 prologue_n3 583 st_r6,sp,24 584 st_r7,sp,32 585 586 mov_r6,r1 ## r6 = src (mutated during byte copy) 587 mov_r7,r2 ## r7 = len (mutated during byte copy) 588 589 ## alloc_size = 8 + ((len + 7) >> 3 << 3) 590 addi_r1,r7,7 591 shri_r1,r1,3 592 shli_r1,r1,3 593 addi_r1,r1,8 594 li_br &alloc 595 call ## r0 = raw ptr 596 597 st_r0,sp,40 ## stash raw ptr 598 599 ## Header: store len in low 48 bits, then stamp type=2 in byte 7. 600 ld_r0,sp,40 601 st_r7,r0,0 602 li_r1 %2 603 sb_r1,r0,7 604 605 ## Byte copy: dst = r3 (= raw+8), src = r6, len = r7. 606 ld_r0,sp,40 607 addi_r3,r0,8 608 li_r1 %0 ## zero const for loop test 609 610 :make_symbol_copy 611 li_br &make_symbol_done 612 beq_r7,r1 613 614 lb_r2,r6,0 615 sb_r2,r3,0 616 addi_r6,r6,1 617 addi_r3,r3,1 618 addi_r7,r7,neg1 619 li_br &make_symbol_copy 620 b 621 622 :make_symbol_done 623 ld_r0,sp,40 624 ori_r0,r0,4 ## set bit 2 (aarch64 bitmask-imm can't do 0b101 directly) 625 ori_r0,r0,1 ## set bit 0 → tag = 101 (SYMBOL) 626 ld_r6,sp,24 627 ld_r7,sp,32 628 epilogue_n3 629 ret 630 631 632 ## ---- byte_copy(r1=dst, r2=src, r3=len) -> r0 = dst_end ------------- 633 ## Raw byte copy used by string construction, path marshaling, and 634 ## string-append. No overlap handling needed in the seed. 635 :byte_copy 636 li_r0 %0 637 :byte_copy_loop 638 li_br &byte_copy_done 639 beq_r3,r0 640 lb_r0,r2,0 641 sb_r0,r1,0 642 addi_r1,r1,1 643 addi_r2,r2,1 644 addi_r3,r3,neg1 645 li_r0 %0 646 li_br &byte_copy_loop 647 b 648 :byte_copy_done 649 mov_r0,r1 650 ret 651 652 653 ## ---- alloc_string(r1=len) -> r0 = tagged string --------------------- 654 ## Allocates header + zero or more payload bytes, stamps type=1, and 655 ## leaves the payload for the caller to fill. 656 :alloc_string 657 prologue 658 st_r1,sp,24 659 660 addi_r1,r1,7 661 shri_r1,r1,3 662 shli_r1,r1,3 663 addi_r1,r1,8 664 li_br &alloc 665 call 666 667 ld_r1,sp,24 668 st_r1,r0,0 669 li_r1 %1 670 sb_r1,r0,7 671 ori_r0,r0,4 ## tag = 100 (string) 672 epilogue 673 ret 674 675 676 ## ---- make_string(r1=src, r2=len) -> r0 = tagged string ------------- 677 :make_string 678 prologue_n3 679 st_r1,sp,24 680 st_r2,sp,32 681 682 mov_r1,r2 683 li_br &alloc_string 684 call 685 st_r0,sp,40 686 687 addi_r1,r0,neg4 ## raw string ptr 688 addi_r1,r1,8 ## dst payload 689 ld_r2,sp,24 ## src payload 690 ld_r3,sp,32 ## len 691 li_br &byte_copy 692 call 693 694 ld_r0,sp,40 695 epilogue_n3 696 ret 697 698 699 ## ---- make_vector(r1=len_raw, r2=init) -> r0 = tagged vector -------- 700 ## Allocates header + len tagged slots, stamps type=3, and fills every 701 ## element with `init`. 702 :make_vector 703 prologue_n2 704 st_r1,sp,24 705 st_r2,sp,32 706 707 shli_r1,r1,3 708 addi_r1,r1,8 709 li_br &alloc 710 call 711 712 ld_r1,sp,24 ## len_raw 713 st_r0,sp,24 ## overwrite slot1 = raw ptr 714 st_r1,r0,0 715 li_r1 %3 716 sb_r1,r0,7 717 718 ld_r1,sp,24 ## raw ptr 719 ld_r2,sp,32 ## init 720 ld_r0,r1,0 ## header word (type|len) 721 shli_r0,r0,16 ## mask off type byte 722 shri_r0,r0,16 ## r0 = len_raw 723 addi_r1,r1,8 ## payload cursor 724 li_r3 %0 725 :make_vector_loop 726 li_br &make_vector_done 727 beq_r0,r3 728 st_r2,r1,0 729 addi_r1,r1,8 730 addi_r0,r0,neg1 731 li_br &make_vector_loop 732 b 733 :make_vector_done 734 ld_r0,sp,24 735 ori_r0,r0,2 736 ori_r0,r0,1 ## tag = 011 (vector) 737 epilogue_n2 738 ret 739 740 741 ## ---- string_to_c_path(r1=tagged_string) -> r0 = &path_buf ---------- 742 ## Copies the string bytes to a shared NUL-terminated buffer so openat 743 ## can consume it as a kernel pathname. 744 :string_to_c_path 745 prologue_n2 746 addi_r1,r1,neg4 ## raw string ptr 747 ld_r2,r1,0 748 shli_r2,r2,16 749 shri_r2,r2,16 ## r2 = len 750 mov_r3,r2 751 li_r0 %256 ## PATH_BUF_SIZE = 256 752 li_br &string_to_c_path_ok 753 blt_r3,r0 754 li_br &err_path_too_long 755 b 756 :string_to_c_path_ok 757 mov_r0,sp 758 st_r2,r0,24 759 addi_r1,r1,8 ## payload ptr 760 st_r1,r0,32 761 li_r1 &path_buf 762 ld_r2,r0,32 763 ld_r3,r0,24 764 li_br &byte_copy 765 call 766 li_r1 %0 767 sb_r1,r0,0 768 li_r0 &path_buf 769 epilogue_n2 770 ret 771 772 773 ## ---- intern(r1=name_ptr, r2=name_len) -> r0 = tagged sym ----------- 774 ## Open-addressing linear probe over a 4096-slot table. Empty slot = 775 ## 0 word (nil is 0x07, never 0, so this sentinel is unambiguous). 776 ## Frame layout: slot 1 = saved r6, slot 2 = saved r7, slot 3 = current 777 ## probe index h. 778 :intern 779 prologue_n3 780 st_r6,sp,24 781 st_r7,sp,32 782 783 mov_r6,r1 ## r6 = name_ptr (callee-saved copy) 784 mov_r7,r2 ## r7 = name_len 785 786 li_br &hash 787 call ## r0 = 64-bit hash 788 789 shli_r0,r0,52 790 shri_r0,r0,52 ## r0 = h & 4095 791 792 mov_r3,sp 793 st_r0,sp,40 ## slot 3 = h 794 795 :intern_probe 796 ld_r0,sp,40 797 shli_r0,r0,3 ## r0 = h * 8 798 li_r2 &symbol_table 799 add_r2,r2,r0 ## r2 = &symbol_table[h] 800 ld_r3,r2,0 ## r3 = slot value 801 802 li_br &intern_empty 803 beqz_r3 ## slot == 0 → allocate 804 805 ## Compare existing symbol to (r6, r7). 806 mov_r1,r3 ## r1 = tagged sym 807 mov_r2,r6 ## r2 = cmp_ptr 808 mov_r3,r7 ## r3 = cmp_len 809 li_br &sym_name_equal 810 call ## r0 = 0 or 1 811 812 li_br &intern_hit 813 bnez_r0 814 815 ## Advance h = (h+1) & 4095. 816 ld_r0,sp,40 817 addi_r0,r0,1 818 shli_r0,r0,52 819 shri_r0,r0,52 820 st_r0,sp,40 821 li_br &intern_probe 822 b 823 824 :intern_empty 825 ## Allocate a fresh symbol and plant it in the slot. 826 mov_r1,r6 827 mov_r2,r7 828 li_br &make_symbol 829 call ## r0 = tagged sym 830 831 ld_r1,sp,40 832 shli_r1,r1,3 833 li_r2 &symbol_table 834 add_r2,r2,r1 835 st_r0,r2,0 ## write into slot 836 837 li_br &intern_done 838 b 839 840 :intern_hit 841 mov_r3,sp 842 ld_r0,sp,40 843 shli_r0,r0,3 844 li_r2 &symbol_table 845 add_r2,r2,r0 846 ld_r0,r2,0 ## r0 = existing tagged sym 847 848 :intern_done 849 ld_r6,sp,24 850 ld_r7,sp,32 851 epilogue_n3 852 ret 853 854 855 ## ---- cons / car / cdr (preserved from step 2) ----------------------- 856 :cons 857 prologue_n2 858 st_r1,sp,24 859 st_r2,sp,32 860 li_br &pair_alloc 861 call 862 ld_r1,sp,24 863 st_r1,r0,0 864 ld_r2,sp,32 865 st_r2,r0,8 866 ori_r0,r0,2 867 epilogue_n2 868 ret 869 870 :car 871 addi_r1,r1,neg2 872 ld_r0,r1,0 873 ret 874 875 :cdr 876 addi_r1,r1,neg2 877 ld_r0,r1,8 878 ret 879 880 881 ## ---- obj_freelist_for_size(size) -> &head cell or 0 ----------------- 882 :obj_freelist_for_size 883 li_r0 %16 884 li_br &obj_freelist_16 885 beq_r1,r0 886 li_r0 %24 887 li_br &obj_freelist_24 888 beq_r1,r0 889 li_r0 %32 890 li_br &obj_freelist_32 891 beq_r1,r0 892 li_r0 %40 893 li_br &obj_freelist_40 894 beq_r1,r0 895 li_r0 %48 896 li_br &obj_freelist_48 897 beq_r1,r0 898 li_r0 %56 899 li_br &obj_freelist_56 900 beq_r1,r0 901 li_r0 %64 902 li_br &obj_freelist_64 903 beq_r1,r0 904 li_r0 %80 905 li_br &obj_freelist_80 906 beq_r1,r0 907 li_r0 %96 908 li_br &obj_freelist_96 909 beq_r1,r0 910 li_r0 %128 911 li_br &obj_freelist_128 912 beq_r1,r0 913 li_r0 %0 914 ret 915 :obj_freelist_16 916 li_r0 &free_list_obj16 917 ret 918 :obj_freelist_24 919 li_r0 &free_list_obj24 920 ret 921 :obj_freelist_32 922 li_r0 &free_list_obj32 923 ret 924 :obj_freelist_40 925 li_r0 &free_list_obj40 926 ret 927 :obj_freelist_48 928 li_r0 &free_list_obj48 929 ret 930 :obj_freelist_56 931 li_r0 &free_list_obj56 932 ret 933 :obj_freelist_64 934 li_r0 &free_list_obj64 935 ret 936 :obj_freelist_80 937 li_r0 &free_list_obj80 938 ret 939 :obj_freelist_96 940 li_r0 &free_list_obj96 941 ret 942 :obj_freelist_128 943 li_r0 &free_list_obj128 944 ret 945 946 947 ## ---- pair_alloc() -> raw pair ptr ----------------------------------- 948 :pair_alloc 949 prologue 950 951 li_r3 &free_list_pair 952 ld_r0,r3,0 953 li_br &pair_alloc_bump 954 beqz_r0 955 ld_r1,r0,0 956 st_r1,r3,0 957 epilogue 958 ret 959 960 :pair_alloc_bump 961 li_r3 &pair_heap_next 962 ld_r0,r3,0 963 addi_r0,r0,7 964 shri_r0,r0,3 965 shli_r0,r0,3 966 addi_r2,r0,16 967 li_r1 &pair_heap_end 968 li_br &pair_alloc_need_gc 969 blt_r1,r2 970 st_r2,r3,0 971 epilogue 972 ret 973 974 ## GC, then retry. Re-checks the free list first because the sweep 975 ## populates it with reclaimed pairs; only falls through to the bump 976 ## path if the list is still empty. A second overflow goes to OOM. 977 :pair_alloc_need_gc 978 li_br &gc 979 call 980 li_r3 &free_list_pair 981 ld_r0,r3,0 982 li_br &pair_alloc_retry_bump 983 beqz_r0 984 ld_r1,r0,0 985 st_r1,r3,0 986 epilogue 987 ret 988 989 :pair_alloc_retry_bump 990 li_r3 &pair_heap_next 991 ld_r0,r3,0 992 addi_r0,r0,7 993 shri_r0,r0,3 994 shli_r0,r0,3 995 addi_r2,r0,16 996 li_r1 &pair_heap_end 997 li_br &alloc_oom 998 blt_r1,r2 999 st_r2,r3,0 1000 epilogue 1001 ret 1002 1003 1004 ## ---- alloc / obj_alloc(size) -> raw object ptr ---------------------- 1005 :alloc 1006 :obj_alloc 1007 prologue_n2 1008 1009 addi_r1,r1,7 1010 shri_r1,r1,3 1011 shli_r1,r1,3 1012 st_r1,sp,24 ## slot 1 = rounded size 1013 1014 li_br &obj_freelist_for_size 1015 call 1016 st_r0,sp,32 ## slot 2 = &free_list head or 0 1017 1018 li_br &obj_alloc_bump 1019 beqz_r0 1020 ld_r1,r0,0 1021 li_br &obj_alloc_bump 1022 beqz_r1 1023 ld_r2,r1,8 1024 ld_r3,sp,32 1025 st_r2,r3,0 1026 mov_r0,r1 1027 epilogue_n2 1028 ret 1029 1030 :obj_alloc_bump 1031 ld_r1,sp,24 1032 li_r3 &obj_heap_next 1033 ld_r0,r3,0 1034 addi_r0,r0,7 1035 shri_r0,r0,3 1036 shli_r0,r0,3 1037 add_r2,r0,r1 1038 li_r1 &obj_heap_end 1039 li_br &obj_alloc_need_gc 1040 blt_r1,r2 1041 st_r2,r3,0 1042 epilogue_n2 1043 ret 1044 1045 ## GC, then retry. Slot 2 still holds the size-class freelist head 1046 ## (or 0 when no class fits this size). After the sweep, re-check 1047 ## that list before falling through to the bump path. Second overflow 1048 ## escalates to OOM. 1049 :obj_alloc_need_gc 1050 li_br &gc 1051 call 1052 ld_r3,sp,32 ## &head (or 0 = no size class) 1053 li_br &obj_alloc_retry_bump 1054 beqz_r3 1055 ld_r1,r3,0 1056 li_br &obj_alloc_retry_bump 1057 beqz_r1 1058 ld_r2,r1,8 1059 st_r2,r3,0 1060 mov_r0,r1 1061 epilogue_n2 1062 ret 1063 1064 :obj_alloc_retry_bump 1065 ld_r1,sp,24 1066 li_r3 &obj_heap_next 1067 ld_r0,r3,0 1068 addi_r0,r0,7 1069 shri_r0,r0,3 1070 shli_r0,r0,3 1071 add_r2,r0,r1 1072 li_r1 &obj_heap_end 1073 li_br &alloc_oom 1074 blt_r1,r2 1075 st_r2,r3,0 1076 epilogue_n2 1077 ret 1078 1079 1080 ## ---- obj_size_bytes(raw obj ptr) -> r0 = rounded chunk size --------- 1081 :obj_size_bytes 1082 lb_r2,r1,7 1083 li_r0 %0 1084 li_br &obj_size_free 1085 beq_r2,r0 1086 li_r0 %1 1087 li_br &obj_size_string 1088 beq_r2,r0 1089 li_r0 %2 1090 li_br &obj_size_symbol 1091 beq_r2,r0 1092 li_r0 %3 1093 li_br &obj_size_vector 1094 beq_r2,r0 1095 li_r0 %4 1096 li_br &obj_size_closure 1097 beq_r2,r0 1098 li_r0 %5 1099 li_br &obj_size_primitive 1100 beq_r2,r0 1101 li_r0 %6 1102 li_br &obj_size_primitive 1103 beq_r2,r0 1104 li_r0 %16 1105 ret 1106 1107 :obj_size_free 1108 ld_r0,r1,0 1109 shli_r0,r0,16 1110 shri_r0,r0,16 1111 ret 1112 1113 :obj_size_string 1114 :obj_size_symbol 1115 ld_r0,r1,0 1116 shli_r0,r0,16 1117 shri_r0,r0,16 1118 addi_r0,r0,15 1119 shri_r0,r0,3 1120 shli_r0,r0,3 1121 ret 1122 1123 :obj_size_vector 1124 ld_r0,r1,0 1125 shli_r0,r0,16 1126 shri_r0,r0,16 1127 shli_r0,r0,3 1128 addi_r0,r0,8 1129 ret 1130 1131 :obj_size_closure 1132 li_r0 %32 1133 ret 1134 1135 :obj_size_primitive 1136 li_r0 %16 1137 ret 1138 1139 1140 ## ---- obj_is_live_marked(raw obj ptr) -> r0 = 1 iff marked live ----- 1141 :obj_is_live_marked 1142 lb_r2,r1,7 1143 li_br &obj_is_dead 1144 beqz_r2 1145 lb_r2,r1,6 1146 andi_r2,r2,1 1147 li_br &obj_is_dead 1148 beqz_r2 1149 li_r0 %1 1150 ret 1151 :obj_is_dead 1152 li_r0 %0 1153 ret 1154 1155 1156 ## ---- obj_mark_seen_or_set(raw obj ptr) -> r0 = 1 iff already marked - 1157 :obj_mark_seen_or_set 1158 lb_r2,r1,6 1159 andi_r3,r2,1 1160 li_br &obj_mark_seen 1161 bnez_r3 1162 ori_r2,r2,1 1163 sb_r2,r1,6 1164 li_r0 %0 1165 ret 1166 :obj_mark_seen 1167 li_r0 %1 1168 ret 1169 1170 1171 ## ---- pair mark-bitmap helpers --------------------------------------- 1172 :pair_mark_seen_or_set 1173 li_r2 &pair_heap_base 1174 ld_r2,r2,0 1175 sub_r2,r1,r2 1176 shri_r2,r2,4 ## slot index 1177 mov_r3,r2 1178 shri_r3,r3,3 ## byte index 1179 li_r0 &pair_mark_bitmap 1180 add_r0,r0,r3 ## r0 = byte ptr 1181 lb_r3,r0,0 ## r3 = byte 1182 andi_r1,r2,7 ## r1 = bit index 1183 li_r2 %1 1184 shl_r2,r2,r1 ## r2 = mask 1185 mov_r1,r3 1186 and_r1,r1,r2 1187 li_br &pair_mark_seen 1188 bnez_r1 1189 or_r3,r3,r2 1190 sb_r3,r0,0 1191 li_r0 %0 1192 ret 1193 :pair_mark_seen 1194 li_r0 %1 1195 ret 1196 1197 :pair_mark_test 1198 li_r2 &pair_heap_base 1199 ld_r2,r2,0 1200 sub_r2,r1,r2 1201 shri_r2,r2,4 1202 mov_r3,r2 1203 shri_r3,r3,3 1204 li_r0 &pair_mark_bitmap 1205 add_r0,r0,r3 1206 lb_r3,r0,0 1207 andi_r1,r2,7 1208 li_r2 %1 1209 shl_r2,r2,r1 1210 and_r3,r3,r2 1211 li_r0 %0 1212 li_br &pair_mark_test_done 1213 beqz_r3 1214 li_r0 %1 1215 :pair_mark_test_done 1216 ret 1217 1218 :pair_mark_clear 1219 li_r2 &pair_heap_base 1220 ld_r2,r2,0 1221 sub_r2,r1,r2 1222 shri_r2,r2,4 1223 mov_r3,r2 1224 shri_r3,r3,3 1225 li_r0 &pair_mark_bitmap 1226 add_r0,r0,r3 1227 lb_r3,r0,0 1228 andi_r1,r2,7 1229 li_r2 %1 1230 shl_r2,r2,r1 1231 sub_r3,r3,r2 1232 sb_r3,r0,0 1233 ret 1234 1235 1236 ## ---- mark_push(tagged value) ---------------------------------------- 1237 :mark_push 1238 li_r2 &mark_stack_next 1239 ld_r3,r2,0 1240 li_r0 &mark_stack_end 1241 li_br &mark_push_recurse 1242 beq_r3,r0 1243 st_r1,r3,0 1244 addi_r3,r3,8 1245 st_r3,r2,0 1246 ret 1247 ## Tail-branch (not CALL) so mark_value returns directly to mark_push's 1248 ## caller. A CALL+RET here would loop on aarch64/riscv64: their native 1249 ## CALL writes retaddr to LR rather than pushing it, so the inner CALL 1250 ## clobbers this routine's own incoming LR and the final RET branches 1251 ## back to itself. mark_push has no prologue and thus no place to spill 1252 ## LR, so tail-branching is the correct shape. 1253 :mark_push_recurse 1254 li_br &mark_value 1255 b 1256 1257 1258 ## ---- mark_value(tagged value) --------------------------------------- 1259 :mark_value 1260 prologue_n4 1261 1262 andi_r0,r1,7 1263 li_r2 %2 1264 li_br &mark_value_pair 1265 beq_r0,r2 1266 li_r2 %3 1267 li_br &mark_value_object 1268 beq_r0,r2 1269 li_r2 %4 1270 li_br &mark_value_object 1271 beq_r0,r2 1272 li_r2 %5 1273 li_br &mark_value_object 1274 beq_r0,r2 1275 li_r2 %6 1276 li_br &mark_value_object 1277 beq_r0,r2 1278 epilogue_n4 1279 ret 1280 1281 ## Tagged-pair bounds check: a frame slot may hold a raw integer whose 1282 ## low 3 bits coincide with the pair tag (e.g. a small loop counter). 1283 ## Reject anything outside [pair_heap_start, pair_heap_next) so the 1284 ## bitmap index stays in range and dereferencing stays inside our arena. 1285 :mark_value_pair 1286 addi_r1,r1,neg2 ## r1 = raw pair ptr 1287 li_r0 &pair_heap_base 1288 ld_r0,r0,0 1289 li_br &mark_value_done 1290 blt_r1,r0 1291 li_r0 &pair_heap_next 1292 ld_r0,r0,0 1293 li_br &mark_value_pair_inb 1294 blt_r1,r0 1295 li_br &mark_value_done 1296 b 1297 :mark_value_pair_inb 1298 st_r1,sp,24 ## slot 1 = raw pair ptr 1299 li_br &pair_mark_seen_or_set 1300 call 1301 li_br &mark_value_done 1302 bnez_r0 1303 1304 ld_r1,sp,24 1305 ld_r0,r1,0 1306 st_r0,sp,32 ## slot 2 = car 1307 ld_r1,r1,8 1308 li_br &mark_push 1309 call 1310 ld_r1,sp,32 1311 li_br &mark_push 1312 call 1313 li_br &mark_value_done 1314 b 1315 1316 ## Same bounds-check rationale as mark_value_pair. Reject anything 1317 ## outside [obj_heap_start, obj_heap_next) before touching the header 1318 ## byte, so a stale raw pointer in a frame slot can't crash the marker. 1319 :mark_value_object 1320 andi_r0,r1,7 1321 sub_r1,r1,r0 ## raw object ptr 1322 li_r0 &obj_heap_base 1323 ld_r0,r0,0 1324 li_br &mark_value_done 1325 blt_r1,r0 1326 li_r0 &obj_heap_next 1327 ld_r0,r0,0 1328 li_br &mark_value_object_inb 1329 blt_r1,r0 1330 li_br &mark_value_done 1331 b 1332 :mark_value_object_inb 1333 st_r1,sp,24 ## slot 1 = raw object ptr 1334 li_br &obj_mark_seen_or_set 1335 call 1336 li_br &mark_value_done 1337 bnez_r0 1338 1339 ld_r1,sp,24 1340 lb_r0,r1,7 1341 li_r2 %3 1342 li_br &mark_value_vector 1343 beq_r0,r2 1344 li_r2 %4 1345 li_br &mark_value_closure 1346 beq_r0,r2 1347 li_br &mark_value_done 1348 b 1349 1350 :mark_value_vector 1351 ld_r0,r1,0 1352 shli_r0,r0,16 1353 shri_r0,r0,16 1354 addi_r2,r1,8 1355 st_r2,sp,24 ## slot 1 = cursor 1356 st_r0,sp,32 ## slot 2 = remaining 1357 1358 :mark_value_vector_loop 1359 ld_r0,sp,32 1360 li_br &mark_value_done 1361 beqz_r0 1362 ld_r2,sp,24 1363 ld_r1,r2,0 1364 li_br &mark_push 1365 call 1366 ld_r2,sp,24 1367 addi_r2,r2,8 1368 st_r2,sp,24 1369 ld_r0,sp,32 1370 addi_r0,r0,neg1 1371 st_r0,sp,32 1372 li_br &mark_value_vector_loop 1373 b 1374 1375 :mark_value_closure 1376 ld_r0,r1,16 1377 st_r0,sp,32 ## slot 2 = body 1378 ld_r0,r1,24 1379 st_r0,sp,40 ## slot 3 = env 1380 ld_r1,r1,8 1381 li_br &mark_push 1382 call 1383 ld_r1,sp,32 1384 li_br &mark_push 1385 call 1386 ld_r1,sp,40 1387 li_br &mark_push 1388 call 1389 1390 :mark_value_done 1391 epilogue_n4 1392 ret 1393 1394 1395 ## ---- mark_frame_slots(raw frame ptr) -------------------------------- 1396 :mark_frame_slots 1397 prologue_n2 1398 ld_r0,r1,16 1399 addi_r2,r1,24 1400 st_r2,sp,24 ## slot 1 = cursor 1401 st_r0,sp,32 ## slot 2 = remaining 1402 1403 :mark_frame_slots_loop 1404 ld_r0,sp,32 1405 li_br &mark_frame_slots_done 1406 beqz_r0 1407 ld_r2,sp,24 1408 ld_r1,r2,0 1409 li_br &mark_value 1410 call 1411 ld_r2,sp,24 1412 addi_r2,r2,8 1413 st_r2,sp,24 1414 ld_r0,sp,32 1415 addi_r0,r0,neg1 1416 st_r0,sp,32 1417 li_br &mark_frame_slots_loop 1418 b 1419 1420 :mark_frame_slots_done 1421 epilogue_n2 1422 ret 1423 1424 1425 ## ---- gc_mark_symbol_table() ----------------------------------------- 1426 :gc_mark_symbol_table 1427 prologue 1428 li_r1 &symbol_table 1429 st_r1,sp,24 1430 1431 :gc_mark_symbol_table_loop 1432 ld_r2,sp,24 1433 li_r0 &symbol_table_end 1434 li_br &gc_mark_symbol_table_done 1435 beq_r2,r0 1436 ld_r1,r2,0 1437 li_br &mark_value 1438 call 1439 ld_r2,sp,24 1440 addi_r2,r2,8 1441 st_r2,sp,24 1442 li_br &gc_mark_symbol_table_loop 1443 b 1444 1445 :gc_mark_symbol_table_done 1446 epilogue 1447 ret 1448 1449 1450 ## ---- gc_mark_prim_argv() -------------------------------------------- 1451 ## Bound by :prim_argc, the slot count marshal_argv last published. 1452 ## Walking past it would re-mark stale tagged values left over from 1453 ## earlier primitive calls, falsely keeping the objects they pointed 1454 ## to alive across an arbitrary number of GCs. 1455 :gc_mark_prim_argv 1456 prologue_n2 1457 li_r1 &prim_argv 1458 st_r1,sp,24 ## slot 1 = cursor 1459 li_r1 &prim_argc 1460 ld_r1,r1,0 1461 st_r1,sp,32 ## slot 2 = remaining slots 1462 1463 :gc_mark_prim_argv_loop 1464 ld_r0,sp,32 1465 li_br &gc_mark_prim_argv_done 1466 beqz_r0 1467 ld_r2,sp,24 1468 ld_r1,r2,0 1469 li_br &mark_value 1470 call 1471 ld_r2,sp,24 1472 addi_r2,r2,8 1473 st_r2,sp,24 1474 ld_r0,sp,32 1475 addi_r0,r0,neg1 1476 st_r0,sp,32 1477 li_br &gc_mark_prim_argv_loop 1478 b 1479 1480 :gc_mark_prim_argv_done 1481 epilogue_n2 1482 ret 1483 1484 1485 ## ---- gc_mark_stack_roots() ------------------------------------------ 1486 :gc_mark_stack_roots 1487 prologue_n2 1488 li_r1 &gc_root_fp 1489 ld_r1,r1,0 1490 st_r1,sp,24 ## slot 1 = current frame ptr 1491 li_r1 &stack_bottom_fp 1492 ld_r1,r1,0 1493 st_r1,sp,32 ## slot 2 = bottom caller sp 1494 1495 :gc_mark_stack_roots_loop 1496 ld_r1,sp,24 1497 li_br &gc_mark_stack_roots_done 1498 beqz_r1 1499 li_br &mark_frame_slots 1500 call 1501 ld_r1,sp,24 1502 ld_r1,r1,8 1503 st_r1,sp,24 1504 ld_r0,sp,32 1505 li_br &gc_mark_stack_roots_done 1506 beq_r1,r0 1507 li_br &gc_mark_stack_roots_loop 1508 b 1509 1510 :gc_mark_stack_roots_done 1511 epilogue_n2 1512 ret 1513 1514 1515 ## ---- gc_mark_all() --------------------------------------------------- 1516 :gc_mark_all 1517 prologue 1518 li_r1 &mark_stack 1519 li_r2 &mark_stack_next 1520 st_r1,r2,0 1521 1522 li_r1 &global_env_cell 1523 ld_r1,r1,0 1524 li_br &mark_value 1525 call 1526 1527 li_br &gc_mark_symbol_table 1528 call 1529 li_br &gc_mark_prim_argv 1530 call 1531 li_br &gc_mark_stack_roots 1532 call 1533 1534 :gc_mark_drain 1535 li_r2 &mark_stack_next 1536 ld_r0,r2,0 1537 li_r1 &mark_stack 1538 li_br &gc_mark_done 1539 beq_r0,r1 1540 addi_r0,r0,neg8 1541 st_r0,r2,0 1542 ld_r1,r0,0 1543 li_br &mark_value 1544 call 1545 li_br &gc_mark_drain 1546 b 1547 1548 :gc_mark_done 1549 epilogue 1550 ret 1551 1552 1553 ## ---- gc_clear_freelists() ------------------------------------------- 1554 :gc_clear_freelists 1555 li_r0 %0 1556 li_r1 &free_list_pair 1557 st_r0,r1,0 1558 li_r1 &free_list_obj16 1559 st_r0,r1,0 1560 li_r1 &free_list_obj24 1561 st_r0,r1,0 1562 li_r1 &free_list_obj32 1563 st_r0,r1,0 1564 li_r1 &free_list_obj40 1565 st_r0,r1,0 1566 li_r1 &free_list_obj48 1567 st_r0,r1,0 1568 li_r1 &free_list_obj56 1569 st_r0,r1,0 1570 li_r1 &free_list_obj64 1571 st_r0,r1,0 1572 li_r1 &free_list_obj80 1573 st_r0,r1,0 1574 li_r1 &free_list_obj96 1575 st_r0,r1,0 1576 li_r1 &free_list_obj128 1577 st_r0,r1,0 1578 ret 1579 1580 1581 ## ---- gc_sweep_pair() ------------------------------------------------- 1582 :gc_sweep_pair 1583 prologue 1584 li_r1 &pair_heap_base 1585 ld_r1,r1,0 1586 st_r1,sp,24 1587 1588 :gc_sweep_pair_loop 1589 ld_r1,sp,24 1590 li_r0 &pair_heap_next 1591 ld_r0,r0,0 1592 li_br &gc_sweep_pair_done 1593 beq_r1,r0 1594 1595 li_br &pair_mark_test 1596 call 1597 li_br &gc_sweep_pair_dead 1598 beqz_r0 1599 1600 ld_r1,sp,24 1601 li_br &pair_mark_clear 1602 call 1603 li_br &gc_sweep_pair_advance 1604 b 1605 1606 :gc_sweep_pair_dead 1607 ld_r1,sp,24 1608 li_r2 &free_list_pair 1609 ld_r0,r2,0 1610 st_r0,r1,0 1611 st_r1,r2,0 1612 1613 :gc_sweep_pair_advance 1614 ld_r1,sp,24 1615 addi_r1,r1,16 1616 st_r1,sp,24 1617 li_br &gc_sweep_pair_loop 1618 b 1619 1620 :gc_sweep_pair_done 1621 epilogue 1622 ret 1623 1624 1625 ## ---- gc_sweep_obj() -------------------------------------------------- 1626 :gc_sweep_obj 1627 prologue_n4 1628 li_r1 &obj_heap_base 1629 ld_r1,r1,0 1630 st_r1,sp,24 ## slot 1 = current ptr 1631 1632 :gc_sweep_obj_loop 1633 ld_r1,sp,24 1634 li_r0 &obj_heap_next 1635 ld_r0,r0,0 1636 li_br &gc_sweep_obj_done 1637 beq_r1,r0 1638 1639 li_br &obj_is_live_marked 1640 call 1641 li_br &gc_sweep_obj_dead 1642 beqz_r0 1643 1644 ld_r1,sp,24 1645 lb_r0,r1,6 1646 addi_r0,r0,neg1 1647 sb_r0,r1,6 1648 li_br &obj_size_bytes 1649 call 1650 ld_r1,sp,24 1651 add_r1,r1,r0 1652 st_r1,sp,24 1653 li_br &gc_sweep_obj_loop 1654 b 1655 1656 :gc_sweep_obj_dead 1657 ld_r1,sp,24 1658 li_br &obj_size_bytes 1659 call 1660 ld_r1,sp,24 1661 add_r2,r1,r0 1662 st_r2,sp,32 ## slot 2 = run end 1663 1664 :gc_sweep_obj_dead_scan 1665 ld_r2,sp,32 1666 li_r0 &obj_heap_next 1667 ld_r0,r0,0 1668 li_br &gc_sweep_obj_tail_dead 1669 beq_r2,r0 1670 1671 mov_r1,r2 1672 li_br &obj_is_live_marked 1673 call 1674 li_br &gc_sweep_obj_dead_run_ready 1675 bnez_r0 1676 1677 ld_r1,sp,32 1678 li_br &obj_size_bytes 1679 call 1680 ld_r2,sp,32 1681 add_r2,r2,r0 1682 st_r2,sp,32 1683 li_br &gc_sweep_obj_dead_scan 1684 b 1685 1686 :gc_sweep_obj_tail_dead 1687 ld_r1,sp,24 1688 li_r0 &obj_heap_next 1689 st_r1,r0,0 1690 li_br &gc_sweep_obj_done 1691 b 1692 1693 :gc_sweep_obj_dead_run_ready 1694 ld_r1,sp,32 1695 ld_r2,sp,24 1696 sub_r1,r1,r2 ## r1 = run size 1697 st_r1,sp,40 ## slot 3 = run size 1698 li_br &obj_freelist_for_size 1699 call 1700 st_r0,sp,48 ## slot 4 = &free_list head or 0 1701 1702 li_br &gc_sweep_obj_write_pseudo 1703 beqz_r0 1704 1705 ld_r2,sp,24 ## run start 1706 ld_r1,sp,40 ## run size 1707 st_r1,r2,0 1708 ld_r3,sp,48 1709 ld_r0,r3,0 1710 st_r0,r2,8 1711 st_r2,r3,0 1712 li_br &gc_sweep_obj_advance 1713 b 1714 1715 :gc_sweep_obj_write_pseudo 1716 ld_r2,sp,24 1717 ld_r1,sp,40 1718 st_r1,r2,0 1719 1720 :gc_sweep_obj_advance 1721 ld_r1,sp,32 1722 st_r1,sp,24 1723 li_br &gc_sweep_obj_loop 1724 b 1725 1726 :gc_sweep_obj_done 1727 epilogue_n4 1728 ret 1729 1730 1731 ## ---- gc_sweep_all() -------------------------------------------------- 1732 :gc_sweep_all 1733 prologue 1734 li_br &gc_clear_freelists 1735 call 1736 li_br &gc_sweep_pair 1737 call 1738 li_br &gc_sweep_obj 1739 call 1740 epilogue 1741 ret 1742 1743 1744 ## ---- gc() ------------------------------------------------------------ 1745 :gc 1746 prologue 1747 ld_r1,sp,8 ## metadata: caller frame ptr 1748 li_r2 &gc_root_fp 1749 st_r1,r2,0 1750 li_br &gc_mark_all 1751 call 1752 li_br &gc_sweep_all 1753 call 1754 epilogue 1755 ret 1756 1757 1758 ## ---- error(msg_ptr, msg_len) ---------------------------------------- 1759 :error 1760 mov_r6,r1 1761 mov_r7,r2 1762 1763 li_r0 sys_write 1764 li_r1 %2 1765 li_r2 &msg_error_prefix 1766 li_r3 %7 1767 syscall 1768 1769 li_r0 sys_write 1770 li_r1 %2 1771 mov_r2,r6 1772 mov_r3,r7 1773 syscall 1774 1775 li_r0 sys_write 1776 li_r1 %2 1777 li_r2 &msg_newline 1778 li_r3 %1 1779 syscall 1780 1781 li_r0 sys_exit 1782 li_r1 %1 1783 syscall 1784 1785 1786 ## ---- Error landing pads --------------------------------------------- 1787 :alloc_oom 1788 li_r1 &msg_oom 1789 li_r2 %14 1790 li_br &error 1791 b 1792 1793 :err_intern_not_eq 1794 li_r1 &msg_intern_not_eq 1795 li_r2 %34 ## strlen == 34 1796 li_br &error 1797 b 1798 1799 :err_intern_collision 1800 li_r1 &msg_intern_collision 1801 li_r2 %44 ## strlen == 44 1802 li_br &error 1803 b 1804 1805 :err_usage 1806 li_r1 &msg_usage 1807 li_r2 %22 ## strlen("usage: lisp <file.scm>") == 22 1808 li_br &error 1809 b 1810 1811 :err_open 1812 li_r1 &msg_open_fail 1813 li_r2 %26 ## strlen("failed to open source file") == 26 1814 li_br &error 1815 b 1816 1817 :err_src_too_big 1818 li_r1 &msg_src_too_big 1819 li_r2 %21 ## strlen("source file too large") == 21 1820 li_br &error 1821 b 1822 1823 ## err_reader_bad: "error: reader: malformed input at LINE:COL\n" → stderr. 1824 ## Doesn't go through :error because we want line:col tacked on; easier to 1825 ## inline the syscalls than thread a format through error(). 1826 :err_reader_bad 1827 ## "error: " 1828 li_r0 sys_write 1829 li_r1 %2 1830 li_r2 &msg_error_prefix 1831 li_r3 %7 1832 syscall 1833 1834 ## "reader: malformed input" 1835 li_r0 sys_write 1836 li_r1 %2 1837 li_r2 &msg_reader_bad 1838 li_r3 %23 ## strlen("reader: malformed input") == 23 1839 syscall 1840 1841 ## " at " 1842 li_r0 sys_write 1843 li_r1 %2 1844 li_r2 &msg_at 1845 li_r3 %4 1846 syscall 1847 1848 ## LINE (via display_uint to stderr) 1849 li_r1 &src_line 1850 ld_r1,r1,0 1851 li_r2 %2 1852 li_br &display_uint 1853 call 1854 1855 ## ":" 1856 li_r0 sys_write 1857 li_r1 %2 1858 li_r2 &msg_colon 1859 li_r3 %1 1860 syscall 1861 1862 ## COL 1863 li_r1 &src_col 1864 ld_r1,r1,0 1865 li_r2 %2 1866 li_br &display_uint 1867 call 1868 1869 ## "\n" 1870 li_r0 sys_write 1871 li_r1 %2 1872 li_r2 &msg_newline 1873 li_r3 %1 1874 syscall 1875 1876 li_r0 sys_exit 1877 li_r1 %1 1878 syscall 1879 1880 1881 ## ================ Step 4: reader + minimal display ================== 1882 ## Recursive-descent over an in-memory source string at src_base with 1883 ## length src_len. Read one expression. Supports: lists, positive 1884 ## decimal fixnums, symbols (interned). Skips whitespace and `;` line 1885 ## comments. No quote, strings, source-locations, or improper lists 1886 ## in this round — those land in later steps. 1887 1888 1889 ## ---- Reader state (globals) ----------------------------------------- 1890 ## src_base/src_len are set by _start after it reads argv[1] into 1891 ## src_buf. src_cursor starts at 0; src_line/src_col start at 1. 1892 :src_base %0 %0 1893 :src_len %0 %0 1894 :src_cursor %0 %0 1895 :src_line %1 %0 1896 :src_col %1 %0 1897 1898 1899 ## ---- peek_char() -> r0 = char (0..255) or -1 on EOF ---------------- 1900 :peek_char 1901 li_r1 &src_cursor 1902 ld_r1,r1,0 ## r1 = cursor 1903 li_r2 &src_len 1904 ld_r2,r2,0 ## r2 = len 1905 li_br &peek_char_inb 1906 blt_r1,r2 ## cursor < len → in-bounds 1907 li_r0 %0 1908 addi_r0,r0,neg1 ## r0 = -1 1909 ret 1910 :peek_char_inb 1911 li_r2 &src_base 1912 ld_r2,r2,0 ## r2 = base 1913 add_r2,r2,r1 ## r2 = base + cursor 1914 lb_r0,r2,0 ## r0 = *ptr (0-ext byte) 1915 ret 1916 1917 1918 ## ---- peek2_char() -> r0 = char at cursor+1 (0..255) or -1 on EOF ---- 1919 ## Used by the reader to distinguish `,` vs `,@`, `-` vs `-<digit>`, and 1920 ## `0` vs `0x`/`0X` without consuming input. 1921 :peek2_char 1922 li_r1 &src_cursor 1923 ld_r1,r1,0 1924 addi_r1,r1,1 ## r1 = cursor + 1 1925 li_r2 &src_len 1926 ld_r2,r2,0 1927 li_br &peek2_char_inb 1928 blt_r1,r2 1929 li_r0 %0 1930 addi_r0,r0,neg1 1931 ret 1932 :peek2_char_inb 1933 li_r2 &src_base 1934 ld_r2,r2,0 1935 add_r2,r2,r1 1936 lb_r0,r2,0 1937 ret 1938 1939 1940 ## ---- advance_char() — consume current char, track line/col -------- 1941 :advance_char 1942 prologue 1943 li_br &peek_char 1944 call ## r0 = current char (possibly -1 if EOF) 1945 1946 ## cursor++ 1947 li_r1 &src_cursor 1948 ld_r2,r1,0 1949 addi_r2,r2,1 1950 st_r2,r1,0 1951 1952 ## if char == '\n', bump line and reset col; else col++. 1953 li_r1 %10 ## '\n' = 10 1954 li_br &advance_newline 1955 beq_r0,r1 1956 1957 li_r1 &src_col 1958 ld_r2,r1,0 1959 addi_r2,r2,1 1960 st_r2,r1,0 1961 epilogue 1962 ret 1963 1964 :advance_newline 1965 li_r1 &src_line 1966 ld_r2,r1,0 1967 addi_r2,r2,1 1968 st_r2,r1,0 1969 li_r1 &src_col 1970 li_r2 %1 1971 st_r2,r1,0 1972 epilogue 1973 ret 1974 1975 1976 ## ---- skip_ws() — eat whitespace and ; line comments ---------------- 1977 :skip_ws 1978 prologue 1979 :skip_ws_loop 1980 li_br &peek_char 1981 call ## r0 = next char (or -1) 1982 1983 ## EOF → done 1984 li_r1 %0 1985 addi_r1,r1,neg1 1986 li_br &skip_ws_done 1987 beq_r0,r1 1988 1989 ## ' ' (0x20) 1990 li_r1 %32 1991 li_br &skip_ws_eat 1992 beq_r0,r1 1993 ## '\t' (0x09) 1994 li_r1 %9 1995 li_br &skip_ws_eat 1996 beq_r0,r1 1997 ## '\n' (0x0A) 1998 li_r1 %10 1999 li_br &skip_ws_eat 2000 beq_r0,r1 2001 ## '\r' (0x0D) 2002 li_r1 %13 2003 li_br &skip_ws_eat 2004 beq_r0,r1 2005 ## ';' (0x3B) 2006 li_r1 %59 2007 li_br &skip_ws_comment 2008 beq_r0,r1 2009 2010 li_br &skip_ws_done 2011 b 2012 2013 :skip_ws_eat 2014 li_br &advance_char 2015 call 2016 li_br &skip_ws_loop 2017 b 2018 2019 :skip_ws_comment 2020 li_br &advance_char 2021 call ## eat ';' 2022 :skip_ws_comment_loop 2023 li_br &peek_char 2024 call 2025 ## EOF → outer loop (will see -1 and exit) 2026 li_r1 %0 2027 addi_r1,r1,neg1 2028 li_br &skip_ws_loop 2029 beq_r0,r1 2030 ## '\n' → eat and outer loop 2031 li_r1 %10 2032 li_br &skip_ws_eat 2033 beq_r0,r1 2034 ## else eat and keep going 2035 li_br &advance_char 2036 call 2037 li_br &skip_ws_comment_loop 2038 b 2039 2040 :skip_ws_done 2041 epilogue 2042 ret 2043 2044 2045 ## ---- is_digit(c) -> r0 = 0 or 1 ------------------------------------ 2046 ## Character already in r1. 2047 :is_digit 2048 li_r2 %48 ## '0' = 48 2049 li_r0 %0 ## default: not digit 2050 li_br &is_digit_done 2051 blt_r1,r2 ## c < '0' → not digit 2052 li_r2 %58 ## '9' + 1 = 58 (:) 2053 li_br &is_digit_yes 2054 blt_r1,r2 ## c < ':' → in range 2055 li_br &is_digit_done 2056 b 2057 :is_digit_yes 2058 li_r0 %1 2059 :is_digit_done 2060 ret 2061 2062 2063 ## ---- read_number() -> r0 = tagged fixnum ---------------------------- 2064 ## Reads a non-negative decimal integer, or a hex integer with `0x`/`0X` 2065 ## prefix. Digits are consumed while peek_char is in [0-9] (or [0-9a-fA-F] 2066 ## after the hex prefix). Uses r6 = accumulator across iterations (saved 2067 ## via PROLOGUE_N1 slot 1). 2068 :read_number 2069 prologue 2070 mov_r3,sp 2071 st_r6,sp,24 2072 2073 li_r6 %0 ## r6 = 0 (accumulator) 2074 2075 ## Detect `0x` / `0X` hex prefix — only triggers if the very first 2076 ## byte is '0' and the next is 'x' or 'X'. Anything else falls 2077 ## through to the decimal digit loop (including bare `0` or `07`). 2078 li_br &peek_char 2079 call 2080 li_r1 %48 ## '0' 2081 li_br &read_number_loop 2082 bne_r0,r1 2083 li_br &peek2_char 2084 call 2085 li_r1 %120 ## 'x' 2086 li_br &read_number_hex_start 2087 beq_r0,r1 2088 li_r1 %88 ## 'X' 2089 li_br &read_number_hex_start 2090 beq_r0,r1 2091 li_br &read_number_loop 2092 b 2093 2094 :read_number_hex_start 2095 li_br &advance_char 2096 call ## eat '0' 2097 li_br &advance_char 2098 call ## eat 'x' / 'X' 2099 2100 :read_number_hex_loop 2101 li_br &peek_char 2102 call 2103 mov_r1,r0 2104 li_br &hex_digit_val 2105 call ## r0 = digit (0..15) or -1 2106 li_r1 %0 2107 addi_r1,r1,neg1 2108 li_br &read_number_done 2109 beq_r0,r1 2110 2111 ## r6 = r6 * 16 + digit. ADDI_IMMS lacks 4-shift, so SHLI 2 twice. 2112 shli_r6,r6,2 2113 shli_r6,r6,2 2114 add_r6,r6,r0 2115 2116 li_br &advance_char 2117 call 2118 li_br &read_number_hex_loop 2119 b 2120 2121 :read_number_loop 2122 li_br &peek_char 2123 call ## r0 = char (or -1) 2124 2125 ## If char not a digit, stop. 2126 mov_r1,r0 2127 li_br &is_digit 2128 call ## r0 = 0 or 1 2129 li_br &read_number_done 2130 beqz_r0 2131 2132 ## digit = peek - '0'. Re-peek (simpler than stashing). 2133 li_br &peek_char 2134 call 2135 addi_r0,r0,neg48 ## r0 = digit (0..9) 2136 2137 ## r6 = r6 * 10 + digit. Do via (r6 << 3) + (r6 << 1). 2138 mov_r1,r6 2139 shli_r1,r6,3 ## r1 = r6 << 3 = r6*8 2140 mov_r2,r6 2141 shli_r2,r6,1 ## r2 = r6 << 1 = r6*2 2142 add_r6,r1,r2 ## r6 = r6*10 2143 add_r6,r6,r0 ## r6 = r6*10 + digit 2144 2145 li_br &advance_char 2146 call 2147 li_br &read_number_loop 2148 b 2149 2150 :read_number_done 2151 ## Tag as fixnum (low 3 bits = 001 = shift-left-3 + OR 1). 2152 shli_r0,r6,3 2153 ori_r0,r0,1 2154 ld_r6,sp,24 2155 epilogue 2156 ret 2157 2158 2159 ## ---- hex_digit_val(r1=c) -> r0 = digit in [0..15] or -1 ------------ 2160 ## Classifies one byte as a hex digit. Range-checks via BLT then does 2161 ## an arithmetic subtract: digits subtract '0', A-F subtract 55, a-f 2162 ## subtract 87. Non-hex chars (including EOF=-1) return -1. 2163 :hex_digit_val 2164 li_r2 %48 ## '0' 2165 li_br &hex_dv_none 2166 blt_r1,r2 2167 li_r2 %58 ## ':' (one past '9') 2168 li_br &hex_dv_dec 2169 blt_r1,r2 2170 li_r2 %65 ## 'A' 2171 li_br &hex_dv_none 2172 blt_r1,r2 2173 li_r2 %71 ## 'G' (one past 'F') 2174 li_br &hex_dv_upper 2175 blt_r1,r2 2176 li_r2 %97 ## 'a' 2177 li_br &hex_dv_none 2178 blt_r1,r2 2179 li_r2 %103 ## 'g' (one past 'f') 2180 li_br &hex_dv_lower 2181 blt_r1,r2 2182 :hex_dv_none 2183 li_r0 %0 2184 addi_r0,r0,neg1 2185 ret 2186 :hex_dv_dec 2187 mov_r0,r1 2188 addi_r0,r0,neg48 2189 ret 2190 :hex_dv_upper 2191 li_r0 %55 ## 'A' - 10 = 55 2192 sub_r0,r1,r0 2193 ret 2194 :hex_dv_lower 2195 li_r0 %87 ## 'a' - 10 = 87 2196 sub_r0,r1,r0 2197 ret 2198 2199 2200 ## ---- is_delim(c) -> r0 = 0 or 1 ------------------------------------ 2201 ## Returns 1 if c is a token delimiter: whitespace, '(', ')', ';', or EOF. 2202 :is_delim 2203 li_r0 %1 ## optimistic: delim 2204 li_r2 %0 2205 addi_r2,r2,neg1 2206 li_br &is_delim_done 2207 beq_r1,r2 ## EOF 2208 li_r2 %32 2209 li_br &is_delim_done 2210 beq_r1,r2 2211 li_r2 %9 2212 li_br &is_delim_done 2213 beq_r1,r2 2214 li_r2 %10 2215 li_br &is_delim_done 2216 beq_r1,r2 2217 li_r2 %13 2218 li_br &is_delim_done 2219 beq_r1,r2 2220 li_r2 %40 ## '(' 2221 li_br &is_delim_done 2222 beq_r1,r2 2223 li_r2 %41 ## ')' 2224 li_br &is_delim_done 2225 beq_r1,r2 2226 li_r2 %59 ## ';' 2227 li_br &is_delim_done 2228 beq_r1,r2 2229 li_r0 %0 2230 :is_delim_done 2231 ret 2232 2233 2234 ## ---- read_symbol() -> r0 = tagged symbol -------------------------- 2235 ## Reads until the next delimiter, then calls intern on the byte range. 2236 ## Uses r6 = start cursor, r7 = start pointer (both saved across loop). 2237 :read_symbol 2238 prologue_n2 2239 mov_r3,sp 2240 st_r6,sp,24 2241 st_r7,sp,32 2242 2243 ## r7 = &src[cursor] (stored start) 2244 li_r1 &src_cursor 2245 ld_r1,r1,0 2246 li_r2 &src_base 2247 ld_r2,r2,0 2248 add_r7,r1,r2 ## r7 = base + cursor 2249 mov_r6,r1 ## r6 = start cursor 2250 2251 :read_symbol_loop 2252 li_br &peek_char 2253 call 2254 mov_r1,r0 2255 li_br &is_delim 2256 call 2257 li_r1 %1 2258 li_br &read_symbol_done 2259 beq_r0,r1 ## delim → stop 2260 2261 li_br &advance_char 2262 call 2263 li_br &read_symbol_loop 2264 b 2265 2266 :read_symbol_done 2267 ## Length = current cursor - r6. 2268 li_r1 &src_cursor 2269 ld_r1,r1,0 2270 sub_r2,r1,r6 ## r2 = cur - start = len 2271 2272 ## If length 0, malformed (shouldn't happen — caller peeks before). 2273 li_br &err_reader_bad 2274 beqz_r2 2275 2276 mov_r1,r7 ## r1 = ptr to first byte 2277 ## r2 = len (already set) 2278 li_br &intern 2279 call 2280 2281 ld_r6,sp,24 2282 ld_r7,sp,32 2283 epilogue_n2 2284 ret 2285 2286 2287 ## ---- read_string() -> r0 = tagged string -------------------------- 2288 ## Reads a double-quoted string into a scratch buffer, handling the 2289 ## four escape forms the seed needs: \n, \t, \\, and \". 2290 :read_string 2291 prologue_n2 2292 st_r6,sp,24 2293 st_r7,sp,32 2294 2295 li_br &advance_char 2296 call ## eat opening '"' 2297 2298 li_r6 &reader_string_buf 2299 li_r7 %0 ## length 2300 2301 :read_string_loop 2302 li_br &peek_char 2303 call 2304 li_r1 %0 2305 addi_r1,r1,neg1 2306 li_br &err_reader_bad 2307 beq_r0,r1 ## EOF in string 2308 2309 li_r1 %34 2310 li_br &read_string_done 2311 beq_r0,r1 ## closing '"' 2312 2313 li_r1 %92 2314 li_br &read_string_escape 2315 beq_r0,r1 2316 2317 li_r1 %512 ## READER_STRING_BUF_SIZE = 512 2318 li_br &read_string_store 2319 blt_r7,r1 2320 li_br &err_reader_bad 2321 b 2322 2323 :read_string_store 2324 sb_r0,r6,0 2325 addi_r6,r6,1 2326 addi_r7,r7,1 2327 li_br &advance_char 2328 call 2329 li_br &read_string_loop 2330 b 2331 2332 :read_string_escape 2333 li_br &advance_char 2334 call ## eat '\' 2335 li_br &peek_char 2336 call 2337 li_r1 %0 2338 addi_r1,r1,neg1 2339 li_br &err_reader_bad 2340 beq_r0,r1 2341 2342 li_r1 %110 ## 'n' 2343 li_br &read_string_escape_nl 2344 beq_r0,r1 2345 li_r1 %116 ## 't' 2346 li_br &read_string_escape_tab 2347 beq_r0,r1 2348 li_r1 %92 ## '\\' 2349 li_br &read_string_store_escaped 2350 beq_r0,r1 2351 li_r1 %34 ## '"' 2352 li_br &read_string_store_escaped 2353 beq_r0,r1 2354 li_br &err_reader_bad 2355 b 2356 2357 :read_string_escape_nl 2358 li_r0 %10 2359 li_br &read_string_store_escaped 2360 b 2361 2362 :read_string_escape_tab 2363 li_r0 %9 2364 2365 :read_string_store_escaped 2366 li_r1 %4096 2367 li_br &read_string_store_escaped_ok 2368 blt_r7,r1 2369 li_br &err_reader_bad 2370 b 2371 2372 :read_string_store_escaped_ok 2373 sb_r0,r6,0 2374 addi_r6,r6,1 2375 addi_r7,r7,1 2376 li_br &advance_char 2377 call ## eat escaped payload char 2378 li_br &read_string_loop 2379 b 2380 2381 :read_string_done 2382 li_br &advance_char 2383 call ## eat closing '"' 2384 li_r1 &reader_string_buf 2385 mov_r2,r7 2386 li_br &make_string 2387 call 2388 2389 ld_r6,sp,24 2390 ld_r7,sp,32 2391 epilogue_n2 2392 ret 2393 2394 2395 ## ---- read_list() -> r0 = tagged list -------------------------------- 2396 ## Assumes the '(' has already been consumed. Reads expressions until ')'. 2397 ## Builds a list in order using a reversed cons chain, then reverses. 2398 ## Simpler approach here: recursive — read one expr, then cons onto the 2399 ## read_list tail. Stack-bounded but OK for shallow depths. 2400 ## 2401 ## Frame: PROLOGUE_N1 with slot 1 = saved r6 (first element tagged value). 2402 :read_list 2403 prologue 2404 2405 li_br &skip_ws 2406 call 2407 2408 ## If next char is ')', return nil (end of list). 2409 li_br &peek_char 2410 call 2411 li_r1 %41 ## ')' 2412 li_br &read_list_close 2413 beq_r0,r1 2414 2415 ## EOF mid-list → error. 2416 li_r1 %0 2417 addi_r1,r1,neg1 2418 li_br &err_reader_bad 2419 beq_r0,r1 2420 2421 ## '.' followed by a delimiter → dotted tail. `.<alpha>` still 2422 ## parses as a regular symbol (fall-through to read_expr). 2423 li_r1 %46 ## '.' 2424 li_br &read_list_maybe_dotted 2425 beq_r0,r1 2426 2427 ## Read head element, then tail, then cons them. 2428 li_br &read_expr 2429 call ## r0 = head 2430 2431 st_r0,sp,24 ## slot 1 = head (spill across next call) 2432 2433 li_br &read_list 2434 call ## r0 = tail (recursive) 2435 2436 ## cons(head, tail) 2437 ld_r1,sp,24 2438 mov_r2,r0 2439 li_br &cons 2440 call 2441 2442 epilogue 2443 ret 2444 2445 :read_list_maybe_dotted 2446 li_br &peek2_char 2447 call 2448 mov_r1,r0 2449 li_br &is_delim 2450 call 2451 li_r1 %1 2452 li_br &read_list_dotted 2453 beq_r0,r1 2454 2455 ## Bare `.` followed by non-delim → fall back to regular item path. 2456 li_br &read_expr 2457 call 2458 st_r0,sp,24 2459 li_br &read_list 2460 call 2461 ld_r1,sp,24 2462 mov_r2,r0 2463 li_br &cons 2464 call 2465 epilogue 2466 ret 2467 2468 :read_list_dotted 2469 li_br &advance_char 2470 call ## eat '.' 2471 li_br &skip_ws 2472 call 2473 li_br &read_expr 2474 call ## r0 = dotted tail expr 2475 st_r0,sp,24 2476 li_br &skip_ws 2477 call 2478 li_br &peek_char 2479 call 2480 li_r1 %41 ## ')' 2481 li_br &err_reader_bad 2482 bne_r0,r1 2483 li_br &advance_char 2484 call ## eat ')' 2485 ld_r0,sp,24 2486 epilogue 2487 ret 2488 2489 :read_list_close 2490 li_br &advance_char 2491 call ## eat ')' 2492 li_r0 NIL ## nil = 0x07 2493 epilogue 2494 ret 2495 2496 2497 ## ---- read_expr() -> r0 = tagged value ------------------------------- 2498 ## Dispatches on the first non-whitespace char. 2499 :read_expr 2500 prologue 2501 2502 li_br &skip_ws 2503 call 2504 2505 li_br &peek_char 2506 call ## r0 = char 2507 2508 ## EOF → error. 2509 li_r1 %0 2510 addi_r1,r1,neg1 2511 li_br &err_reader_bad 2512 beq_r0,r1 2513 2514 ## '(' → list. 2515 li_r1 %40 2516 li_br &read_expr_list 2517 beq_r0,r1 2518 2519 ## '"' → string literal. 2520 li_r1 %34 2521 li_br &read_expr_string 2522 beq_r0,r1 2523 2524 ## '#' → hash-prefix literal (#t, #f, #\char, #(...)). 2525 li_r1 %35 2526 li_br &read_expr_hash 2527 beq_r0,r1 2528 2529 ## '\'' → (quote x) 2530 li_r1 %39 2531 li_br &read_expr_quote 2532 beq_r0,r1 2533 2534 ## '`' → (quasiquote x) 2535 li_r1 %96 2536 li_br &read_expr_quasiquote 2537 beq_r0,r1 2538 2539 ## ',' → (unquote x) or (unquote-splicing x) if followed by '@' 2540 li_r1 %44 2541 li_br &read_expr_unquote 2542 beq_r0,r1 2543 2544 ## '-' followed by a digit → negative fixnum literal. Plain '-' 2545 ## still reads as a symbol (the subtraction primitive). 2546 li_r1 %45 2547 li_br &read_expr_maybe_neg 2548 beq_r0,r1 2549 2550 ## digit → number. 2551 mov_r1,r0 2552 li_br &is_digit 2553 call 2554 li_r1 %1 2555 li_br &read_expr_number 2556 beq_r0,r1 2557 2558 ## else → symbol. 2559 li_br &read_symbol 2560 call 2561 epilogue 2562 ret 2563 2564 :read_expr_list 2565 li_br &advance_char 2566 call ## eat '(' 2567 li_br &read_list 2568 call 2569 epilogue 2570 ret 2571 2572 :read_expr_number 2573 li_br &read_number 2574 call 2575 epilogue 2576 ret 2577 2578 :read_expr_string 2579 li_br &read_string 2580 call 2581 epilogue 2582 ret 2583 2584 ## Hash-prefix literals: `#t`/`#f` singletons, `#\char` ASCII fixnum, 2585 ## `#(...)` vector literal. Inherits read_expr's PROLOGUE. 2586 :read_expr_hash 2587 li_br &advance_char 2588 call ## eat '#' 2589 li_br &peek_char 2590 call ## r0 = next char 2591 2592 li_r1 %116 ## 't' 2593 li_br &read_expr_hash_t 2594 beq_r0,r1 2595 li_r1 %102 ## 'f' 2596 li_br &read_expr_hash_f 2597 beq_r0,r1 2598 li_r1 %92 ## '\\' 2599 li_br &read_expr_hash_char 2600 beq_r0,r1 2601 li_r1 %40 ## '(' 2602 li_br &read_expr_hash_vector 2603 beq_r0,r1 2604 2605 li_br &err_reader_bad 2606 b 2607 2608 :read_expr_hash_t 2609 li_br &advance_char 2610 call ## eat 't' 2611 li_r0 TRUE ## #t singleton 2612 epilogue 2613 ret 2614 2615 :read_expr_hash_f 2616 li_br &advance_char 2617 call ## eat 'f' 2618 li_r0 FALSE ## #f singleton 2619 epilogue 2620 ret 2621 2622 :read_expr_hash_char 2623 li_br &read_char_literal 2624 call 2625 epilogue 2626 ret 2627 2628 :read_expr_hash_vector 2629 li_br &read_vector_literal 2630 call 2631 epilogue 2632 ret 2633 2634 2635 ## ---- read_expr_quote / quasiquote / unquote / maybe_neg ------------- 2636 ## All inherit read_expr's PROLOGUE; each wraps the downstream value and 2637 ## ret/epilogue out. 2638 :read_expr_quote 2639 li_br &advance_char 2640 call ## eat '\'' 2641 li_r1 &sym_quote 2642 ld_r1,r1,0 2643 li_br &read_quoted_wrap 2644 call 2645 epilogue 2646 ret 2647 2648 :read_expr_quasiquote 2649 li_br &advance_char 2650 call ## eat '`' 2651 li_r1 &sym_quasiquote 2652 ld_r1,r1,0 2653 li_br &read_quoted_wrap 2654 call 2655 epilogue 2656 ret 2657 2658 :read_expr_unquote 2659 li_br &advance_char 2660 call ## eat ',' 2661 li_br &peek_char 2662 call 2663 li_r1 %64 ## '@' 2664 li_br &read_expr_unquote_splicing 2665 beq_r0,r1 2666 li_r1 &sym_unquote 2667 ld_r1,r1,0 2668 li_br &read_quoted_wrap 2669 call 2670 epilogue 2671 ret 2672 2673 :read_expr_unquote_splicing 2674 li_br &advance_char 2675 call ## eat '@' 2676 li_r1 &sym_unquote_splicing 2677 ld_r1,r1,0 2678 li_br &read_quoted_wrap 2679 call 2680 epilogue 2681 ret 2682 2683 :read_expr_maybe_neg 2684 li_br &peek2_char 2685 call 2686 mov_r1,r0 2687 li_br &is_digit 2688 call 2689 li_r1 %1 2690 li_br &read_expr_negnum 2691 beq_r0,r1 2692 ## Not a digit after '-' → treat as a regular symbol (the '-' is 2693 ## the sub primitive, or a longer ident like `-ident`). 2694 li_br &read_symbol 2695 call 2696 epilogue 2697 ret 2698 2699 :read_expr_negnum 2700 li_br &advance_char 2701 call ## eat '-' 2702 li_br &read_number 2703 call ## r0 = tagged positive number 2704 sari_r0,r0,3 ## decode 2705 li_r1 %0 2706 sub_r0,r1,r0 ## r0 = -value 2707 shli_r0,r0,3 2708 ori_r0,r0,1 ## retag 2709 epilogue 2710 ret 2711 2712 2713 ## ---- read_quoted_wrap(r1=tagged_sym) -> r0 = (sym expr) ------------- 2714 ## Reads one expression and returns (sym . (expr . nil)). Shared by the 2715 ## four reader shorthand forms (quote/quasi/unquote/unquote-splicing). 2716 :read_quoted_wrap 2717 prologue 2718 st_r1,sp,24 ## save tagged sym 2719 li_br &read_expr 2720 call ## r0 = expr 2721 mov_r1,r0 2722 li_r2 NIL 2723 li_br &cons 2724 call ## r0 = (expr . nil) 2725 mov_r2,r0 2726 ld_r1,sp,24 2727 li_br &cons ## r0 = (sym . (expr . nil)) 2728 tail 2729 2730 2731 ## ---- read_char_literal() -> r0 = tagged fixnum (ASCII) ------------- 2732 ## `\` already at cursor. Consumes `\`, then one or more chars. If the 2733 ## first char is followed by a delimiter, that char is the result. If 2734 ## more chars follow, the full span is matched against `space`, `newline`, 2735 ## or `tab`; anything else errors out. 2736 :read_char_literal 2737 prologue_n4 2738 li_br &advance_char 2739 call ## eat '\\' 2740 2741 li_r1 &src_cursor 2742 ld_r0,r1,0 2743 st_r0,sp,24 ## slot 1 = start cursor 2744 2745 li_br &peek_char 2746 call ## r0 = first char (must not be EOF) 2747 li_r1 %0 2748 addi_r1,r1,neg1 2749 li_br &err_reader_bad 2750 beq_r0,r1 2751 2752 st_r0,sp,32 ## slot 2 = first char 2753 li_br &advance_char 2754 call ## eat first char 2755 2756 li_br &peek_char 2757 call 2758 mov_r1,r0 2759 li_br &is_delim 2760 call 2761 li_r1 %1 2762 li_br &rcl_single 2763 beq_r0,r1 2764 2765 :rcl_name_loop 2766 li_br &peek_char 2767 call 2768 mov_r1,r0 2769 li_br &is_delim 2770 call 2771 li_r1 %1 2772 li_br &rcl_compare 2773 beq_r0,r1 2774 li_br &advance_char 2775 call 2776 li_br &rcl_name_loop 2777 b 2778 2779 :rcl_compare 2780 ## r2 = len (cursor - start), r0 = ptr (base + start). 2781 li_r1 &src_cursor 2782 ld_r2,r1,0 2783 ld_r1,sp,24 2784 sub_r2,r2,r1 2785 li_r0 &src_base 2786 ld_r0,r0,0 2787 add_r0,r0,r1 2788 li_r1 %5 2789 li_br &rcl_cmp5 2790 beq_r2,r1 2791 li_r1 %7 2792 li_br &rcl_cmp7 2793 beq_r2,r1 2794 li_r1 %3 2795 li_br &rcl_cmp3 2796 beq_r2,r1 2797 li_br &err_reader_bad 2798 b 2799 2800 :rcl_cmp5 2801 ## "space" → 32 2802 lb_r1,r0,0 2803 li_r2 %115 2804 li_br &err_reader_bad 2805 bne_r1,r2 2806 addi_r0,r0,1 2807 lb_r1,r0,0 2808 li_r2 %112 2809 li_br &err_reader_bad 2810 bne_r1,r2 2811 addi_r0,r0,1 2812 lb_r1,r0,0 2813 li_r2 %97 2814 li_br &err_reader_bad 2815 bne_r1,r2 2816 addi_r0,r0,1 2817 lb_r1,r0,0 2818 li_r2 %99 2819 li_br &err_reader_bad 2820 bne_r1,r2 2821 addi_r0,r0,1 2822 lb_r1,r0,0 2823 li_r2 %101 2824 li_br &err_reader_bad 2825 bne_r1,r2 2826 li_r0 %32 2827 shli_r0,r0,3 2828 ori_r0,r0,1 2829 epilogue_n4 2830 ret 2831 2832 :rcl_cmp7 2833 ## "newline" → 10 2834 lb_r1,r0,0 2835 li_r2 %110 2836 li_br &err_reader_bad 2837 bne_r1,r2 2838 addi_r0,r0,1 2839 lb_r1,r0,0 2840 li_r2 %101 2841 li_br &err_reader_bad 2842 bne_r1,r2 2843 addi_r0,r0,1 2844 lb_r1,r0,0 2845 li_r2 %119 2846 li_br &err_reader_bad 2847 bne_r1,r2 2848 addi_r0,r0,1 2849 lb_r1,r0,0 2850 li_r2 %108 2851 li_br &err_reader_bad 2852 bne_r1,r2 2853 addi_r0,r0,1 2854 lb_r1,r0,0 2855 li_r2 %105 2856 li_br &err_reader_bad 2857 bne_r1,r2 2858 addi_r0,r0,1 2859 lb_r1,r0,0 2860 li_r2 %110 2861 li_br &err_reader_bad 2862 bne_r1,r2 2863 addi_r0,r0,1 2864 lb_r1,r0,0 2865 li_r2 %101 2866 li_br &err_reader_bad 2867 bne_r1,r2 2868 li_r0 %10 2869 shli_r0,r0,3 2870 ori_r0,r0,1 2871 epilogue_n4 2872 ret 2873 2874 :rcl_cmp3 2875 ## "tab" → 9 2876 lb_r1,r0,0 2877 li_r2 %116 2878 li_br &err_reader_bad 2879 bne_r1,r2 2880 addi_r0,r0,1 2881 lb_r1,r0,0 2882 li_r2 %97 2883 li_br &err_reader_bad 2884 bne_r1,r2 2885 addi_r0,r0,1 2886 lb_r1,r0,0 2887 li_r2 %98 2888 li_br &err_reader_bad 2889 bne_r1,r2 2890 li_r0 %9 2891 shli_r0,r0,3 2892 ori_r0,r0,1 2893 epilogue_n4 2894 ret 2895 2896 :rcl_single 2897 ld_r0,sp,32 2898 shli_r0,r0,3 2899 ori_r0,r0,1 2900 epilogue_n4 2901 ret 2902 2903 2904 ## ---- read_vector_literal() -> r0 = tagged vector ------------------- 2905 ## `#(` detected; cursor at `(`. Read the body as a list, then convert 2906 ## element-by-element into a freshly-allocated vector. 2907 :read_vector_literal 2908 prologue_n3 2909 li_br &advance_char 2910 call ## eat '(' 2911 li_br &read_list 2912 call ## r0 = tagged list 2913 st_r0,sp,24 ## slot 1 = list head 2914 2915 mov_r1,r0 2916 li_r2 %0 ## r2 = count 2917 :rvl_count 2918 li_r0 NIL 2919 li_br &rvl_alloc 2920 beq_r1,r0 2921 addi_r0,r1,neg2 2922 ld_r1,r0,8 2923 addi_r2,r2,1 2924 li_br &rvl_count 2925 b 2926 2927 :rvl_alloc 2928 mov_r1,r2 2929 li_r2 NIL 2930 li_br &make_vector 2931 call ## r0 = tagged vector 2932 st_r0,sp,32 ## slot 2 = vec 2933 2934 addi_r3,r0,neg3 2935 addi_r3,r3,8 ## r3 = payload cursor 2936 ld_r1,sp,24 2937 2938 :rvl_fill 2939 li_r0 NIL 2940 li_br &rvl_done 2941 beq_r1,r0 2942 addi_r0,r1,neg2 2943 ld_r2,r0,0 2944 ld_r1,r0,8 2945 st_r2,r3,0 2946 addi_r3,r3,8 2947 li_br &rvl_fill 2948 b 2949 2950 :rvl_done 2951 ld_r0,sp,32 2952 epilogue_n3 2953 ret 2954 2955 2956 ## ---- Display -------------------------------------------------------- 2957 2958 ## ---- putc(r1=char) — write one byte to fd 1 ------------------------ 2959 :putc_buf %0 %0 2960 2961 :putc 2962 prologue 2963 li_r2 &putc_buf 2964 sb_r1,r2,0 ## *putc_buf = low byte of r1 2965 li_r0 sys_write 2966 li_r1 %1 2967 ## r2 = &putc_buf already 2968 li_r3 %1 2969 syscall 2970 epilogue 2971 ret 2972 2973 2974 ## ---- display_uint(r1=u64, r2=fd) — decimal, no sign -------------- 2975 ## Writes digits to `digit_buf` right-to-left, then SYS_WRITEs the 2976 ## filled range to fd. 24-byte buffer covers any 61-bit value. 2977 ## fd is spilled into slot 3 because the digit loop reuses r2 as the 2978 ## divisor '10' constant — keeping fd in a reg would require saving 2979 ## another callee-saved, which costs the same. 2980 :digit_buf '000000000000000000000000000000000000000000000000' 2981 :digit_buf_end %0 2982 2983 :display_uint 2984 prologue_n3 2985 st_r6,sp,24 2986 st_r7,sp,32 2987 st_r2,sp,40 ## save fd 2988 2989 li_r6 &digit_buf_end ## r6 = end-of-buffer cursor (moves left) 2990 mov_r7,r1 ## r7 = value (mutated) 2991 2992 ## Special-case zero. 2993 li_br &du_loop 2994 bnez_r7 ## if value != 0, loop; else write '0' 2995 addi_r6,r6,neg1 2996 li_r1 %48 ## '0' 2997 sb_r1,r6,0 2998 li_br &du_write 2999 b 3000 3001 :du_loop 3002 li_br &du_write 3003 beqz_r7 ## value == 0 → done digit gen 3004 3005 ## digit = value % 10. 3006 mov_r1,r7 3007 li_r2 %10 3008 rem_r1,r1,r2 ## r1 = value % 10 3009 addi_r1,r1,48 ## r1 += '0' 3010 addi_r6,r6,neg1 3011 sb_r1,r6,0 ## *--r6 = digit 3012 3013 ## value = value / 10. 3014 mov_r1,r7 3015 li_r2 %10 3016 div_r1,r1,r2 3017 mov_r7,r1 3018 3019 li_br &du_loop 3020 b 3021 3022 :du_write 3023 ## Stash fd into r0 first — computing len clobbers r3 (we need it 3024 ## as SP for the slot-3 load), so grab fd before that step. 3025 ld_r0,sp,40 ## r0 = fd (temp; sys_write will overwrite r0) 3026 li_r1 &digit_buf_end 3027 sub_r3,r1,r6 ## r3 = len = digit_buf_end - r6 3028 mov_r1,r0 ## r1 = fd 3029 mov_r2,r6 ## r2 = buf 3030 li_r0 sys_write 3031 syscall 3032 3033 ld_r6,sp,24 3034 ld_r7,sp,32 3035 epilogue_n3 3036 ret 3037 3038 3039 ## ---- display(r1=tagged) — dispatch on tag ------------------------- 3040 ## display owns a PROLOGUE_N2 frame even though its own body only needs 3041 ## retaddr — display_pair dispatches into this frame (via BEQ, not CALL) 3042 ## and uses slots 1/2 to save caller's r6/r7. Giving display_pair its own 3043 ## prologue would leak display's frame on return: the stacked PROLOGUE + 3044 ## PROLOGUE_N2 sum doesn't match display_pair's single EPILOGUE_N2, so 3045 ## recursive calls drift sp and the saved r6/r7 slots point off-frame. 3046 :display 3047 prologue_n2 3048 3049 ## nil = 0x07 3050 li_r2 NIL 3051 li_br &display_nil 3052 beq_r1,r2 3053 3054 ## #t = 0x0F 3055 li_r2 TRUE 3056 li_br &display_true 3057 beq_r1,r2 3058 3059 ## #f = 0x17 3060 li_r2 FALSE 3061 li_br &display_false 3062 beq_r1,r2 3063 3064 ## Low-3-bit tag. 3065 mov_r2,r1 3066 andi_r1,r1,7 ## r1 = tag 3067 li_r3 TAG_FIXNUM 3068 li_br &display_fixnum 3069 beq_r1,r3 3070 li_r3 TAG_PAIR 3071 li_br &display_pair 3072 beq_r1,r3 3073 li_r3 TAG_SYMBOL 3074 li_br &display_symbol 3075 beq_r1,r3 3076 li_r3 TAG_STRING 3077 li_br &display_string 3078 beq_r1,r3 3079 3080 ## Unknown — treat as "?" 3081 li_r1 %63 ## '?' 3082 li_br &putc 3083 call 3084 epilogue_n2 3085 ret 3086 3087 :display_nil 3088 li_r1 %40 ## '(' 3089 li_br &putc 3090 call 3091 li_r1 %41 ## ')' 3092 li_br &putc 3093 call 3094 epilogue_n2 3095 ret 3096 3097 ## #t and #f share display's PROLOGUE_N2 frame (reached via BEQ from 3098 ## display or write — display_fixnum/symbol pattern). 3099 :display_true 3100 li_r1 %35 ## '#' 3101 li_br &putc 3102 call 3103 li_r1 %116 ## 't' 3104 li_br &putc 3105 call 3106 epilogue_n2 3107 ret 3108 3109 :display_false 3110 li_r1 %35 ## '#' 3111 li_br &putc 3112 call 3113 li_r1 %102 ## 'f' 3114 li_br &putc 3115 call 3116 epilogue_n2 3117 ret 3118 3119 :display_fixnum 3120 mov_r1,r2 3121 sari_r1,r1,3 ## signed shift; value (assume non-negative here) 3122 li_r2 %1 ## fd = stdout 3123 li_br &display_uint 3124 call 3125 epilogue_n2 3126 ret 3127 3128 :display_symbol 3129 ## r2 = tagged symbol. Strip tag, read header, then write bytes. 3130 addi_r2,r2,neg5 ## r2 = raw header ptr (tag=0b101 → -5) 3131 ld_r3,r2,0 ## r3 = header 3132 ## Low 48 bits = length. Mask by SHL/SHR 16. 3133 shli_r3,r3,32 ## clear top 16 3134 shri_r3,r3,32 ## r3 = length (0..2^48) 3135 addi_r2,r2,8 ## r2 = name ptr 3136 li_r0 sys_write 3137 li_r1 %1 3138 syscall 3139 epilogue_n2 3140 ret 3141 3142 :display_pair 3143 ## r2 = tagged pair. Shares display's PROLOGUE_N2 frame. 3144 st_r6,sp,24 ## save r6 into display's slot 1 3145 st_r7,sp,32 ## save r7 into display's slot 2 3146 3147 mov_r6,r2 ## r6 = tagged pair (we'll walk cdr chain) 3148 3149 li_r1 %40 ## '(' 3150 li_br &putc 3151 call 3152 3153 :display_pair_loop 3154 ## car(r6) 3155 mov_r1,r6 3156 li_br &car 3157 call 3158 mov_r1,r0 3159 li_br &display 3160 call 3161 3162 ## cdr(r6) 3163 mov_r1,r6 3164 li_br &cdr 3165 call 3166 mov_r6,r0 3167 3168 ## If cdr == nil, done. 3169 li_r1 NIL 3170 li_br &display_pair_close 3171 beq_r6,r1 3172 3173 ## If cdr is a pair, space + loop. 3174 mov_r1,r6 3175 andi_r1,r1,7 3176 li_r2 TAG_PAIR 3177 li_br &display_pair_continue 3178 beq_r1,r2 3179 3180 ## Otherwise, improper: " . x)". 3181 li_r1 %32 ## ' ' 3182 li_br &putc 3183 call 3184 li_r1 %46 ## '.' 3185 li_br &putc 3186 call 3187 li_r1 %32 3188 li_br &putc 3189 call 3190 mov_r1,r6 3191 li_br &display 3192 call 3193 li_br &display_pair_close 3194 b 3195 3196 :display_pair_continue 3197 li_r1 %32 ## ' ' 3198 li_br &putc 3199 call 3200 li_br &display_pair_loop 3201 b 3202 3203 :display_pair_close 3204 li_r1 %41 ## ')' 3205 li_br &putc 3206 call 3207 3208 ld_r6,sp,24 3209 ld_r7,sp,32 3210 epilogue_n2 3211 ret 3212 3213 3214 ## ---- display_string(r2=tagged_string) ------------------------------ 3215 ## Tail-called from display's dispatcher with r2 holding the tagged 3216 ## string (low-3-bit tag = 100 = 4). Strips the tag, reads the 48-bit 3217 ## length out of the 8-byte header, then writes the payload bytes to 3218 ## stdout. Shares display's PROLOGUE_N2 frame, so EPILOGUE_N2 + RET 3219 ## unwinds back to display's caller. 3220 :display_string 3221 addi_r2,r2,neg4 ## r2 = raw header ptr 3222 ld_r3,r2,0 ## r3 = header word 3223 shli_r3,r3,32 3224 shri_r3,r3,32 ## r3 = length (low 48 bits) 3225 addi_r2,r2,8 ## r2 = payload ptr 3226 li_r0 sys_write 3227 li_r1 %1 3228 syscall 3229 epilogue_n2 3230 ret 3231 3232 3233 ## ---- write(r1=val) ------------------------------------------------- 3234 ## Printer form: strings get quoted with escapes honored, everything 3235 ## else renders identically to display. Sharing display_nil/fixnum/ 3236 ## symbol works because write's PROLOGUE_N2 frame matches display's — 3237 ## those handlers unwind the right amount on their own EPILOGUE_N2. 3238 :write 3239 prologue_n2 3240 3241 li_r2 NIL 3242 li_br &display_nil 3243 beq_r1,r2 ## nil 3244 3245 li_r2 TRUE 3246 li_br &display_true 3247 beq_r1,r2 ## #t 3248 3249 li_r2 FALSE 3250 li_br &display_false 3251 beq_r1,r2 ## #f 3252 3253 mov_r2,r1 3254 andi_r1,r1,7 ## r1 = tag 3255 li_r3 TAG_FIXNUM 3256 li_br &display_fixnum 3257 beq_r1,r3 3258 li_r3 TAG_PAIR 3259 li_br &write_pair 3260 beq_r1,r3 3261 li_r3 TAG_SYMBOL 3262 li_br &display_symbol 3263 beq_r1,r3 3264 li_r3 TAG_STRING 3265 li_br &write_string 3266 beq_r1,r3 3267 3268 ## Unknown (closure/prim, etc.) — emit '?' placeholder. 3269 li_r1 %63 3270 li_br &putc 3271 call 3272 epilogue_n2 3273 ret 3274 3275 3276 ## ---- write_string(r2=tagged_string) -------------------------------- 3277 ## Emits "..." with `"` and `\` escaped as `\"` and `\\`. Shares 3278 ## write's PROLOGUE_N2 frame; r6=cursor, r7=remaining-bytes saved in 3279 ## the two slots across putc calls. 3280 :write_string 3281 st_r6,sp,24 3282 st_r7,sp,32 3283 3284 addi_r2,r2,neg4 3285 ld_r3,r2,0 3286 shli_r3,r3,32 3287 shri_r3,r3,32 ## r3 = length 3288 addi_r2,r2,8 ## r2 = payload ptr 3289 mov_r6,r2 ## r6 = cursor 3290 mov_r7,r3 ## r7 = length 3291 3292 li_r1 %34 ## '"' 3293 li_br &putc 3294 call 3295 3296 :write_string_loop 3297 li_br &write_string_done 3298 beqz_r7 ## length == 0 → done 3299 3300 lb_r1,r6,0 ## r1 = *cursor 3301 3302 li_r2 %34 ## '"' 3303 li_br &write_string_escape 3304 beq_r1,r2 3305 3306 li_r2 %92 ## '\\' 3307 li_br &write_string_escape 3308 beq_r1,r2 3309 3310 li_br &putc 3311 call 3312 3313 :write_string_next 3314 addi_r6,r6,1 3315 addi_r7,r7,neg1 3316 li_br &write_string_loop 3317 b 3318 3319 :write_string_escape 3320 ## Emit '\\' then re-emit the char (cursor still points to it). 3321 li_r1 %92 3322 li_br &putc 3323 call 3324 lb_r1,r6,0 3325 li_br &putc 3326 call 3327 li_br &write_string_next 3328 b 3329 3330 :write_string_done 3331 li_r1 %34 ## '"' 3332 li_br &putc 3333 call 3334 ld_r6,sp,24 3335 ld_r7,sp,32 3336 epilogue_n2 3337 ret 3338 3339 3340 ## ---- write_pair(r2=tagged_pair) ------------------------------------ 3341 ## Mirror of display_pair but recurses through write (strings quoted). 3342 ## Shares write's PROLOGUE_N2 frame. 3343 :write_pair 3344 st_r6,sp,24 3345 st_r7,sp,32 3346 3347 mov_r6,r2 ## r6 = tagged pair cursor 3348 3349 li_r1 %40 ## '(' 3350 li_br &putc 3351 call 3352 3353 :write_pair_loop 3354 mov_r1,r6 3355 li_br &car 3356 call 3357 mov_r1,r0 3358 li_br &write 3359 call 3360 3361 mov_r1,r6 3362 li_br &cdr 3363 call 3364 mov_r6,r0 3365 3366 li_r1 NIL 3367 li_br &write_pair_close 3368 beq_r6,r1 3369 3370 mov_r1,r6 3371 andi_r1,r1,7 3372 li_r2 TAG_PAIR 3373 li_br &write_pair_continue 3374 beq_r1,r2 3375 3376 ## improper tail: " . x)" 3377 li_r1 %32 3378 li_br &putc 3379 call 3380 li_r1 %46 3381 li_br &putc 3382 call 3383 li_r1 %32 3384 li_br &putc 3385 call 3386 mov_r1,r6 3387 li_br &write 3388 call 3389 li_br &write_pair_close 3390 b 3391 3392 :write_pair_continue 3393 li_r1 %32 3394 li_br &putc 3395 call 3396 li_br &write_pair_loop 3397 b 3398 3399 :write_pair_close 3400 li_r1 %41 ## ')' 3401 li_br &putc 3402 call 3403 3404 ld_r6,sp,24 3405 ld_r7,sp,32 3406 epilogue_n2 3407 ret 3408 3409 3410 ## ================ Step 6: Eval ======================================= 3411 ## Recursive-descent evaluator. Compound forms dispatch by comparing 3412 ## the car against cached interned pointers (sym_quote/if/begin/ 3413 ## lambda/define, seeded in _start). Anything else is a function 3414 ## application: callee and args get evaluated, then apply runs the 3415 ## closure. Local env is a flat alist of (sym . val) pairs; global 3416 ## bindings live in a singly-linked alist hanging off global_env_cell. 3417 3418 ## ---- gset(r1=sym, r2=val) ------------------------------------------ 3419 ## Prepends (sym . val) to the global alist. Last-write-wins semantics 3420 ## fall out naturally because lookup_alist returns the leftmost match. 3421 :gset 3422 prologue_n2 3423 st_r1,sp,24 ## save sym 3424 st_r2,sp,32 ## save val 3425 3426 li_br &cons 3427 call ## r0 = (sym . val) 3428 3429 mov_r1,r0 3430 li_r2 &global_env_cell 3431 ld_r2,r2,0 ## r2 = old global alist 3432 li_br &cons 3433 call ## r0 = new alist 3434 3435 li_r2 &global_env_cell 3436 st_r0,r2,0 3437 epilogue_n2 3438 ret 3439 3440 3441 ## ---- lookup_alist(r1=sym, r2=env) -> r0 = pair or nil --------------- 3442 ## Walks alist env returning the (sym . val) cons cell that matches, or 3443 ## nil on miss. Caller distinguishes via tag/nil check. 3444 :lookup_alist 3445 prologue_n3 3446 mov_r3,sp 3447 st_r1,sp,24 ## slot 1 = sym 3448 st_r2,sp,32 ## slot 2 = cursor 3449 3450 :lookup_alist_loop 3451 mov_r3,sp 3452 ld_r2,sp,32 3453 li_r1 %7 3454 li_br &lookup_alist_miss 3455 beq_r2,r1 ## cursor == nil → miss 3456 3457 ## pair = car(cursor) 3458 mov_r1,r2 3459 li_br &car 3460 call ## r0 = (k . v) 3461 3462 st_r0,sp,40 ## slot 3 = pair 3463 3464 mov_r1,r0 3465 li_br &car 3466 call ## r0 = key 3467 3468 mov_r3,sp 3469 ld_r1,sp,24 3470 li_br &lookup_alist_hit 3471 beq_r0,r1 3472 3473 ## advance cursor := cdr(cursor) 3474 ld_r1,sp,32 3475 li_br &cdr 3476 call 3477 st_r0,sp,32 3478 li_br &lookup_alist_loop 3479 b 3480 3481 :lookup_alist_hit 3482 ld_r0,sp,40 3483 epilogue_n3 3484 ret 3485 3486 :lookup_alist_miss 3487 li_r0 %7 3488 epilogue_n3 3489 ret 3490 3491 3492 ## ---- lookup(r1=sym, r2=env) -> r0 = value -------------------------- 3493 ## Local first, global second. Errors out via &err_unbound on miss. 3494 :lookup 3495 prologue_n2 3496 st_r1,sp,24 ## save sym for possible global retry 3497 3498 li_br &lookup_alist 3499 call ## r0 = local pair or nil 3500 3501 li_r1 %7 3502 li_br &lookup_global 3503 beq_r0,r1 3504 3505 mov_r1,r0 3506 li_br &cdr 3507 call ## r0 = value 3508 epilogue_n2 3509 ret 3510 3511 :lookup_global 3512 ld_r1,sp,24 3513 li_r2 &global_env_cell 3514 ld_r2,r2,0 3515 li_br &lookup_alist 3516 call 3517 3518 li_r1 %7 3519 li_br &err_unbound 3520 beq_r0,r1 3521 3522 mov_r1,r0 3523 li_br &cdr 3524 call 3525 epilogue_n2 3526 ret 3527 3528 3529 ## ---- env_extend(r1=names, r2=vals, r3=env) -> r0 = new env --------- 3530 ## For each (name, val) pair, prepends (name . val) to env. Iterative; 3531 ## uses a 4-slot frame because the per-iteration state is (names, 3532 ## vals, env-acc, name-temp) and cons clobbers r0-r3 so name has to 3533 ## live somewhere stable across the val-car call. 3534 :env_extend 3535 prologue_n4 3536 mov_r0,r3 ## save env before clobbering r3 3537 mov_r3,sp 3538 st_r1,sp,24 ## slot 1 = names 3539 st_r2,sp,32 ## slot 2 = vals 3540 st_r0,sp,40 ## slot 3 = env accumulator 3541 3542 :env_extend_loop 3543 mov_r3,sp 3544 ld_r1,sp,24 3545 li_r2 %7 3546 li_br &env_extend_done 3547 beq_r1,r2 ## names == nil → done 3548 3549 ld_r1,sp,32 3550 li_r2 %7 3551 li_br &err_arity 3552 beq_r1,r2 ## vals == nil → arity error 3553 3554 ## name = car(names) 3555 ld_r1,sp,24 3556 li_br &car 3557 call ## r0 = name 3558 st_r0,sp,48 ## slot 4 = name 3559 3560 ## val = car(vals) 3561 ld_r1,sp,32 3562 li_br &car 3563 call ## r0 = val 3564 3565 ## pair = cons(name, val) 3566 mov_r2,r0 3567 ld_r1,sp,48 3568 li_br &cons 3569 call ## r0 = (name . val) 3570 3571 ## env := cons(pair, env) 3572 mov_r1,r0 3573 ld_r2,sp,40 3574 li_br &cons 3575 call ## r0 = new env 3576 3577 st_r0,sp,40 3578 3579 ## names := cdr(names) 3580 ld_r1,sp,24 3581 li_br &cdr 3582 call 3583 st_r0,sp,24 3584 3585 ## vals := cdr(vals) 3586 ld_r1,sp,32 3587 li_br &cdr 3588 call 3589 st_r0,sp,32 3590 3591 li_br &env_extend_loop 3592 b 3593 3594 :env_extend_done 3595 ld_r0,sp,40 3596 epilogue_n4 3597 ret 3598 3599 3600 ## ---- make_closure(r1=params, r2=body, r3=env) -> r0 = tagged ------- 3601 ## 32-byte heap object: [header(type=4) | params | body | env]. Tag=6 3602 ## (110) matches the combined closure/prim tag band. Pre-zeroed BSS 3603 ## heap lets us skip writing the header's low 7 bytes and only stamp 3604 ## byte 7 with the type code. 3605 :make_closure 3606 prologue_n3 3607 mov_r0,r3 ## save env 3608 st_r1,sp,24 ## slot 1 = params 3609 st_r2,sp,32 ## slot 2 = body 3610 st_r0,sp,40 ## slot 3 = env 3611 3612 li_r1 %32 ## 32 bytes 3613 li_br &alloc 3614 call ## r0 = raw ptr 3615 3616 li_r1 %4 3617 sb_r1,r0,7 ## type = 4 (closure) 3618 3619 ld_r1,sp,24 3620 st_r1,r0,8 3621 ld_r1,sp,32 3622 st_r1,r0,16 3623 ld_r1,sp,40 3624 st_r1,r0,24 3625 3626 ori_r0,r0,4 3627 ori_r0,r0,2 ## tag = 0b110 = 6 3628 3629 epilogue_n3 3630 ret 3631 3632 3633 ## ---- make_primitive(r1=code_id, r2=arity, r3=type) -> r0 = tagged -- 3634 ## 16-byte heap object: [header | code_id]. header byte 7 = type 3635 ## (5=fixed, 6=variadic); byte 0 = arity. Shares tag 0b110 with 3636 ## closures so apply's tag check still works; the type byte forks 3637 ## there. Arity is ignored for variadic (type 6) — callers pass 0. 3638 :make_primitive 3639 prologue_n3 3640 mov_r0,r3 ## save type → r0 (frees r3) 3641 st_r1,sp,24 ## slot 1 = code_id 3642 st_r2,sp,32 ## slot 2 = arity 3643 st_r0,sp,40 ## slot 3 = type 3644 3645 li_r1 %16 ## 16 bytes 3646 li_br &alloc 3647 call ## r0 = raw ptr 3648 3649 ld_r1,sp,40 3650 sb_r1,r0,7 ## byte 7 = type 3651 ld_r1,sp,32 3652 sb_r1,r0,0 ## byte 0 = arity 3653 ld_r1,sp,24 3654 st_r1,r0,8 ## +8 = code id 3655 3656 ori_r0,r0,4 3657 ori_r0,r0,2 ## tag = 0b110 = 6 3658 3659 epilogue_n3 3660 ret 3661 3662 3663 ## ---- eval_args(r1=args, r2=env) -> r0 = evaluated args list -------- 3664 ## Recursive map: eval each, cons the results left-to-right. 3665 :eval_args 3666 prologue_n3 3667 mov_r3,sp 3668 st_r1,sp,24 ## slot 1 = args cursor 3669 st_r2,sp,32 ## slot 2 = env 3670 3671 li_r2 %7 3672 li_br &eval_args_done 3673 beq_r1,r2 3674 3675 ## head = eval(car(args), env) 3676 li_br &car 3677 call 3678 mov_r1,r0 3679 ld_r2,sp,32 3680 li_br &eval 3681 call ## r0 = head value 3682 3683 st_r0,sp,40 3684 3685 ## tail = eval_args(cdr(args), env) 3686 ld_r1,sp,24 3687 li_br &cdr 3688 call 3689 mov_r1,r0 3690 ld_r2,sp,32 3691 li_br &eval_args 3692 call ## r0 = tail 3693 3694 mov_r2,r0 3695 ld_r1,sp,40 3696 li_br &cons ## tail: cons is last work in this frame 3697 tail_n3 3698 3699 :eval_args_done 3700 li_r0 %7 3701 epilogue_n3 3702 ret 3703 3704 3705 ## ---- apply(r1=callee, r2=args) -> r0 = result ---------------------- 3706 ## Callee must have tag 0b110 (closure/prim band). Header byte 7 3707 ## discriminates: 4=closure (existing path), 5=prim-fixed, 3708 ## 6=prim-variadic. Frame has 4 slots: callee-then-params, args, 3709 ## body, closure-env (prim paths repurpose slots 3/4 for code_id / 3710 ## expected_arity). 3711 :apply 3712 prologue_n4 3713 mov_r3,sp 3714 st_r1,sp,24 ## slot 1 = callee (tagged) 3715 st_r2,sp,32 ## slot 2 = args 3716 3717 andi_r1,r1,7 3718 li_r0 TAG_PROC 3719 li_br &err_not_callable 3720 bne_r1,r0 3721 3722 mov_r3,sp 3723 ld_r1,sp,24 3724 addi_r1,r1,neg6 ## r1 = raw ptr 3725 3726 ## Fork on header type byte. 3727 lb_r0,r1,7 ## r0 = type 3728 li_r2 TYPE_PRIM_FIXED 3729 li_br &apply_prim_fixed 3730 beq_r0,r2 3731 li_r2 TYPE_PRIM_VARIADIC 3732 li_br &apply_prim_variadic 3733 beq_r0,r2 3734 ## Fall through: type == 4 (closure). 3735 3736 ld_r0,r1,8 ## params 3737 st_r0,sp,24 ## slot 1 = params (overwrite) 3738 ld_r0,r1,16 ## body 3739 st_r0,sp,40 ## slot 3 = body 3740 ld_r0,r1,24 ## closure env 3741 st_r0,sp,48 ## slot 4 = closure env 3742 3743 ## env_extend(params, args, closure_env) 3744 ld_r1,sp,24 3745 ld_r2,sp,32 3746 ld_r3,sp,48 3747 li_br &env_extend 3748 call ## r0 = new env 3749 3750 mov_r2,r0 3751 ld_r1,sp,40 ## r1 = body 3752 li_br &eval 3753 tail_n4 ## Scheme tail call: closure body 3754 3755 3756 ## ---- apply_prim_fixed (r1 = raw prim ptr, inside apply's frame) ---- 3757 ## Loads arity + code_id, marshals args, arity-checks, then unwinds 3758 ## apply's frame and B's to prim_dispatch. The cascade there tail- 3759 ## enters the body with r1=argc, r2=argv — body RETs directly to 3760 ## apply's caller since EPILOGUE_N4 already restored lr. 3761 :apply_prim_fixed 3762 ## r3 is still sp from the MOV above; slot 3/4 are free. 3763 lb_r0,r1,0 ## r0 = expected arity 3764 st_r0,r3,48 ## slot 4 = expected arity 3765 ld_r0,r1,8 ## r0 = code id 3766 st_r0,r3,40 ## slot 3 = code id 3767 3768 ld_r1,r3,32 ## r1 = args 3769 li_br &marshal_argv 3770 call ## r0 = argc, r2 = &prim_argv 3771 3772 mov_r3,sp 3773 ld_r1,sp,48 3774 li_br &err_arity 3775 bne_r0,r1 3776 3777 mov_r1,r0 3778 ld_r3,sp,40 ## r3 = code id 3779 epilogue_n4 3780 li_br &prim_dispatch 3781 b 3782 3783 3784 ## ---- apply_prim_variadic (r1 = raw prim ptr) ---------------------- 3785 :apply_prim_variadic 3786 ld_r0,r1,8 ## r0 = code id 3787 st_r0,r3,40 ## slot 3 = code id 3788 3789 ld_r1,r3,32 ## r1 = args 3790 li_br &marshal_argv 3791 call ## r0 = argc, r2 = &prim_argv 3792 3793 mov_r1,r0 3794 ld_r3,sp,40 ## r3 = code id 3795 epilogue_n4 3796 li_br &prim_dispatch 3797 b 3798 3799 3800 ## ---- marshal_argv(r1 = args list) -> r0 = argc, r2 = &prim_argv --- 3801 ## Walks the tagged-pair list, storing each car into prim_argv[i*8]. 3802 ## Overflow beyond PRIM_ARGV_SLOTS is an error (err_too_many_args). 3803 :marshal_argv 3804 prologue 3805 li_r2 &prim_argv ## r2 = cursor 3806 li_r3 %0 ## r3 = count 3807 3808 :marshal_argv_loop 3809 li_r0 %7 3810 li_br &marshal_argv_done 3811 beq_r1,r0 ## list == nil → done 3812 3813 li_r0 %32 ## PRIM_ARGV_SLOTS = 32 3814 li_br &err_too_many_args 3815 beq_r3,r0 3816 3817 addi_r0,r1,neg2 ## r0 = raw pair ptr 3818 ld_r1,r0,0 ## r1 = car (we'll load cdr next) 3819 st_r1,r2,0 ## *cursor = car 3820 ld_r1,r0,8 ## r1 = cdr → next list 3821 addi_r2,r2,8 ## cursor += 8 3822 addi_r3,r3,1 ## count++ 3823 li_br &marshal_argv_loop 3824 b 3825 3826 :marshal_argv_done 3827 ## Publish the count so the GC root walker only marks the live 3828 ## prefix of prim_argv. Stale slots beyond the current call would 3829 ## otherwise look like roots and pin freed objects across GCs. 3830 li_r0 &prim_argc 3831 st_r3,r0,0 3832 mov_r0,r3 ## r0 = count 3833 li_r2 &prim_argv ## r2 = buffer base 3834 epilogue 3835 ret 3836 3837 3838 ## ---- prim_dispatch(r1=argc, r2=argv, r3=code_id) ------------------- 3839 ## Cascade dispatch — r3 is matched against each code id, and on 3840 ## match we B to the primitive body. Bodies are leaf: r1/r2 are 3841 ## already set up, and RET returns to apply's caller (since apply 3842 ## already ran EPILOGUE_N4 before branching here). 3843 :prim_dispatch 3844 li_br &prim_add 3845 beqz_r3 3846 li_r0 %1 3847 li_br &prim_sub 3848 beq_r3,r0 3849 li_r0 %2 3850 li_br &prim_mul 3851 beq_r3,r0 3852 li_r0 %3 3853 li_br &prim_div 3854 beq_r3,r0 3855 li_r0 %4 3856 li_br &prim_mod 3857 beq_r3,r0 3858 li_r0 %5 3859 li_br &prim_numeq 3860 beq_r3,r0 3861 li_r0 %6 3862 li_br &prim_lt 3863 beq_r3,r0 3864 li_r0 %7 3865 li_br &prim_gt 3866 beq_r3,r0 3867 li_r0 %8 3868 li_br &prim_min 3869 beq_r3,r0 3870 li_r0 %9 3871 li_br &prim_max 3872 beq_r3,r0 3873 li_r0 %10 3874 li_br &prim_bitand 3875 beq_r3,r0 3876 li_r0 %11 3877 li_br &prim_bitor 3878 beq_r3,r0 3879 li_r0 %12 3880 li_br &prim_bitxor 3881 beq_r3,r0 3882 li_r0 %13 3883 li_br &prim_bitnot 3884 beq_r3,r0 3885 li_r0 %14 3886 li_br &prim_ashift 3887 beq_r3,r0 3888 li_r0 %15 3889 li_br &prim_numberp 3890 beq_r3,r0 3891 li_r0 %16 3892 li_br &prim_symbolp 3893 beq_r3,r0 3894 li_r0 %17 3895 li_br &prim_stringp 3896 beq_r3,r0 3897 li_r0 %18 3898 li_br &prim_vectorp 3899 beq_r3,r0 3900 li_r0 %19 3901 li_br &prim_procp 3902 beq_r3,r0 3903 li_r0 %20 3904 li_br &prim_eqp 3905 beq_r3,r0 3906 li_r0 %21 3907 li_br &prim_cons 3908 beq_r3,r0 3909 li_r0 %22 3910 li_br &prim_car 3911 beq_r3,r0 3912 li_r0 %23 3913 li_br &prim_cdr 3914 beq_r3,r0 3915 li_r0 %24 3916 li_br &prim_pairp 3917 beq_r3,r0 3918 li_r0 %25 3919 li_br &prim_nullp 3920 beq_r3,r0 3921 li_r0 %26 3922 li_br &prim_list 3923 beq_r3,r0 3924 li_r0 %27 3925 li_br &prim_append 3926 beq_r3,r0 3927 li_r0 %28 3928 li_br &prim_string_length 3929 beq_r3,r0 3930 li_r0 %29 3931 li_br &prim_string_ref 3932 beq_r3,r0 3933 li_r0 %30 3934 li_br &prim_substring 3935 beq_r3,r0 3936 li_r0 %31 3937 li_br &prim_string_append 3938 beq_r3,r0 3939 li_r0 %32 3940 li_br &prim_string_to_symbol 3941 beq_r3,r0 3942 li_r0 %33 3943 li_br &prim_symbol_to_string 3944 beq_r3,r0 3945 li_r0 %34 3946 li_br &prim_make_vector 3947 beq_r3,r0 3948 li_r0 %35 3949 li_br &prim_vector_ref 3950 beq_r3,r0 3951 li_r0 %36 3952 li_br &prim_vector_set 3953 beq_r3,r0 3954 li_r0 %37 3955 li_br &prim_vector_length 3956 beq_r3,r0 3957 li_r0 %38 3958 li_br &prim_display 3959 beq_r3,r0 3960 li_r0 %39 3961 li_br &prim_write 3962 beq_r3,r0 3963 li_r0 %40 3964 li_br &prim_newline 3965 beq_r3,r0 3966 li_r0 %41 3967 li_br &prim_format 3968 beq_r3,r0 3969 li_r0 %42 3970 li_br &prim_error 3971 beq_r3,r0 3972 li_r0 %43 3973 li_br &prim_read_file 3974 beq_r3,r0 3975 li_r0 %44 3976 li_br &prim_write_file 3977 beq_r3,r0 3978 li_r0 %45 3979 li_br &prim_apply 3980 beq_r3,r0 3981 3982 li_br &err_bad_prim 3983 b 3984 3985 3986 ## ---- Primitive bodies ---------------------------------------------- 3987 ## Convention: entry with r1=argc (raw int), r2=argv (ptr into the 3988 ## prim_argv buffer of tagged values). Leaf code: may use r0-r3 3989 ## freely; must not touch r4-r7 (still hold apply's caller's values 3990 ## since EPILOGUE_N4 ran before this branch). End with RET — that 3991 ## returns to apply's caller, because lr was restored by EPILOGUE_N4. 3992 3993 ## (+ ...) — variadic sum. Tagged fixnum (v<<3)|1: untag each, add 3994 ## to decoded accumulator, retag at end. 3995 :prim_add 3996 li_r3 %0 ## acc = 0 3997 shli_r1,r1,3 3998 add_r1,r1,r2 ## r1 = argv_end 3999 :prim_add_loop 4000 li_br &prim_add_done 4001 beq_r2,r1 4002 ld_r0,r2,0 4003 sari_r0,r0,3 4004 add_r3,r3,r0 4005 addi_r2,r2,8 4006 li_br &prim_add_loop 4007 b 4008 :prim_add_done 4009 shli_r0,r3,3 4010 ori_r0,r0,1 4011 ret 4012 4013 4014 ## (- x) negates; (- x y ...) folds subtraction from the left. 4015 :prim_sub 4016 li_r0 %1 4017 li_br &prim_sub_negate 4018 beq_r1,r0 ## argc == 1 → unary negate 4019 4020 ld_r3,r2,0 4021 sari_r3,r3,3 ## r3 = decoded first 4022 addi_r2,r2,8 4023 addi_r1,r1,neg1 4024 shli_r1,r1,3 4025 add_r1,r1,r2 ## r1 = argv_end 4026 :prim_sub_loop 4027 li_br &prim_sub_done 4028 beq_r2,r1 4029 ld_r0,r2,0 4030 sari_r0,r0,3 4031 sub_r3,r3,r0 4032 addi_r2,r2,8 4033 li_br &prim_sub_loop 4034 b 4035 :prim_sub_done 4036 shli_r0,r3,3 4037 ori_r0,r0,1 4038 ret 4039 :prim_sub_negate 4040 ld_r3,r2,0 4041 sari_r3,r3,3 4042 li_r0 %0 4043 sub_r3,r0,r3 4044 shli_r0,r3,3 4045 ori_r0,r0,1 4046 ret 4047 4048 4049 ## (* ...) — variadic product. Identity 1. 4050 :prim_mul 4051 li_r3 %1 ## acc = 1 4052 shli_r1,r1,3 4053 add_r1,r1,r2 4054 :prim_mul_loop 4055 li_br &prim_mul_done 4056 beq_r2,r1 4057 ld_r0,r2,0 4058 sari_r0,r0,3 4059 mul_r3,r3,r0 4060 addi_r2,r2,8 4061 li_br &prim_mul_loop 4062 b 4063 :prim_mul_done 4064 shli_r0,r3,3 4065 ori_r0,r0,1 4066 ret 4067 4068 4069 ## (/ x y) — binary integer division (signed). 4070 :prim_div 4071 ld_r3,r2,0 4072 sari_r3,r3,3 4073 ld_r0,r2,8 4074 sari_r0,r0,3 4075 div_r3,r3,r0 4076 shli_r0,r3,3 4077 ori_r0,r0,1 4078 ret 4079 4080 4081 ## (% x y) — binary signed remainder. Scheme calls this `modulo` 4082 ## / `remainder`; we bind the primitive as `%` to stay fixnum-only 4083 ## and avoid the sign-convention split. 4084 :prim_mod 4085 ld_r3,r2,0 4086 sari_r3,r3,3 4087 ld_r0,r2,8 4088 sari_r0,r0,3 4089 rem_r3,r3,r0 4090 shli_r0,r3,3 4091 ori_r0,r0,1 4092 ret 4093 4094 4095 ## (= x y) — binary fixnum equality. Because tagged fixnums share 4096 ## the same low-3-bit tag (001) and the payload is bit-identical 4097 ## for equal values, we compare the tagged words directly. 4098 :prim_numeq 4099 ld_r3,r2,0 4100 ld_r0,r2,8 4101 li_br &prim_true 4102 beq_r3,r0 4103 li_r0 FALSE ## #f 4104 ret 4105 4106 ## Shared "return #t" tail used by comparison/predicate primitives. 4107 :prim_true 4108 li_r0 TRUE 4109 ret 4110 4111 4112 ## (< x y). Tagged-fixnum order preserves decoded order (shift-by-3 4113 ## is monotone for 61-bit signed values). 4114 :prim_lt 4115 ld_r3,r2,0 4116 ld_r0,r2,8 4117 li_br &prim_true 4118 blt_r3,r0 4119 li_r0 %23 4120 ret 4121 4122 4123 ## (> x y) ≡ (< y x). 4124 :prim_gt 4125 ld_r3,r2,0 4126 ld_r0,r2,8 4127 li_br &prim_true 4128 blt_r0,r3 4129 li_r0 %23 4130 ret 4131 4132 4133 ## (<=, >=, zero?, negative?, positive?, abs) live in the Scheme 4134 ## prelude now — see src/prelude.scm. 4135 4136 4137 ## (min ...) — variadic; first arg is seed, each later arg replaces 4138 ## acc if strictly smaller. Comparing tagged fixnums directly keeps 4139 ## the body untagging-free. 4140 :prim_min 4141 ld_r3,r2,0 ## r3 = acc (tagged) 4142 addi_r2,r2,8 4143 addi_r1,r1,neg1 4144 shli_r1,r1,3 4145 add_r1,r1,r2 4146 :prim_min_loop 4147 li_br &prim_min_done 4148 beq_r2,r1 4149 ld_r0,r2,0 4150 li_br &prim_min_skip 4151 blt_r3,r0 ## acc < x → keep acc 4152 mov_r3,r0 4153 :prim_min_skip 4154 addi_r2,r2,8 4155 li_br &prim_min_loop 4156 b 4157 :prim_min_done 4158 mov_r0,r3 4159 ret 4160 4161 4162 ## (max ...) — acc replaces on strictly greater. 4163 :prim_max 4164 ld_r3,r2,0 4165 addi_r2,r2,8 4166 addi_r1,r1,neg1 4167 shli_r1,r1,3 4168 add_r1,r1,r2 4169 :prim_max_loop 4170 li_br &prim_max_done 4171 beq_r2,r1 4172 ld_r0,r2,0 4173 li_br &prim_max_skip 4174 blt_r0,r3 ## x < acc → keep acc 4175 mov_r3,r0 4176 :prim_max_skip 4177 addi_r2,r2,8 4178 li_br &prim_max_loop 4179 b 4180 :prim_max_done 4181 mov_r0,r3 4182 ret 4183 4184 4185 ## (bit-and ...) — variadic fold. Identity is all-ones; we seed the 4186 ## decoded accumulator with -1 which ANDs as identity. Tag bits are 4187 ## preserved through AND on tagged fixnums, but we still untag/retag 4188 ## to match the shape of the other variadic bit ops. 4189 :prim_bitand 4190 li_r3 %0 4191 addi_r3,r3,neg1 ## r3 = -1 4192 shli_r1,r1,3 4193 add_r1,r1,r2 4194 :prim_bitand_loop 4195 li_br &prim_bitand_done 4196 beq_r2,r1 4197 ld_r0,r2,0 4198 sari_r0,r0,3 4199 and_r3,r3,r0 4200 addi_r2,r2,8 4201 li_br &prim_bitand_loop 4202 b 4203 :prim_bitand_done 4204 shli_r0,r3,3 4205 ori_r0,r0,1 4206 ret 4207 4208 4209 ## (bit-or ...) — identity 0. 4210 :prim_bitor 4211 li_r3 %0 4212 shli_r1,r1,3 4213 add_r1,r1,r2 4214 :prim_bitor_loop 4215 li_br &prim_bitor_done 4216 beq_r2,r1 4217 ld_r0,r2,0 4218 sari_r0,r0,3 4219 or_r3,r3,r0 4220 addi_r2,r2,8 4221 li_br &prim_bitor_loop 4222 b 4223 :prim_bitor_done 4224 shli_r0,r3,3 4225 ori_r0,r0,1 4226 ret 4227 4228 4229 ## (bit-xor ...) — identity 0. 4230 :prim_bitxor 4231 li_r3 %0 4232 shli_r1,r1,3 4233 add_r1,r1,r2 4234 :prim_bitxor_loop 4235 li_br &prim_bitxor_done 4236 beq_r2,r1 4237 ld_r0,r2,0 4238 sari_r0,r0,3 4239 xor_r3,r3,r0 4240 addi_r2,r2,8 4241 li_br &prim_bitxor_loop 4242 b 4243 :prim_bitxor_done 4244 shli_r0,r3,3 4245 ori_r0,r0,1 4246 ret 4247 4248 4249 ## (bit-not x) — ~x = -1 - x. 4250 :prim_bitnot 4251 ld_r3,r2,0 4252 sari_r3,r3,3 4253 li_r0 %0 4254 addi_r0,r0,neg1 ## r0 = -1 4255 sub_r3,r0,r3 ## r3 = -1 - r3 4256 shli_r0,r3,3 4257 ori_r0,r0,1 4258 ret 4259 4260 4261 ## (arithmetic-shift n k) — k>=0 left shift, k<0 arithmetic right. 4262 :prim_ashift 4263 ld_r3,r2,0 4264 sari_r3,r3,3 ## r3 = n (decoded) 4265 ld_r0,r2,8 4266 sari_r0,r0,3 ## r0 = k (decoded, signed) 4267 li_br &prim_ashift_neg 4268 bltz_r0 ## k < 0 → right shift by -k 4269 shl_r3,r3,r0 4270 li_br &prim_ashift_done 4271 b 4272 :prim_ashift_neg 4273 li_r1 %0 4274 sub_r0,r1,r0 ## r0 = -k 4275 sar_r3,r3,r0 4276 :prim_ashift_done 4277 shli_r0,r3,3 4278 ori_r0,r0,1 4279 ret 4280 4281 4282 ## (number? x) — true iff tag is 1 (fixnum). 4283 :prim_numberp 4284 ld_r3,r2,0 4285 andi_r3,r3,7 4286 li_r0 TAG_FIXNUM 4287 li_br &prim_true 4288 beq_r3,r0 4289 li_r0 FALSE 4290 ret 4291 4292 4293 ## (symbol? x) — tag is 5. 4294 :prim_symbolp 4295 ld_r3,r2,0 4296 andi_r3,r3,7 4297 li_r0 TAG_SYMBOL 4298 li_br &prim_true 4299 beq_r3,r0 4300 li_r0 FALSE 4301 ret 4302 4303 4304 ## (string? x) — tag is 4. 4305 :prim_stringp 4306 ld_r3,r2,0 4307 andi_r3,r3,7 4308 li_r0 TAG_STRING 4309 li_br &prim_true 4310 beq_r3,r0 4311 li_r0 FALSE 4312 ret 4313 4314 4315 ## (vector? x) — tag is 3. 4316 :prim_vectorp 4317 ld_r3,r2,0 4318 andi_r3,r3,7 4319 li_r0 TAG_VECTOR 4320 li_br &prim_true 4321 beq_r3,r0 4322 li_r0 FALSE 4323 ret 4324 4325 4326 ## (procedure? x) — tag 6 AND header type ∈ {4,5,6}. Any of the three 4327 ## heads (closure, prim-fixed, prim-variadic) count; the only other 4328 ## tag-6 value shape (if we add one later) would need explicit 4329 ## enumeration here. 4330 :prim_procp 4331 ld_r3,r2,0 4332 mov_r0,r3 4333 andi_r0,r0,7 4334 li_r1 TAG_PROC 4335 li_br &prim_procp_false 4336 bne_r0,r1 ## tag != 6 → #f 4337 4338 addi_r3,r3,neg6 ## strip tag 4339 lb_r0,r3,7 ## r0 = type byte 4340 li_r1 TYPE_CLOSURE 4341 li_br &prim_true 4342 beq_r0,r1 4343 li_r1 TYPE_PRIM_FIXED 4344 li_br &prim_true 4345 beq_r0,r1 4346 li_r1 TYPE_PRIM_VARIADIC 4347 li_br &prim_true 4348 beq_r0,r1 4349 :prim_procp_false 4350 li_r0 FALSE 4351 ret 4352 4353 4354 ## (eq? x y) — pointer/bit identity. Fixnums, interned symbols, and 4355 ## singletons all compare correctly this way; pairs/strings/vectors/ 4356 ## closures compare by heap address (distinct allocations are #f). 4357 :prim_eqp 4358 ld_r3,r2,0 4359 ld_r0,r2,8 4360 li_br &prim_true 4361 beq_r3,r0 4362 li_r0 FALSE 4363 ret 4364 4365 4366 ## ---- Step-10d list-core primitives --------------------------------- 4367 ## (cons a d) — wraps the internal `cons` helper which calls alloc; 4368 ## need PROLOGUE to save lr across the call. 4369 :prim_cons 4370 prologue 4371 ld_r1,r2,0 ## r1 = car arg 4372 ld_r2,r2,8 ## r2 = cdr arg (uses old r2 base) 4373 li_br &cons 4374 call 4375 epilogue 4376 ret 4377 4378 4379 ## (car p) — unsafe (no tag check). Inlined: strip 0b010 tag, load +0. 4380 :prim_car 4381 ld_r1,r2,0 4382 addi_r1,r1,neg2 4383 ld_r0,r1,0 4384 ret 4385 4386 4387 ## (cdr p) — symmetric. 4388 :prim_cdr 4389 ld_r1,r2,0 4390 addi_r1,r1,neg2 4391 ld_r0,r1,8 4392 ret 4393 4394 4395 ## (pair? x) — tag is 2. 4396 :prim_pairp 4397 ld_r3,r2,0 4398 andi_r3,r3,7 4399 li_r0 TAG_PAIR 4400 li_br &prim_true 4401 beq_r3,r0 4402 li_r0 %23 4403 ret 4404 4405 4406 ## (null? x) — equals nil singleton (0x07). 4407 :prim_nullp 4408 ld_r3,r2,0 4409 li_r0 NIL 4410 li_br &prim_true 4411 beq_r3,r0 4412 li_r0 %23 4413 ret 4414 4415 4416 ## (list ...) — variadic; build (a b c ...) by walking argv from the 4417 ## tail and consing onto an accumulator (initially nil). Walking from 4418 ## the tail avoids needing a reverse pass. 4419 :prim_list 4420 prologue_n3 4421 mov_r3,sp 4422 li_r0 NIL ## nil 4423 st_r0,sp,24 ## slot1 = accumulator 4424 st_r2,sp,40 ## slot3 = base 4425 shli_r1,r1,3 ## r1 = argc * 8 4426 add_r1,r1,r2 ## r1 = end ptr (one past last) 4427 st_r1,sp,32 ## slot2 = cursor 4428 :prim_list_loop 4429 mov_r3,sp 4430 ld_r0,sp,32 ## cursor 4431 ld_r1,sp,40 ## base 4432 li_br &prim_list_done 4433 beq_r0,r1 4434 addi_r0,r0,neg8 ## cursor -= 8 4435 st_r0,sp,32 4436 ld_r1,r0,0 ## r1 = arg 4437 ld_r2,sp,24 ## r2 = accumulator 4438 li_br &cons 4439 call 4440 st_r0,sp,24 ## accumulator = new pair 4441 li_br &prim_list_loop 4442 b 4443 :prim_list_done 4444 ld_r0,sp,24 4445 epilogue_n3 4446 ret 4447 4448 4449 ## (length, list?) live in the Scheme prelude now — see 4450 ## src/prelude.scm. 4451 4452 4453 ## append_one(r1=xs, r2=ys) -> r0 = xs ++ ys. Copies xs's spine; ys 4454 ## is shared by reference. Recursive — bounded by xs's length. 4455 :append_one 4456 prologue_n2 4457 li_r0 %7 4458 li_br &append_one_base 4459 beq_r1,r0 4460 st_r1,sp,24 ## save xs 4461 st_r2,sp,32 ## save ys 4462 addi_r0,r1,neg2 4463 ld_r1,r0,8 ## r1 = cdr(xs) 4464 li_br &append_one 4465 call ## r0 = appended tail 4466 mov_r2,r0 4467 ld_r0,sp,24 4468 addi_r0,r0,neg2 4469 ld_r1,r0,0 ## r1 = car(xs) 4470 li_br &cons 4471 call 4472 epilogue_n2 4473 ret 4474 :append_one_base 4475 mov_r0,r2 4476 epilogue_n2 4477 ret 4478 4479 4480 ## (append ...) — variadic; the last argument is shared (not copied), 4481 ## all earlier arguments have their spines copied. Right-to-left fold. 4482 :prim_append 4483 prologue_n3 4484 li_br &prim_append_zero 4485 beqz_r1 4486 mov_r3,sp 4487 st_r2,sp,24 ## slot1 = argv base 4488 addi_r1,r1,neg1 ## r1 = argc-1 4489 shli_r1,r1,3 ## r1 = (argc-1)*8 4490 add_r1,r1,r2 ## r1 = &argv[argc-1] 4491 st_r1,sp,40 ## slot3 = cursor 4492 ld_r0,r1,0 ## r0 = last arg 4493 st_r0,sp,32 ## slot2 = result 4494 :prim_append_loop 4495 mov_r3,sp 4496 ld_r0,sp,40 ## cursor 4497 ld_r1,sp,24 ## base 4498 li_br &prim_append_done 4499 beq_r0,r1 4500 addi_r0,r0,neg8 4501 st_r0,sp,40 4502 ld_r1,r0,0 ## r1 = arg at cursor 4503 ld_r2,sp,32 ## r2 = result 4504 li_br &append_one 4505 call 4506 st_r0,sp,32 4507 li_br &prim_append_loop 4508 b 4509 :prim_append_done 4510 ld_r0,sp,32 4511 epilogue_n3 4512 ret 4513 :prim_append_zero 4514 li_r0 NIL 4515 epilogue_n3 4516 ret 4517 4518 4519 ## (reverse, assoc, member) live in the Scheme prelude now — see 4520 ## src/prelude.scm. 4521 4522 4523 ## ---- (string-length s) — fixnum length out of header --------------- 4524 :prim_string_length 4525 ld_r1,r2,0 4526 addi_r1,r1,neg4 ## raw header 4527 ld_r0,r1,0 4528 shli_r0,r0,16 4529 shri_r0,r0,16 ## low 48b = length 4530 shli_r0,r0,3 4531 ori_r0,r0,1 ## fixnum encode 4532 ret 4533 4534 4535 ## ---- (string-ref s i) — zero-extended byte as fixnum --------------- 4536 :prim_string_ref 4537 ld_r1,r2,0 ## tagged string 4538 ld_r0,r2,8 ## tagged idx 4539 sari_r0,r0,3 ## raw idx 4540 addi_r1,r1,neg4 ## raw header 4541 addi_r1,r1,8 ## payload base 4542 add_r1,r1,r0 ## r1 = payload + idx 4543 lb_r0,r1,0 4544 shli_r0,r0,3 4545 ori_r0,r0,1 4546 ret 4547 4548 4549 ## ---- (substring s start end) — copies s[start:end] ----------------- 4550 :prim_substring 4551 prologue_n3 4552 st_r6,sp,24 4553 st_r7,sp,32 4554 4555 ld_r1,r2,0 ## tagged string 4556 ld_r0,r2,8 ## tagged start 4557 ld_r3,r2,16 ## tagged end 4558 sari_r0,r0,3 ## raw start 4559 sari_r3,r3,3 ## raw end 4560 4561 addi_r1,r1,neg4 ## raw header 4562 addi_r1,r1,8 ## payload base 4563 sub_r3,r3,r0 ## r3 = end - start (save len before r0 clobber) 4564 mov_r7,r3 ## r7 = len 4565 mov_r2,r0 ## r2 = start 4566 add_r6,r1,r2 ## src = payload + start 4567 4568 mov_r1,r6 4569 mov_r2,r7 4570 li_br &make_string 4571 call 4572 4573 ld_r6,sp,24 4574 ld_r7,sp,32 4575 epilogue_n3 4576 ret 4577 4578 4579 ## ---- (string-append ...) — variadic concat, two-pass --------------- 4580 ## Pass 1 walks each arg's header to total length; pass 2 alloc_string 4581 ## then byte_copy each payload. argv is always &prim_argv so we don't 4582 ## need to spill the r2 it came in on. 4583 :prim_string_append 4584 prologue_n4 4585 st_r6,sp,24 ## save r6 (cursor/total reuse) 4586 st_r7,sp,32 ## save r7 (i) 4587 st_r1,sp,40 ## slot3 = argc 4588 4589 li_r6 %0 ## r6 = total length 4590 li_r7 %0 ## r7 = i 4591 :psa_lp1 4592 mov_r3,sp 4593 ld_r0,r3,40 ## argc 4594 li_br &psa_done1 4595 beq_r7,r0 4596 4597 li_r0 &prim_argv 4598 shli_r3,r7,3 4599 add_r0,r0,r3 4600 ld_r0,r0,0 ## tagged str 4601 addi_r0,r0,neg4 4602 ld_r0,r0,0 ## header 4603 shli_r0,r0,16 4604 shri_r0,r0,16 ## len 4605 add_r6,r6,r0 4606 addi_r7,r7,1 4607 li_br &psa_lp1 4608 b 4609 4610 :psa_done1 4611 mov_r1,r6 ## total len 4612 li_br &alloc_string 4613 call ## r0 = tagged result 4614 mov_r3,sp 4615 st_r0,r3,48 ## slot4 = tagged result 4616 4617 ## r6 = payload cursor 4618 addi_r6,r0,neg4 4619 addi_r6,r6,8 4620 li_r7 %0 ## i = 0 4621 4622 :psa_lp2 4623 mov_r3,sp 4624 ld_r0,r3,40 4625 li_br &psa_done2 4626 beq_r7,r0 4627 4628 li_r0 &prim_argv 4629 shli_r3,r7,3 4630 add_r0,r0,r3 4631 ld_r2,r0,0 ## tagged str 4632 addi_r2,r2,neg4 ## raw header 4633 ld_r3,r2,0 4634 shli_r3,r3,32 4635 shri_r3,r3,32 ## len 4636 addi_r2,r2,8 ## src payload 4637 mov_r1,r6 ## dst 4638 li_br &byte_copy 4639 call ## r0 = dst end 4640 mov_r6,r0 4641 addi_r7,r7,1 4642 li_br &psa_lp2 4643 b 4644 4645 :psa_done2 4646 mov_r3,sp 4647 ld_r0,r3,48 4648 ld_r6,sp,24 4649 ld_r7,sp,32 4650 epilogue_n4 4651 ret 4652 4653 4654 ## ---- (string->symbol s) — intern the string's bytes ---------------- 4655 :prim_string_to_symbol 4656 prologue 4657 ld_r0,r2,0 4658 addi_r0,r0,neg4 4659 ld_r2,r0,0 4660 shli_r2,r2,16 4661 shri_r2,r2,16 ## len 4662 addi_r1,r0,8 ## payload 4663 li_br &intern 4664 call 4665 epilogue 4666 ret 4667 4668 4669 ## ---- (symbol->string sym) — copy sym name into a fresh string ----- 4670 :prim_symbol_to_string 4671 prologue 4672 ld_r0,r2,0 4673 addi_r0,r0,neg5 ## raw sym ptr 4674 ld_r2,r0,0 4675 shli_r2,r2,16 4676 shri_r2,r2,16 ## len 4677 addi_r1,r0,8 ## src payload 4678 li_br &make_string 4679 call 4680 epilogue 4681 ret 4682 4683 4684 ## ---- (make-vector n init) — n-slot vector filled with init -------- 4685 :prim_make_vector 4686 prologue 4687 ld_r1,r2,0 ## tagged n 4688 sari_r1,r1,3 ## raw n 4689 ld_r2,r2,8 ## init (tagged) 4690 li_br &make_vector 4691 call 4692 epilogue 4693 ret 4694 4695 4696 ## ---- (vector-ref v i) — unsafe slot read --------------------------- 4697 :prim_vector_ref 4698 ld_r1,r2,0 4699 ld_r3,r2,8 4700 sari_r3,r3,3 ## raw idx 4701 addi_r1,r1,neg3 ## raw vec 4702 addi_r1,r1,8 ## payload base 4703 shli_r3,r3,3 ## idx*8 4704 mov_r2,r3 ## stage in r2 (no ADD_R1_R1_R3) 4705 add_r1,r1,r2 4706 ld_r0,r1,0 4707 ret 4708 4709 4710 ## ---- (vector-set! v i val) -> unspec -------------------------------- 4711 :prim_vector_set 4712 ld_r1,r2,0 4713 ld_r3,r2,8 4714 ld_r0,r2,16 4715 sari_r3,r3,3 4716 addi_r1,r1,neg3 4717 addi_r1,r1,8 4718 shli_r3,r3,3 4719 mov_r2,r3 ## stage idx*8 in r2 4720 add_r1,r1,r2 4721 st_r0,r1,0 4722 li_r0 UNSPEC 4723 ret 4724 4725 4726 ## ---- (vector-length v) — fixnum slot count ------------------------- 4727 :prim_vector_length 4728 ld_r1,r2,0 4729 addi_r1,r1,neg3 4730 ld_r0,r1,0 4731 shli_r0,r0,16 4732 shri_r0,r0,16 4733 shli_r0,r0,3 4734 ori_r0,r0,1 4735 ret 4736 4737 4738 ## (vector->list, list->vector) live in the Scheme prelude now — 4739 ## see src/prelude.scm. 4740 4741 4742 ## ---- (display x) — runtime printer (unspec result) ------------------ 4743 :prim_display 4744 prologue 4745 ld_r1,r2,0 4746 li_br &display 4747 call 4748 li_r0 UNSPEC 4749 epilogue 4750 ret 4751 4752 4753 ## ---- (write x) — quoting printer ------------------------------------ 4754 :prim_write 4755 prologue 4756 ld_r1,r2,0 4757 li_br &write 4758 call 4759 li_r0 UNSPEC 4760 epilogue 4761 ret 4762 4763 4764 ## ---- (newline) — emit '\n' ----------------------------------------- 4765 :prim_newline 4766 prologue 4767 li_r1 %10 4768 li_br &putc 4769 call 4770 li_r0 UNSPEC 4771 epilogue 4772 ret 4773 4774 4775 ## ---- (format fmt args...) — minimal printf-ish --------------------- 4776 ## Supported directives: ~a (display), ~s (write), ~d (decimal fixnum), 4777 ## ~% (newline), ~~ (literal '~'). Unknown directives consume their char 4778 ## silently. argv is &prim_argv; arg_index counts past the fmt string. 4779 :prim_format 4780 prologue_n4 4781 st_r6,sp,24 4782 st_r7,sp,32 4783 4784 mov_r3,sp 4785 st_r1,r3,40 ## slot3 = argc 4786 li_r0 %1 4787 st_r0,r3,48 ## slot4 = arg_index (skip fmt) 4788 4789 ld_r0,r2,0 ## tagged fmt string 4790 addi_r0,r0,neg4 4791 ld_r7,r0,0 4792 shli_r7,r7,16 4793 shri_r7,r7,16 ## r7 = len 4794 addi_r6,r0,8 ## r6 = cursor 4795 4796 :pf_loop 4797 li_br &pf_done 4798 beqz_r7 4799 4800 lb_r0,r6,0 4801 li_r1 %126 ## '~' 4802 li_br &pf_directive 4803 beq_r0,r1 4804 4805 mov_r1,r0 4806 li_br &putc 4807 call 4808 addi_r6,r6,1 4809 addi_r7,r7,neg1 4810 li_br &pf_loop 4811 b 4812 4813 :pf_directive 4814 addi_r6,r6,1 4815 addi_r7,r7,neg1 4816 li_br &pf_done 4817 beqz_r7 4818 4819 lb_r0,r6,0 4820 addi_r6,r6,1 4821 addi_r7,r7,neg1 4822 4823 li_r1 %37 ## '%' 4824 li_br &pf_newline 4825 beq_r0,r1 4826 4827 li_r1 %126 ## '~' 4828 li_br &pf_emit_tilde 4829 beq_r0,r1 4830 4831 ## a/s/d — fetch arg 4832 li_r1 &prim_argv 4833 mov_r3,sp 4834 ld_r2,r3,48 4835 shli_r2,r2,3 4836 add_r1,r1,r2 4837 ld_r1,r1,0 ## r1 = tagged arg 4838 ld_r2,r3,48 4839 addi_r2,r2,1 4840 st_r2,r3,48 4841 4842 li_r2 %97 ## 'a' 4843 li_br &pf_dir_a 4844 beq_r0,r2 4845 li_r2 %115 ## 's' 4846 li_br &pf_dir_s 4847 beq_r0,r2 4848 li_r2 %100 ## 'd' 4849 li_br &pf_dir_d 4850 beq_r0,r2 4851 4852 li_br &pf_loop 4853 b 4854 4855 :pf_dir_a 4856 li_br &display 4857 call 4858 li_br &pf_loop 4859 b 4860 4861 :pf_dir_s 4862 li_br &write 4863 call 4864 li_br &pf_loop 4865 b 4866 4867 :pf_dir_d 4868 sari_r1,r1,3 4869 li_r2 %1 ## fd = 1 (stdout) 4870 li_br &display_uint 4871 call 4872 li_br &pf_loop 4873 b 4874 4875 :pf_newline 4876 li_r1 %10 4877 li_br &putc 4878 call 4879 li_br &pf_loop 4880 b 4881 4882 :pf_emit_tilde 4883 li_r1 %126 4884 li_br &putc 4885 call 4886 li_br &pf_loop 4887 b 4888 4889 :pf_done 4890 li_r0 UNSPEC 4891 ld_r6,sp,24 4892 ld_r7,sp,32 4893 epilogue_n4 4894 ret 4895 4896 4897 ## ---- (error msg) — print and exit(1) ------------------------------- 4898 :prim_error 4899 ld_r0,r2,0 4900 addi_r0,r0,neg4 4901 ld_r2,r0,0 4902 shli_r2,r2,16 4903 shri_r2,r2,16 4904 addi_r1,r0,8 4905 li_br &error 4906 b 4907 4908 4909 ## ---- (read-file path) — slurp into a fresh string ------------------ 4910 ## Uses io_buf as scratch (512B); make_string copies into the heap. 4911 :prim_read_file 4912 prologue_n3 4913 st_r6,sp,24 4914 st_r7,sp,32 4915 4916 ld_r1,r2,0 4917 li_br &string_to_c_path 4918 call ## r0 = &path_buf 4919 4920 mov_r2,r0 4921 li_r0 sys_openat 4922 li_r1 %-100 4923 li_r3 %0 4924 li_r4 %0 4925 syscall ## r0 = fd or err 4926 4927 li_br &err_open 4928 bltz_r0 4929 4930 mov_r6,r0 ## r6 = fd 4931 4932 mov_r1,r6 4933 li_r2 &io_buf 4934 li_r3 %512 4935 li_br &read_file_all 4936 call ## r0 = bytes_read 4937 mov_r7,r0 4938 4939 li_r0 sys_close 4940 mov_r1,r6 4941 syscall 4942 4943 li_r1 &io_buf 4944 mov_r2,r7 4945 li_br &make_string 4946 call 4947 4948 ld_r6,sp,24 4949 ld_r7,sp,32 4950 epilogue_n3 4951 ret 4952 4953 4954 ## ---- (write-file path data) — overwrite/truncate ------------------ 4955 :prim_write_file 4956 prologue_n3 4957 st_r6,sp,24 4958 st_r7,sp,32 4959 4960 ld_r7,r2,8 ## r7 = tagged data string 4961 4962 ld_r1,r2,0 ## tagged path 4963 li_br &string_to_c_path 4964 call ## r0 = &path_buf 4965 4966 mov_r2,r0 4967 li_r0 sys_openat 4968 li_r1 %-100 4969 li_r3 %577 ## O_WRONLY|O_CREAT|O_TRUNC 4970 li_r4 %420 ## mode 0644 4971 syscall ## r0 = fd 4972 4973 li_br &err_open 4974 bltz_r0 4975 4976 mov_r6,r0 ## r6 = fd 4977 4978 mov_r1,r7 4979 addi_r1,r1,neg4 ## raw header 4980 ld_r3,r1,0 4981 shli_r3,r3,32 4982 shri_r3,r3,32 ## len 4983 addi_r2,r1,8 ## payload 4984 mov_r1,r6 ## fd 4985 li_br &write_file_all 4986 call 4987 4988 li_r0 sys_close 4989 mov_r1,r6 4990 syscall 4991 4992 li_r0 UNSPEC 4993 ld_r6,sp,24 4994 ld_r7,sp,32 4995 epilogue_n3 4996 ret 4997 4998 4999 ## (equal?) lives in the Scheme prelude now — see src/prelude.scm. 5000 5001 5002 ## ---- (apply proc arg ... last-list) --------------------------------- 5003 ## argc >= 2. Build args = (arg1 ... arg{n-1} ++ last-list), then call 5004 ## the C-level apply with (callee, args). 5005 :prim_apply 5006 prologue_n4 5007 st_r6,sp,24 5008 st_r7,sp,32 5009 5010 ld_r6,r2,0 ## r6 = callee 5011 5012 ## acc = argv[argc-1] (the trailing list) 5013 addi_r3,r1,neg1 5014 shli_r0,r3,3 5015 li_r7 &prim_argv 5016 add_r7,r7,r0 5017 ld_r7,r7,0 ## r7 = acc 5018 5019 addi_r3,r1,neg2 ## i = argc - 2 5020 :papply_lp 5021 li_r0 %1 5022 li_br &papply_done 5023 blt_r3,r0 ## i < 1 → done 5024 5025 shli_r0,r3,3 5026 li_r1 &prim_argv 5027 add_r1,r1,r0 5028 ld_r1,r1,0 ## r1 = arg 5029 5030 st_r3,sp,40 ## save i 5031 5032 mov_r2,r7 5033 li_br &cons 5034 call ## r0 = (arg . acc) 5035 mov_r7,r0 5036 5037 mov_r3,sp 5038 ld_r3,r3,40 5039 addi_r3,r3,neg1 5040 li_br &papply_lp 5041 b 5042 5043 :papply_done 5044 mov_r1,r6 5045 mov_r2,r7 5046 li_br &apply 5047 call 5048 5049 ld_r6,sp,24 5050 ld_r7,sp,32 5051 epilogue_n4 5052 ret 5053 5054 5055 5056 ## ---- eval(r1=expr, r2=env) -> r0 = value --------------------------- 5057 ## Self-evaluating: nil/fixnum/string/closure. Symbols → lookup. Pairs 5058 ## → eval_pair (special-form or application dispatch). 5059 :eval 5060 prologue_n3 5061 mov_r3,sp 5062 st_r1,sp,24 ## slot 1 = expr 5063 st_r2,sp,32 ## slot 2 = env 5064 5065 li_r2 %7 5066 li_br &eval_self_slot1 5067 beq_r1,r2 5068 5069 andi_r1,r1,7 ## r1 = tag 5070 5071 li_r2 %1 5072 li_br &eval_self_slot1 5073 beq_r1,r2 ## fixnum 5074 5075 li_r2 %5 5076 li_br &eval_sym 5077 beq_r1,r2 5078 5079 li_r2 %2 5080 li_br &eval_pair 5081 beq_r1,r2 5082 5083 ## Other tags (string, closure) self-evaluate. 5084 li_br &eval_self_slot1 5085 b 5086 5087 :eval_self_slot1 5088 ld_r0,sp,24 5089 epilogue_n3 5090 ret 5091 5092 :eval_sym 5093 ld_r1,sp,24 5094 ld_r2,sp,32 5095 li_br &lookup 5096 tail_n3 5097 5098 :eval_pair 5099 ## Compound expression. Dispatch on car against cached sym_* 5100 ## pointers; otherwise treat as function application. 5101 ld_r1,sp,24 5102 li_br &car 5103 call ## r0 = callee-expr 5104 5105 li_r1 &sym_quote 5106 ld_r1,r1,0 5107 li_br &eval_quote 5108 beq_r0,r1 5109 5110 li_r1 &sym_if 5111 ld_r1,r1,0 5112 li_br &eval_if 5113 beq_r0,r1 5114 5115 li_r1 &sym_begin 5116 ld_r1,r1,0 5117 li_br &eval_begin 5118 beq_r0,r1 5119 5120 li_r1 &sym_lambda 5121 ld_r1,r1,0 5122 li_br &eval_lambda 5123 beq_r0,r1 5124 5125 li_r1 &sym_define 5126 ld_r1,r1,0 5127 li_br &eval_define 5128 beq_r0,r1 5129 5130 li_r1 &sym_set 5131 ld_r1,r1,0 5132 li_br &eval_set 5133 beq_r0,r1 5134 5135 li_r1 &sym_let 5136 ld_r1,r1,0 5137 li_br &eval_let 5138 beq_r0,r1 5139 5140 li_r1 &sym_letstar 5141 ld_r1,r1,0 5142 li_br &eval_letstar 5143 beq_r0,r1 5144 5145 li_r1 &sym_letrec 5146 ld_r1,r1,0 5147 li_br &eval_letrec 5148 beq_r0,r1 5149 5150 li_r1 &sym_cond 5151 ld_r1,r1,0 5152 li_br &eval_cond 5153 beq_r0,r1 5154 5155 li_r1 &sym_quasiquote 5156 ld_r1,r1,0 5157 li_br &eval_quasiquote 5158 beq_r0,r1 5159 5160 ## Application: callee = eval(callee-expr, env) 5161 mov_r1,r0 5162 ld_r2,sp,32 5163 li_br &eval 5164 call ## r0 = callee value 5165 5166 st_r0,sp,40 ## slot 3 = callee 5167 5168 ## args = eval_args(cdr(expr), env) 5169 ld_r1,sp,24 5170 li_br &cdr 5171 call 5172 mov_r1,r0 5173 ld_r2,sp,32 5174 li_br &eval_args 5175 call ## r0 = args list 5176 5177 mov_r2,r0 5178 ld_r1,sp,40 5179 li_br &apply 5180 tail_n3 ## Scheme tail call: application 5181 5182 5183 ## ---- eval_quote / eval_if / eval_begin ----------------------------- 5184 ## All run inside eval's PROLOGUE_N3 frame: slot 1 = expr, slot 2 = 5185 ## env, slot 3 = per-form scratch. 5186 :eval_quote 5187 ld_r1,sp,24 5188 li_br &cdr 5189 call ## r0 = (x) 5190 mov_r1,r0 5191 li_br &car 5192 tail_n3 ## Scheme tail: quote result = datum 5193 5194 :eval_if 5195 ## (if cond then else). Save (then else) tail into slot 3, eval 5196 ## cond, branch to the correct arm. 5197 ld_r1,sp,24 5198 li_br &cdr 5199 call ## r0 = (cond then else) 5200 mov_r1,r0 5201 li_br &cdr 5202 call ## r0 = (then else) 5203 st_r0,sp,40 5204 5205 ld_r1,sp,24 5206 li_br &cdr 5207 call ## r0 = (cond then else) 5208 mov_r1,r0 5209 li_br &car 5210 call ## r0 = cond expr 5211 mov_r1,r0 5212 ld_r2,sp,32 5213 li_br &eval 5214 call ## r0 = cond value 5215 5216 li_r1 FALSE 5217 li_br &eval_if_else 5218 beq_r0,r1 5219 5220 ## Then branch: eval car(slot3) 5221 ld_r1,sp,40 5222 li_br &car 5223 call 5224 mov_r1,r0 5225 ld_r2,sp,32 5226 li_br &eval ## Scheme tail: then-branch 5227 tail_n3 5228 5229 :eval_if_else 5230 ## Else branch: eval car(cdr(slot3)) 5231 ld_r1,sp,40 5232 li_br &cdr 5233 call 5234 mov_r1,r0 5235 li_br &car 5236 call 5237 mov_r1,r0 5238 ld_r2,sp,32 5239 li_br &eval ## Scheme tail: else-branch 5240 tail_n3 5241 5242 :eval_begin 5243 ## (begin e1 e2 ... en). Slot 1 = cursor over the body list. The 5244 ## last form (en) is Scheme tail-position, so the loop peeks 5245 ## cdr(cursor): nil next → TAIL eval the current head; otherwise 5246 ## CALL eval and advance. Slot 3 bridges the saved next-cursor 5247 ## across the CALL eval in the non-tail branch. 5248 ld_r1,sp,24 5249 li_br &cdr 5250 call ## r0 = body list 5251 mov_r3,sp 5252 st_r0,sp,24 ## slot 1 := cursor 5253 5254 :eval_begin_loop 5255 mov_r3,sp 5256 ld_r1,sp,24 5257 li_r2 %7 5258 li_br &eval_begin_empty 5259 beq_r1,r2 ## cursor == nil → empty begin → nil 5260 5261 ## next = cdr(cursor) 5262 li_br &cdr 5263 call ## r0 = next 5264 5265 li_r1 %7 5266 li_br &eval_begin_tail 5267 beq_r0,r1 ## next == nil → tail-eval last form 5268 5269 ## Non-tail: stash next in slot 3, CALL eval(car(cursor), env), 5270 ## discard result, advance cursor := next. 5271 st_r0,sp,40 ## slot 3 := next 5272 ld_r1,sp,24 5273 li_br &car 5274 call 5275 mov_r1,r0 5276 ld_r2,sp,32 5277 li_br &eval 5278 call 5279 5280 ld_r0,sp,40 5281 st_r0,sp,24 ## cursor := next 5282 li_br &eval_begin_loop 5283 b 5284 5285 :eval_begin_tail 5286 ## Scheme tail: TAIL eval(car(cursor), env). Frame torn down. 5287 ld_r1,sp,24 5288 li_br &car 5289 call ## r0 = head expr 5290 mov_r1,r0 5291 ld_r2,sp,32 5292 li_br &eval 5293 tail_n3 5294 5295 :eval_begin_empty 5296 li_r0 %7 5297 epilogue_n3 5298 ret 5299 5300 :eval_lambda 5301 ## (lambda params body1 body2 ...). Collect body-list, rewrite any 5302 ## leading (define …) forms into a letrec (step 12e), then stash 5303 ## the resulting single expression as the closure's body. 5304 ld_r1,sp,24 5305 li_br &cdr 5306 call ## r0 = (params body1 body2 ...) 5307 st_r0,sp,40 ## slot 3 = after-head 5308 5309 mov_r1,r0 5310 li_br &car 5311 call ## r0 = params 5312 st_r0,sp,24 ## slot 1 := params 5313 5314 ld_r1,sp,40 5315 li_br &cdr 5316 call ## r0 = body list 5317 mov_r1,r0 5318 li_br &rewrite_lambda_body 5319 call ## r0 = rewritten single expr 5320 5321 mov_r2,r0 5322 ld_r1,sp,24 ## r1 = params 5323 ld_r3,sp,32 ## r3 = env 5324 li_br &make_closure 5325 tail_n3 5326 5327 :eval_define 5328 ## (define sym val-expr). gset-binds the evaluated val into the 5329 ## global alist, returns nil. 5330 ld_r1,sp,24 5331 li_br &cdr 5332 call ## r0 = (sym val-expr) 5333 st_r0,sp,40 ## slot 3 := (sym val-expr) 5334 5335 mov_r1,r0 5336 li_br &car 5337 call ## r0 = sym 5338 st_r0,sp,24 ## slot 1 := sym 5339 5340 ld_r1,sp,40 5341 li_br &cdr 5342 call ## r0 = (val-expr) 5343 mov_r1,r0 5344 li_br &car 5345 call ## r0 = val-expr 5346 mov_r1,r0 5347 ld_r2,sp,32 5348 li_br &eval 5349 call ## r0 = val 5350 5351 mov_r2,r0 5352 ld_r1,sp,24 5353 li_br &gset 5354 call 5355 5356 li_r0 %7 5357 epilogue_n3 5358 ret 5359 5360 5361 ## ---- eval_set (step 12a) ------------------------------------------- 5362 ## (set! sym val-expr). Evaluates val-expr, then mutates the first 5363 ## binding of sym found in local→global search order. 5364 :eval_set 5365 ld_r1,sp,24 5366 li_br &cdr 5367 call ## r0 = (sym val-expr) 5368 mov_r1,r0 5369 li_br &car 5370 call ## r0 = sym 5371 st_r0,sp,40 ## slot 3 = sym 5372 5373 ld_r1,sp,24 5374 li_br &cdr 5375 call 5376 mov_r1,r0 5377 li_br &cdr 5378 call 5379 mov_r1,r0 5380 li_br &car 5381 call ## r0 = val-expr 5382 mov_r1,r0 5383 ld_r2,sp,32 5384 li_br &eval 5385 call ## r0 = val 5386 5387 mov_r3,r0 5388 ld_r1,sp,40 5389 ld_r2,sp,32 5390 li_br &set_binding 5391 tail_n3 5392 5393 5394 ## ---- set_binding(r1=sym, r2=env, r3=val) -> r0 = unspec ------------- 5395 ## Looks up the first (sym . v) cell in env (local alist first, then 5396 ## global) and mutates the cdr to val. Errors out on unbound sym. 5397 :set_binding 5398 prologue_n3 5399 st_r3,sp,40 ## slot 3 = val 5400 st_r1,sp,24 ## slot 1 = sym (for global fallback) 5401 5402 li_br &lookup_alist 5403 call ## r0 = (sym . v) pair or nil 5404 li_r1 NIL 5405 li_br &set_binding_global 5406 beq_r0,r1 5407 5408 ld_r1,sp,40 5409 addi_r0,r0,neg2 ## raw pair ptr 5410 st_r1,r0,8 ## mutate cdr 5411 li_r0 UNSPEC 5412 epilogue_n3 5413 ret 5414 5415 :set_binding_global 5416 ld_r1,sp,24 5417 li_r2 &global_env_cell 5418 ld_r2,r2,0 5419 li_br &lookup_alist 5420 call 5421 li_r1 NIL 5422 li_br &err_unbound 5423 beq_r0,r1 5424 5425 ld_r1,sp,40 5426 addi_r0,r0,neg2 5427 st_r1,r0,8 5428 li_r0 UNSPEC 5429 epilogue_n3 5430 ret 5431 5432 5433 ## ---- eval_let (step 12b) ------------------------------------------- 5434 ## (let ((n1 e1) …) body…). Each RHS is evaluated in the *outer* env; 5435 ## then body evaluates in env extended with all new bindings. 5436 :eval_let 5437 ld_r1,sp,24 5438 li_br &cdr 5439 call ## r0 = (bindings body…) 5440 st_r0,sp,40 ## slot 3 = after-head 5441 5442 mov_r1,r0 5443 li_br &car 5444 call ## r0 = bindings 5445 mov_r1,r0 5446 ld_r2,sp,32 5447 li_br &build_let_env 5448 call ## r0 = new env 5449 st_r0,sp,32 ## slot 2 = new env 5450 5451 ld_r1,sp,40 5452 li_br &cdr 5453 call ## r0 = body list 5454 mov_r2,r0 5455 li_r1 &sym_begin 5456 ld_r1,r1,0 5457 li_br &cons 5458 call ## r0 = (begin . body) 5459 mov_r1,r0 5460 ld_r2,sp,32 5461 li_br &eval 5462 tail_n3 5463 5464 5465 ## ---- eval_letstar -------------------------------------------------- 5466 ## (let* ((n1 e1) …) body…). Each RHS is evaluated in the env built 5467 ## from previous bindings, then body evaluates in the final env. 5468 :eval_letstar 5469 ld_r1,sp,24 5470 li_br &cdr 5471 call 5472 st_r0,sp,40 5473 5474 mov_r1,r0 5475 li_br &car 5476 call 5477 mov_r1,r0 5478 ld_r2,sp,32 5479 li_br &build_letstar_env 5480 call 5481 st_r0,sp,32 5482 5483 ld_r1,sp,40 5484 li_br &cdr 5485 call 5486 mov_r2,r0 5487 li_r1 &sym_begin 5488 ld_r1,r1,0 5489 li_br &cons 5490 call 5491 mov_r1,r0 5492 ld_r2,sp,32 5493 li_br &eval 5494 tail_n3 5495 5496 5497 ## ---- eval_letrec --------------------------------------------------- 5498 ## (letrec ((n1 e1) …) body…). Pre-binds each name to UNSPEC, then 5499 ## walks the binding list a second time evaluating each RHS in the 5500 ## fully pre-bound env and mutating the binding cell. 5501 :eval_letrec 5502 ld_r1,sp,24 5503 li_br &cdr 5504 call 5505 st_r0,sp,40 5506 5507 mov_r1,r0 5508 li_br &car 5509 call 5510 mov_r1,r0 5511 ld_r2,sp,32 5512 li_br &build_letrec_env 5513 call 5514 st_r0,sp,32 5515 5516 ld_r1,sp,40 5517 li_br &cdr 5518 call 5519 mov_r2,r0 5520 li_r1 &sym_begin 5521 ld_r1,r1,0 5522 li_br &cons 5523 call 5524 mov_r1,r0 5525 ld_r2,sp,32 5526 li_br &eval 5527 tail_n3 5528 5529 5530 ## ---- eval_cond (step 12c) ------------------------------------------ 5531 ## (cond (test body…) … (else body…)). Walks clauses in order, evaluates 5532 ## each test; on truthy test or literal `else`, evaluates body as a begin. 5533 ## Returns unspec if no clause fires. 5534 :eval_cond 5535 ld_r1,sp,24 5536 li_br &cdr 5537 call ## r0 = clauses 5538 st_r0,sp,24 ## slot 1 = cursor 5539 5540 :eval_cond_loop 5541 ld_r1,sp,24 5542 li_r0 NIL 5543 li_br &eval_cond_done 5544 beq_r1,r0 5545 5546 li_br &car 5547 call ## r0 = clause 5548 st_r0,sp,40 ## slot 3 = clause 5549 5550 mov_r1,r0 5551 li_br &car 5552 call ## r0 = test 5553 5554 li_r1 &sym_else 5555 ld_r1,r1,0 5556 li_br &eval_cond_body 5557 beq_r0,r1 5558 5559 mov_r1,r0 5560 ld_r2,sp,32 5561 li_br &eval 5562 call ## r0 = test value 5563 5564 li_r1 FALSE 5565 li_br &eval_cond_next 5566 beq_r0,r1 5567 5568 :eval_cond_body 5569 ld_r1,sp,40 5570 li_br &cdr 5571 call ## r0 = body list 5572 mov_r2,r0 5573 li_r1 &sym_begin 5574 ld_r1,r1,0 5575 li_br &cons 5576 call 5577 mov_r1,r0 5578 ld_r2,sp,32 5579 li_br &eval 5580 tail_n3 5581 5582 :eval_cond_next 5583 ld_r1,sp,24 5584 li_br &cdr 5585 call 5586 st_r0,sp,24 5587 li_br &eval_cond_loop 5588 b 5589 5590 :eval_cond_done 5591 li_r0 UNSPEC 5592 epilogue_n3 5593 ret 5594 5595 5596 ## ---- eval_quasiquote (step 12d) ------------------------------------- 5597 ## (quasiquote template). Delegates to quasi_expand which walks the 5598 ## template recursively, handling `,x` (unquote) and `,@x` 5599 ## (unquote-splicing) forms. 5600 :eval_quasiquote 5601 ld_r1,sp,24 5602 li_br &cdr 5603 call 5604 mov_r1,r0 5605 li_br &car 5606 call ## r0 = template 5607 mov_r1,r0 5608 ld_r2,sp,32 5609 li_br &quasi_expand 5610 tail_n3 5611 5612 5613 ## ---- build_let_env(r1=bindings, r2=outer_env) -> r0 = new_env ------ 5614 ## Builds a fresh alist extending outer_env. Each RHS expression is 5615 ## evaluated in the *outer* env — the let semantics. 5616 :build_let_env 5617 prologue_n4 5618 st_r1,sp,24 ## slot 1 = cursor 5619 st_r2,sp,32 ## slot 2 = outer env 5620 st_r2,sp,40 ## slot 3 = new env acc 5621 5622 :ble_loop 5623 ld_r1,sp,24 5624 li_r0 NIL 5625 li_br &ble_done 5626 beq_r1,r0 5627 5628 li_br &car 5629 call ## r0 = (name expr) 5630 st_r0,sp,48 ## slot 4 = (name expr) 5631 5632 mov_r1,r0 5633 li_br &cdr 5634 call 5635 mov_r1,r0 5636 li_br &car 5637 call ## r0 = expr 5638 5639 mov_r1,r0 5640 ld_r2,sp,32 ## outer env 5641 li_br &eval 5642 call ## r0 = val 5643 5644 mov_r2,r0 5645 ld_r1,sp,48 5646 addi_r1,r1,neg2 5647 ld_r1,r1,0 ## r1 = name 5648 li_br &cons 5649 call ## r0 = (name . val) 5650 5651 mov_r1,r0 5652 ld_r2,sp,40 5653 li_br &cons 5654 call ## r0 = extended env 5655 st_r0,sp,40 5656 5657 ld_r1,sp,24 5658 li_br &cdr 5659 call 5660 st_r0,sp,24 5661 li_br &ble_loop 5662 b 5663 5664 :ble_done 5665 ld_r0,sp,40 5666 epilogue_n4 5667 ret 5668 5669 5670 ## ---- build_letstar_env(r1=bindings, r2=outer_env) -> r0 = new_env -- 5671 ## Like build_let_env, but each RHS is evaluated in the env accumulated 5672 ## so far (not the fixed outer env). 5673 :build_letstar_env 5674 prologue_n4 5675 st_r1,sp,24 5676 st_r2,sp,40 ## slot 3 = current env 5677 5678 :bls_loop 5679 ld_r1,sp,24 5680 li_r0 NIL 5681 li_br &bls_done 5682 beq_r1,r0 5683 5684 li_br &car 5685 call 5686 st_r0,sp,48 5687 5688 mov_r1,r0 5689 li_br &cdr 5690 call 5691 mov_r1,r0 5692 li_br &car 5693 call ## r0 = expr 5694 5695 mov_r1,r0 5696 ld_r2,sp,40 ## current env 5697 li_br &eval 5698 call ## r0 = val 5699 5700 mov_r2,r0 5701 ld_r1,sp,48 5702 addi_r1,r1,neg2 5703 ld_r1,r1,0 5704 li_br &cons 5705 call 5706 5707 mov_r1,r0 5708 ld_r2,sp,40 5709 li_br &cons 5710 call 5711 st_r0,sp,40 5712 5713 ld_r1,sp,24 5714 li_br &cdr 5715 call 5716 st_r0,sp,24 5717 li_br &bls_loop 5718 b 5719 5720 :bls_done 5721 ld_r0,sp,40 5722 epilogue_n4 5723 ret 5724 5725 5726 ## ---- build_letrec_env(r1=bindings, r2=outer_env) -> r0 = new_env -- 5727 ## Two-pass: pass 1 seeds each name with UNSPEC; pass 2 evaluates each 5728 ## RHS in the fully pre-bound env and mutates the binding's cdr via 5729 ## set_binding. Supports mutual recursion. 5730 :build_letrec_env 5731 prologue_n4 5732 st_r1,sp,24 ## slot 1 = cursor 5733 st_r1,sp,32 ## slot 2 = original bindings (for pass 2) 5734 st_r2,sp,40 ## slot 3 = new env 5735 5736 :blr_pre_loop 5737 ld_r1,sp,24 5738 li_r0 NIL 5739 li_br &blr_pre_done 5740 beq_r1,r0 5741 5742 li_br &car 5743 call ## r0 = (name expr) 5744 mov_r1,r0 5745 li_br &car 5746 call ## r0 = name 5747 5748 mov_r1,r0 5749 li_r2 UNSPEC 5750 li_br &cons 5751 call ## r0 = (name . UNSPEC) 5752 5753 mov_r1,r0 5754 ld_r2,sp,40 5755 li_br &cons 5756 call 5757 st_r0,sp,40 5758 5759 ld_r1,sp,24 5760 li_br &cdr 5761 call 5762 st_r0,sp,24 5763 li_br &blr_pre_loop 5764 b 5765 5766 :blr_pre_done 5767 ld_r1,sp,32 5768 st_r1,sp,24 ## reset cursor 5769 5770 :blr_eval_loop 5771 ld_r1,sp,24 5772 li_r0 NIL 5773 li_br &blr_eval_done 5774 beq_r1,r0 5775 5776 li_br &car 5777 call 5778 st_r0,sp,48 ## slot 4 = (name expr) 5779 5780 mov_r1,r0 5781 li_br &cdr 5782 call 5783 mov_r1,r0 5784 li_br &car 5785 call ## r0 = expr 5786 5787 mov_r1,r0 5788 ld_r2,sp,40 5789 li_br &eval 5790 call ## r0 = val 5791 5792 mov_r3,r0 5793 ld_r1,sp,48 5794 addi_r1,r1,neg2 5795 ld_r1,r1,0 ## r1 = name 5796 ld_r2,sp,40 5797 li_br &set_binding 5798 call 5799 5800 ld_r1,sp,24 5801 li_br &cdr 5802 call 5803 st_r0,sp,24 5804 li_br &blr_eval_loop 5805 b 5806 5807 :blr_eval_done 5808 ld_r0,sp,40 5809 epilogue_n4 5810 ret 5811 5812 5813 ## ---- rewrite_lambda_body(r1=body_list) -> r0 = single body expr ---- 5814 ## Collects leading (define name val) forms from a lambda body and 5815 ## rewrites the body into (letrec ((n1 v1) …) body-tail…) if any are 5816 ## present. Otherwise returns either the single body form or a 5817 ## (begin . body) wrapper. 5818 :rewrite_lambda_body 5819 prologue_n4 5820 st_r1,sp,24 ## slot 1 = cursor 5821 li_r0 NIL 5822 st_r0,sp,32 ## slot 2 = reversed bindings acc 5823 5824 :rlb_loop 5825 ld_r1,sp,24 5826 li_r0 NIL 5827 li_br &rlb_done 5828 beq_r1,r0 5829 5830 li_br &car 5831 call ## r0 = body form 5832 mov_r1,r0 5833 andi_r0,r0,7 5834 li_r2 TAG_PAIR 5835 li_br &rlb_done 5836 bne_r0,r2 5837 5838 st_r1,sp,40 ## slot 3 = form 5839 li_br &car 5840 call ## r0 = head 5841 li_r1 &sym_define 5842 ld_r1,r1,0 5843 li_br &rlb_done 5844 bne_r0,r1 5845 5846 ld_r1,sp,40 5847 li_br &cdr 5848 call ## r0 = (name val) 5849 5850 mov_r1,r0 5851 ld_r2,sp,32 5852 li_br &cons 5853 call 5854 st_r0,sp,32 5855 5856 ld_r1,sp,24 5857 li_br &cdr 5858 call 5859 st_r0,sp,24 5860 li_br &rlb_loop 5861 b 5862 5863 :rlb_done 5864 ld_r1,sp,32 5865 li_r0 NIL 5866 li_br &rlb_no_defs 5867 beq_r1,r0 5868 5869 ld_r1,sp,32 5870 li_br &list_reverse 5871 call 5872 st_r0,sp,32 5873 5874 ld_r1,sp,32 5875 ld_r2,sp,24 5876 li_br &cons 5877 call ## r0 = (bindings . body-tail) 5878 mov_r2,r0 5879 li_r1 &sym_letrec 5880 ld_r1,r1,0 5881 li_br &cons 5882 tail_n4 5883 5884 :rlb_no_defs 5885 ld_r1,sp,24 5886 li_br &cdr 5887 call 5888 li_r1 NIL 5889 li_br &rlb_single 5890 beq_r0,r1 5891 5892 ld_r2,sp,24 5893 li_r1 &sym_begin 5894 ld_r1,r1,0 5895 li_br &cons 5896 tail_n4 5897 5898 :rlb_single 5899 ld_r1,sp,24 5900 li_br &car 5901 tail_n4 5902 5903 5904 ## ---- list_reverse(r1=list) -> r0 = reversed list -------------------- 5905 ## Simple tail-consing reverse; used by rewrite_lambda_body to restore 5906 ## source-order bindings. 5907 :list_reverse 5908 prologue_n2 5909 st_r1,sp,24 5910 li_r0 NIL 5911 st_r0,sp,32 5912 5913 :lrv_loop 5914 ld_r1,sp,24 5915 li_r0 NIL 5916 li_br &lrv_done 5917 beq_r1,r0 5918 5919 li_br &car 5920 call 5921 mov_r1,r0 5922 ld_r2,sp,32 5923 li_br &cons 5924 call 5925 st_r0,sp,32 5926 5927 ld_r1,sp,24 5928 li_br &cdr 5929 call 5930 st_r0,sp,24 5931 li_br &lrv_loop 5932 b 5933 5934 :lrv_done 5935 ld_r0,sp,32 5936 epilogue_n2 5937 ret 5938 5939 5940 ## ---- quasi_expand(r1=template, r2=env) -> r0 = value --------------- 5941 ## Top-level quasiquote walker. Non-pair templates self-evaluate. A pair 5942 ## whose car is `unquote` evaluates the first element of its cdr. 5943 ## Otherwise the pair is walked as a list via quasi_list. 5944 :quasi_expand 5945 prologue_n3 5946 st_r1,sp,24 5947 st_r2,sp,32 5948 5949 mov_r0,r1 5950 andi_r0,r0,7 5951 li_r2 TAG_PAIR 5952 li_br &qe_pair 5953 beq_r0,r2 5954 ld_r0,sp,24 5955 epilogue_n3 5956 ret 5957 5958 :qe_pair 5959 ld_r1,sp,24 5960 li_br &car 5961 call 5962 li_r1 &sym_unquote 5963 ld_r1,r1,0 5964 li_br &qe_unquote 5965 beq_r0,r1 5966 5967 ld_r1,sp,24 5968 ld_r2,sp,32 5969 li_br &quasi_list 5970 tail_n3 5971 5972 :qe_unquote 5973 ld_r1,sp,24 5974 li_br &cdr 5975 call 5976 mov_r1,r0 5977 li_br &car 5978 call 5979 mov_r1,r0 5980 ld_r2,sp,32 5981 li_br &eval 5982 tail_n3 5983 5984 5985 ## ---- quasi_list(r1=list, r2=env) -> r0 = expanded list -------------- 5986 ## Recursively expands each element. If an element is (unquote-splicing X), 5987 ## evaluates X and appends the resulting list into the output; else it 5988 ## recursively calls quasi_expand on the element. 5989 :quasi_list 5990 prologue_n4 5991 st_r1,sp,24 ## slot 1 = cursor 5992 st_r2,sp,32 ## slot 2 = env 5993 5994 li_r0 NIL 5995 li_br &ql_nil 5996 beq_r1,r0 5997 5998 mov_r0,r1 5999 andi_r0,r0,7 6000 li_r2 TAG_PAIR 6001 li_br &ql_nonpair 6002 bne_r0,r2 6003 6004 li_br &car 6005 call ## r0 = head 6006 st_r0,sp,40 ## slot 3 = head 6007 6008 mov_r1,r0 6009 andi_r0,r0,7 6010 li_r2 TAG_PAIR 6011 li_br &ql_head_reg 6012 bne_r0,r2 6013 6014 ld_r1,sp,40 6015 li_br &car 6016 call ## r0 = car(head) 6017 li_r1 &sym_unquote_splicing 6018 ld_r1,r1,0 6019 li_br &ql_splice 6020 beq_r0,r1 6021 6022 :ql_head_reg 6023 ld_r1,sp,40 6024 ld_r2,sp,32 6025 li_br &quasi_expand 6026 call 6027 st_r0,sp,40 ## slot 3 = expanded head 6028 6029 ld_r1,sp,24 6030 li_br &cdr 6031 call 6032 mov_r1,r0 6033 ld_r2,sp,32 6034 li_br &quasi_list 6035 call 6036 6037 mov_r2,r0 6038 ld_r1,sp,40 6039 li_br &cons 6040 tail_n4 6041 6042 :ql_splice 6043 ld_r1,sp,40 6044 li_br &cdr 6045 call 6046 mov_r1,r0 6047 li_br &car 6048 call ## r0 = X (the spliced expression) 6049 mov_r1,r0 6050 ld_r2,sp,32 6051 li_br &eval 6052 call ## r0 = evaluated list 6053 st_r0,sp,40 6054 6055 ld_r1,sp,24 6056 li_br &cdr 6057 call 6058 mov_r1,r0 6059 ld_r2,sp,32 6060 li_br &quasi_list 6061 call 6062 6063 mov_r2,r0 6064 ld_r1,sp,40 6065 li_br &append_one 6066 tail_n4 6067 6068 :ql_nil 6069 li_r0 NIL 6070 epilogue_n4 6071 ret 6072 6073 :ql_nonpair 6074 ld_r0,sp,24 6075 epilogue_n4 6076 ret 6077 6078 6079 ## ---- Step-6 error landing pads -------------------------------------- 6080 :err_unbound 6081 li_r1 &msg_unbound 6082 li_r2 %14 ## strlen("unbound symbol") == 14 6083 li_br &error 6084 b 6085 6086 :err_arity 6087 li_r1 &msg_arity 6088 li_r2 %14 ## strlen("arity mismatch") == 14 6089 li_br &error 6090 b 6091 6092 :err_not_callable 6093 li_r1 &msg_not_callable 6094 li_r2 %12 ## strlen("not callable") == 12 6095 li_br &error 6096 b 6097 6098 :err_too_many_args 6099 li_r1 &msg_too_many_args 6100 li_r2 %21 ## strlen("primitive argc > 32") == 21 6101 li_br &error 6102 b 6103 6104 :err_bad_prim 6105 li_r1 &msg_bad_prim 6106 li_r2 %20 ## strlen("unknown primitive id") == 20 6107 li_br &error 6108 b 6109 6110 :err_path_too_long 6111 li_r1 &msg_path_too_long 6112 li_r2 %18 6113 li_br &error 6114 b 6115 6116 6117 ## ---- Static strings ------------------------------------------------- 6118 :msg_newline 6119 " 6120 " 6121 :msg_error_prefix "error: " 6122 :msg_oom "heap exhausted" 6123 :msg_intern_not_eq "intern: foo a != foo b (same name)" 6124 :msg_intern_collision "intern: foo == bar (distinct names collided)" 6125 :msg_reader_bad "reader: malformed input" 6126 :msg_at " at " 6127 :msg_colon ":" 6128 :msg_unbound "unbound symbol" 6129 :msg_arity "arity mismatch" 6130 :msg_not_callable "not callable" 6131 :msg_usage "usage: lisp <file.scm>" 6132 :msg_open_fail "failed to open source file" 6133 :msg_src_too_big "source file too large" 6134 :msg_too_many_args "primitive argc > 32" 6135 :msg_bad_prim "unknown primitive id" 6136 :msg_path_too_long "file path too long" 6137 6138 ## Interned name samples used by the self-test. 6139 :str_foo "foo" 6140 :str_bar "bar" 6141 6142 ## Special-form name strings. Lengths are baked into _start's intern 6143 ## calls (update both sides together). 6144 :str_quote "quote" 6145 :str_if "if" 6146 :str_begin "begin" 6147 :str_lambda "lambda" 6148 :str_define "define" 6149 :str_quasiquote "quasiquote" 6150 :str_unquote "unquote" 6151 :str_unquote_splicing "unquote-splicing" 6152 :str_set "set!" 6153 :str_let "let" 6154 :str_letstar "let*" 6155 :str_letrec "letrec" 6156 :str_cond "cond" 6157 :str_else "else" 6158 6159 ## Primitive name strings (step 10b). The registration table below 6160 ## holds (ptr, len, code_id, type, arity) for each. _start walks the 6161 ## table, interns each name, and binds a freshly-built primitive into 6162 ## global_env. 6163 :str_prim_plus "+" 6164 :str_prim_minus "-" 6165 :str_prim_mul "*" 6166 :str_prim_div "/" 6167 :str_prim_mod "%" 6168 :str_prim_numeq "=" 6169 :str_prim_lt "<" 6170 :str_prim_gt ">" 6171 :str_prim_min "min" 6172 :str_prim_max "max" 6173 :str_prim_bitand "bit-and" 6174 :str_prim_bitor "bit-or" 6175 :str_prim_bitxor "bit-xor" 6176 :str_prim_bitnot "bit-not" 6177 :str_prim_ashift "arithmetic-shift" 6178 :str_prim_numberp "number?" 6179 :str_prim_symbolp "symbol?" 6180 :str_prim_stringp "string?" 6181 :str_prim_vectorp "vector?" 6182 :str_prim_procp "procedure?" 6183 :str_prim_eqp "eq?" 6184 6185 ## Step-10d list-core names. 6186 :str_prim_cons "cons" 6187 :str_prim_car "car" 6188 :str_prim_cdr "cdr" 6189 :str_prim_pairp "pair?" 6190 :str_prim_nullp "null?" 6191 :str_prim_list "list" 6192 :str_prim_append "append" 6193 6194 ## Step-10e string / vector / I/O / equal? / apply names. 6195 :str_prim_string_length "string-length" 6196 :str_prim_string_ref "string-ref" 6197 :str_prim_substring "substring" 6198 :str_prim_string_append "string-append" 6199 :str_prim_string_to_symbol "string->symbol" 6200 :str_prim_symbol_to_string "symbol->string" 6201 :str_prim_make_vector "make-vector" 6202 :str_prim_vector_ref "vector-ref" 6203 :str_prim_vector_set "vector-set!" 6204 :str_prim_vector_length "vector-length" 6205 :str_prim_display "display" 6206 :str_prim_write "write" 6207 :str_prim_newline "newline" 6208 :str_prim_format "format" 6209 :str_prim_error "error" 6210 :str_prim_read_file "read-file" 6211 :str_prim_write_file "write-file" 6212 :str_prim_apply "apply" 6213 6214 6215 ## Registration table. 40-byte records: ptr(8) + len(8) + code_id(8) + 6216 ## type(8) + arity(8). End-sentinel = zero name pointer. _start iterates 6217 ## with ADDI +40. 6218 :prim_table 6219 ## Primitives promoted to the Scheme prelude (src/prelude.scm) and no 6220 ## longer registered here: <=, >=, zero?, negative?, positive?, abs, 6221 ## length, list?, reverse, assoc, member, vector->list, list->vector, 6222 ## equal?. Code ids are contiguous 0..45 after that removal. 6223 ## (+ ...) variadic — code 0 6224 &str_prim_plus %0 6225 %1 %0 6226 %0 %0 6227 %6 %0 6228 %0 %0 6229 ## (- x ...) variadic — code 1 (unary-negate branch in body) 6230 &str_prim_minus %0 6231 %1 %0 6232 %1 %0 6233 %6 %0 6234 %0 %0 6235 ## (* ...) variadic — code 2 6236 &str_prim_mul %0 6237 %1 %0 6238 %2 %0 6239 %6 %0 6240 %0 %0 6241 ## (/ x y) fixed 2 — code 3 6242 &str_prim_div %0 6243 %1 %0 6244 %3 %0 6245 %5 %0 6246 %2 %0 6247 ## (% x y) fixed 2 — code 4 6248 &str_prim_mod %0 6249 %1 %0 6250 %4 %0 6251 %5 %0 6252 %2 %0 6253 ## (= x y) fixed 2 — code 5 6254 &str_prim_numeq %0 6255 %1 %0 6256 %5 %0 6257 %5 %0 6258 %2 %0 6259 ## (< x y) fixed 2 — code 6 6260 &str_prim_lt %0 6261 %1 %0 6262 %6 %0 6263 %5 %0 6264 %2 %0 6265 ## (> x y) fixed 2 — code 7 6266 &str_prim_gt %0 6267 %1 %0 6268 %7 %0 6269 %5 %0 6270 %2 %0 6271 ## (min ...) variadic — code 8 (≥1 enforced by body load) 6272 &str_prim_min %0 6273 %3 %0 6274 %8 %0 6275 %6 %0 6276 %0 %0 6277 ## (max ...) variadic — code 9 6278 &str_prim_max %0 6279 %3 %0 6280 %9 %0 6281 %6 %0 6282 %0 %0 6283 ## (bit-and ...) variadic — code 10 6284 &str_prim_bitand %0 6285 %7 %0 6286 %10 %0 6287 %6 %0 6288 %0 %0 6289 ## (bit-or ...) variadic — code 11 6290 &str_prim_bitor %0 6291 %6 %0 6292 %11 %0 6293 %6 %0 6294 %0 %0 6295 ## (bit-xor ...) variadic — code 12 6296 &str_prim_bitxor %0 6297 %7 %0 6298 %12 %0 6299 %6 %0 6300 %0 %0 6301 ## (bit-not x) fixed 1 — code 13 6302 &str_prim_bitnot %0 6303 %7 %0 6304 %13 %0 6305 %5 %0 6306 %1 %0 6307 ## (arithmetic-shift n k) fixed 2 — code 14 6308 &str_prim_ashift %0 6309 %16 %0 6310 %14 %0 6311 %5 %0 6312 %2 %0 6313 ## (number? x) fixed 1 — code 15 6314 &str_prim_numberp %0 6315 %7 %0 6316 %15 %0 6317 %5 %0 6318 %1 %0 6319 ## (symbol? x) fixed 1 — code 16 6320 &str_prim_symbolp %0 6321 %7 %0 6322 %16 %0 6323 %5 %0 6324 %1 %0 6325 ## (string? x) fixed 1 — code 17 6326 &str_prim_stringp %0 6327 %7 %0 6328 %17 %0 6329 %5 %0 6330 %1 %0 6331 ## (vector? x) fixed 1 — code 18 6332 &str_prim_vectorp %0 6333 %7 %0 6334 %18 %0 6335 %5 %0 6336 %1 %0 6337 ## (procedure? x) fixed 1 — code 19 6338 &str_prim_procp %0 6339 %10 %0 6340 %19 %0 6341 %5 %0 6342 %1 %0 6343 ## (eq? x y) fixed 2 — code 20 6344 &str_prim_eqp %0 6345 %3 %0 6346 %20 %0 6347 %5 %0 6348 %2 %0 6349 ## (cons a d) fixed 2 — code 21 6350 &str_prim_cons %0 6351 %4 %0 6352 %21 %0 6353 %5 %0 6354 %2 %0 6355 ## (car p) fixed 1 — code 22 6356 &str_prim_car %0 6357 %3 %0 6358 %22 %0 6359 %5 %0 6360 %1 %0 6361 ## (cdr p) fixed 1 — code 23 6362 &str_prim_cdr %0 6363 %3 %0 6364 %23 %0 6365 %5 %0 6366 %1 %0 6367 ## (pair? x) fixed 1 — code 24 6368 &str_prim_pairp %0 6369 %5 %0 6370 %24 %0 6371 %5 %0 6372 %1 %0 6373 ## (null? x) fixed 1 — code 25 6374 &str_prim_nullp %0 6375 %5 %0 6376 %25 %0 6377 %5 %0 6378 %1 %0 6379 ## (list ...) variadic — code 26 6380 &str_prim_list %0 6381 %4 %0 6382 %26 %0 6383 %6 %0 6384 %0 %0 6385 ## (append ...) variadic — code 27 6386 &str_prim_append %0 6387 %6 %0 6388 %27 %0 6389 %6 %0 6390 %0 %0 6391 ## (string-length s) fixed 1 — code 28 6392 &str_prim_string_length %0 6393 %13 %0 6394 %28 %0 6395 %5 %0 6396 %1 %0 6397 ## (string-ref s i) fixed 2 — code 29 6398 &str_prim_string_ref %0 6399 %10 %0 6400 %29 %0 6401 %5 %0 6402 %2 %0 6403 ## (substring s start end) fixed 3 — code 30 6404 &str_prim_substring %0 6405 %9 %0 6406 %30 %0 6407 %5 %0 6408 %3 %0 6409 ## (string-append ...) variadic — code 31 6410 &str_prim_string_append %0 6411 %13 %0 6412 %31 %0 6413 %6 %0 6414 %0 %0 6415 ## (string->symbol s) fixed 1 — code 32 6416 &str_prim_string_to_symbol %0 6417 %14 %0 6418 %32 %0 6419 %5 %0 6420 %1 %0 6421 ## (symbol->string sym) fixed 1 — code 33 6422 &str_prim_symbol_to_string %0 6423 %14 %0 6424 %33 %0 6425 %5 %0 6426 %1 %0 6427 ## (make-vector n init) fixed 2 — code 34 6428 &str_prim_make_vector %0 6429 %11 %0 6430 %34 %0 6431 %5 %0 6432 %2 %0 6433 ## (vector-ref v i) fixed 2 — code 35 6434 &str_prim_vector_ref %0 6435 %10 %0 6436 %35 %0 6437 %5 %0 6438 %2 %0 6439 ## (vector-set! v i x) fixed 3 — code 36 6440 &str_prim_vector_set %0 6441 %11 %0 6442 %36 %0 6443 %5 %0 6444 %3 %0 6445 ## (vector-length v) fixed 1 — code 37 6446 &str_prim_vector_length %0 6447 %13 %0 6448 %37 %0 6449 %5 %0 6450 %1 %0 6451 ## (display x) fixed 1 — code 38 6452 &str_prim_display %0 6453 %7 %0 6454 %38 %0 6455 %5 %0 6456 %1 %0 6457 ## (write x) fixed 1 — code 39 6458 &str_prim_write %0 6459 %5 %0 6460 %39 %0 6461 %5 %0 6462 %1 %0 6463 ## (newline) fixed 0 — code 40 6464 &str_prim_newline %0 6465 %7 %0 6466 %40 %0 6467 %5 %0 6468 %0 %0 6469 ## (format fmt ...) variadic — code 41 6470 &str_prim_format %0 6471 %6 %0 6472 %41 %0 6473 %6 %0 6474 %0 %0 6475 ## (error msg) fixed 1 — code 42 6476 &str_prim_error %0 6477 %5 %0 6478 %42 %0 6479 %5 %0 6480 %1 %0 6481 ## (read-file path) fixed 1 — code 43 6482 &str_prim_read_file %0 6483 %9 %0 6484 %43 %0 6485 %5 %0 6486 %1 %0 6487 ## (write-file path data) fixed 2 — code 44 6488 &str_prim_write_file %0 6489 %10 %0 6490 %44 %0 6491 %5 %0 6492 %2 %0 6493 ## (apply proc arg ... last-list) variadic — code 45 6494 &str_prim_apply %0 6495 %5 %0 6496 %45 %0 6497 %6 %0 6498 %0 %0 6499 ## End sentinel: zero name pointer. 6500 %0 %0 6501 %0 %0 6502 %0 %0 6503 %0 %0 6504 %0 %0 6505 6506 6507 ## ---- Special-form symbol slots -------------------------------------- 6508 ## Zero-initialized; _start populates each slot with the interned 6509 ## tagged-symbol pointer so eval_pair can dispatch by pointer identity. 6510 :sym_quote %0 %0 6511 :sym_if %0 %0 6512 :sym_begin %0 %0 6513 :sym_lambda %0 %0 6514 :sym_define %0 %0 6515 :sym_quasiquote %0 %0 6516 :sym_unquote %0 %0 6517 :sym_unquote_splicing %0 %0 6518 :sym_set %0 %0 6519 :sym_let %0 %0 6520 :sym_letstar %0 %0 6521 :sym_letrec %0 %0 6522 :sym_cond %0 %0 6523 :sym_else %0 %0 6524 6525 ## Global-binding alist head. Zero-initialized (which is a valid 6526 ## untagged pair/nil sentinel? No — nil = 0x07. Seed to nil on entry 6527 ## from _start; the zero here would be interpreted as a pair pointer 6528 ## otherwise). 6529 :global_env_cell NIL %0 6530 6531 6532 ## -------------------------------------------------------------------- 6533 ## :ELF_end marks the end of the initialised image — everything above 6534 ## is code and data with real bytes, everything below is BSS: labels 6535 ## only, no real content. The ELF header declares p_filesz ending 6536 ## here and p_memsz ending at :ELF_bss_end, so the kernel zero-fills 6537 ## the tail at load time and the on-disk file stops at :ELF_end (a 6538 ## post-link truncate drops the trailing zero bytes). Labels here 6539 ## still need `%0`/`ZERO32` placeholders so hex2's IP keeps advancing 6540 ## and label addresses after them remain correct; the bytes those 6541 ## emit get truncated away and replaced by kernel zeros. 6542 ## -------------------------------------------------------------------- 6543 :ELF_end 6544 6545 :stack_bottom_fp %0 %0 6546 :gc_root_fp %0 %0 6547 6548 ## Aligned mirrors of :pair_heap_start / :obj_heap_start. _start fills 6549 ## these once, so every GC code path can derive the same canonical 6550 ## start address that the bump pointer actually used for its first 6551 ## allocation. See the alignment block in _start for the rationale. 6552 :pair_heap_base %0 %0 6553 :obj_heap_base %0 %0 6554 6555 ## Live prefix length of :prim_argv, set by marshal_argv on every 6556 ## primitive call. The GC root walker uses this as the upper bound 6557 ## of the prim_argv scan. Initialised to 0 so a GC that fires before 6558 ## the first primitive call walks zero slots. 6559 :prim_argc %0 %0 6560 6561 :free_list_pair %0 %0 6562 :free_list_obj16 %0 %0 6563 :free_list_obj24 %0 %0 6564 :free_list_obj32 %0 %0 6565 :free_list_obj40 %0 %0 6566 :free_list_obj48 %0 %0 6567 :free_list_obj56 %0 %0 6568 :free_list_obj64 %0 %0 6569 :free_list_obj80 %0 %0 6570 :free_list_obj96 %0 %0 6571 :free_list_obj128 %0 %0 6572 6573 ## mark_stack_next lives in BSS: gc_mark_all re-seeds it to &mark_stack 6574 ## at the top of every mark pass (see :gc_mark_all), so the initial 6575 ## value is dead — zero is fine. 6576 :mark_stack_next %0 %0 6577 :pair_mark_bitmap 6578 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6579 6580 :mark_stack 6581 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6582 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6583 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6584 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6585 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6586 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6587 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6588 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6589 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6590 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6591 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6592 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6593 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6594 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6595 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6596 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6597 :mark_stack_end 6598 6599 6600 ## ---- Primitive argv scratch buffer (32 slots × 8B = 256B) ----------- 6601 ## apply fills this with tagged values before cascading to a primitive 6602 ## body. 32 slots is generous for the step-10 surface; extend if later 6603 ## primitives need more. Zeroed so a stray read sees the 0 tagged 6604 ## sentinel (not a valid value — harmless). 6605 :prim_argv 6606 ZERO32 ZERO32 6607 ZERO32 ZERO32 6608 ZERO32 ZERO32 6609 ZERO32 ZERO32 6610 6611 ## Reader-state save area for eval_source(). 6612 :saved_src_base %0 %0 6613 :saved_src_len %0 %0 6614 :saved_src_cursor %0 %0 6615 :saved_src_line %0 %0 6616 :saved_src_col %0 %0 6617 6618 ## Shared buffers for pathname marshaling, string literal decoding, and 6619 ## file I/O primitives. 6620 :path_buf 6621 ZERO32 ZERO32 6622 ZERO32 ZERO32 6623 ZERO32 ZERO32 6624 ZERO32 ZERO32 6625 6626 :reader_string_buf 6627 ZERO32 ZERO32 6628 ZERO32 ZERO32 6629 ZERO32 ZERO32 6630 ZERO32 ZERO32 6631 ZERO32 ZERO32 6632 ZERO32 ZERO32 6633 ZERO32 ZERO32 6634 ZERO32 ZERO32 6635 6636 :io_buf 6637 ZERO32 ZERO32 6638 ZERO32 ZERO32 6639 ZERO32 ZERO32 6640 ZERO32 ZERO32 6641 ZERO32 ZERO32 6642 ZERO32 ZERO32 6643 ZERO32 ZERO32 6644 ZERO32 ZERO32 6645 6646 6647 ## ---- Symbol table (4096 slots × 8 bytes = 32 KiB) ------------------- 6648 ## Open-addressing hash table. Empty slot = 0 (no valid tagged value 6649 ## is 0). LISP.md §GC §Roots makes this a named BSS root. 6650 :symbol_table 6651 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6652 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6653 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6654 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6655 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6656 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6657 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6658 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6659 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6660 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6661 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6662 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6663 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6664 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6665 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6666 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6667 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6668 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6669 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6670 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6671 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6672 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6673 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6674 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6675 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6676 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6677 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6678 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6679 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6680 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6681 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6682 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6683 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6684 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6685 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6686 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6687 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6688 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6689 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6690 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6691 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6692 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6693 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6694 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6695 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6696 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6697 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6698 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6699 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6700 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6701 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6702 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6703 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6704 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6705 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6706 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6707 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6708 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6709 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6710 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6711 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6712 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6713 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6714 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6715 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6716 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6717 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6718 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6719 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6720 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6721 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6722 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6723 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6724 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6725 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6726 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6727 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6728 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6729 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6730 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6731 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6732 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6733 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6734 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6735 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6736 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6737 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6738 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6739 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6740 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6741 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6742 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6743 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6744 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6745 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6746 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6747 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6748 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6749 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6750 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6751 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6752 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6753 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6754 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6755 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6756 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6757 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6758 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6759 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6760 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6761 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6762 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6763 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6764 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6765 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6766 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6767 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6768 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6769 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6770 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6771 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6772 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6773 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6774 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6775 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6776 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6777 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6778 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6779 :symbol_table_end 6780 6781 6782 ## ---- Heap arena (64 KiB split 32 KiB / 32 KiB for GC bring-up) ------- 6783 :heap_start 6784 :pair_heap_start 6785 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6786 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6787 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6788 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6789 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6790 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6791 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6792 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6793 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6794 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6795 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6796 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6797 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6798 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6799 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6800 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6801 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6802 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6803 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6804 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6805 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6806 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6807 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6808 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6809 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6810 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6811 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6812 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6813 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6814 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6815 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6816 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6817 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6818 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6819 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6820 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6821 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6822 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6823 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6824 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6825 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6826 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6827 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6828 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6829 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6830 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6831 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6832 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6833 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6834 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6835 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6836 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6837 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6838 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6839 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6840 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6841 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6842 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6843 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6844 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6845 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6846 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6847 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6848 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6849 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6850 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6851 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6852 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6853 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6854 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6855 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6856 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6857 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6858 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6859 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6860 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6861 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6862 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6863 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6864 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6865 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6866 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6867 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6868 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6869 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6870 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6871 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6872 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6873 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6874 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6875 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6876 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6877 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6878 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6879 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6880 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6881 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6882 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6883 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6884 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6885 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6886 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6887 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6888 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6889 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6890 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6891 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6892 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6893 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6894 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6895 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6896 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6897 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6898 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6899 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6900 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6901 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6902 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6903 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6904 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6905 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6906 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6907 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6908 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6909 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6910 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6911 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6912 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6913 :pair_heap_end 6914 :obj_heap_start 6915 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6916 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6917 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6918 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6919 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6920 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6921 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6922 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6923 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6924 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6925 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6926 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6927 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6928 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6929 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6930 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6931 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6932 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6933 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6934 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6935 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6936 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6937 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6938 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6939 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6940 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6941 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6942 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6943 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6944 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6945 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6946 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6947 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6948 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6949 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6950 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6951 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6952 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6953 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6954 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6955 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6956 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6957 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6958 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6959 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6960 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6961 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6962 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6963 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6964 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6965 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6966 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6967 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6968 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6969 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6970 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6971 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6972 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6973 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6974 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6975 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6976 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6977 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6978 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6979 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6980 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6981 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6982 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6983 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6984 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6985 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6986 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6987 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6988 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6989 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6990 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6991 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6992 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6993 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6994 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6995 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6996 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6997 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6998 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 6999 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7000 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7001 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7002 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7003 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7004 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7005 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7006 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7007 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7008 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7009 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7010 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7011 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7012 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7013 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7014 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7015 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7016 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7017 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7018 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7019 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7020 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7021 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7022 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7023 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7024 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7025 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7026 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7027 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7028 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7029 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7030 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7031 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7032 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7033 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7034 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7035 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7036 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7037 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7038 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7039 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7040 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7041 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7042 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7043 :obj_heap_end 7044 :heap_tail 7045 7046 7047 ## ---- Source buffer (16 KiB) ----------------------------------------- 7048 ## Receives the contents of the file named by argv[1]. Sized to cover 7049 ## the step-9 test fixtures with headroom; widen once those exist. 7050 ## Kept out of the Scheme heap so the reader's raw byte pointers don't 7051 ## fight the bump allocator. 7052 :src_buf 7053 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7054 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7055 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7056 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7057 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7058 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7059 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7060 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7061 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7062 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7063 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7064 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7065 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7066 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7067 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7068 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7069 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7070 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7071 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7072 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7073 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7074 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7075 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7076 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7077 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7078 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7079 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7080 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7081 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7082 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7083 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7084 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7085 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7086 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7087 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7088 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7089 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7090 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7091 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7092 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7093 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7094 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7095 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7096 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7097 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7098 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7099 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7100 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7101 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7102 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7103 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7104 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7105 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7106 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7107 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7108 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7109 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7110 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7111 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7112 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7113 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7114 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7115 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7116 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 ZERO32 7117 :src_buf_end 7118 7119 :ELF_bss_end