boot2

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

p1pp.P1pp (20996B)


      1 # p1pp.P1pp -- libp1pp v1, portable utility library for P1pp programs.
      2 #
      3 # Concatenated after the P1 backend header and frontend, and before user
      4 # source:
      5 #
      6 #     catm P1-<arch>.M1pp P1.M1pp p1pp.P1pp usersrc.P1pp > program.M1
      7 #
      8 # Targets P1v2-64 only (WORD = 8). All internal labels use the
      9 # `libp1pp__` prefix; public entry points are unprefixed.
     10 #
     11 # See docs/LIBP1PP.md for the public contract.
     12 
     13 # =========================================================================
     14 # Control-flow macros
     15 # =========================================================================
     16 #
     17 # Every conditional block macro uses a uniform three-branch lowering that
     18 # works for all seven P1 conditions (including LT, LTU, LTZ which have no
     19 # inverted branch): load a "take the body" target, branch on cc, then
     20 # unconditionally skip past the body.
     21 
     22 # ---- %if_<cc> -----------------------------------------------------------
     23 
     24 %macro if_eq(ra, rb, body)
     25 %beq(ra, rb, &@body)
     26 %b(&@end)
     27 :@body
     28 body
     29 :@end
     30 %endm
     31 
     32 %macro if_ne(ra, rb, body)
     33 %bne(ra, rb, &@body)
     34 %b(&@end)
     35 :@body
     36 body
     37 :@end
     38 %endm
     39 
     40 %macro if_lt(ra, rb, body)
     41 %blt(ra, rb, &@body)
     42 %b(&@end)
     43 :@body
     44 body
     45 :@end
     46 %endm
     47 
     48 %macro if_ltu(ra, rb, body)
     49 %bltu(ra, rb, &@body)
     50 %b(&@end)
     51 :@body
     52 body
     53 :@end
     54 %endm
     55 
     56 %macro if_eqz(ra, body)
     57 %beqz(ra, &@body)
     58 %b(&@end)
     59 :@body
     60 body
     61 :@end
     62 %endm
     63 
     64 %macro if_nez(ra, body)
     65 %bnez(ra, &@body)
     66 %b(&@end)
     67 :@body
     68 body
     69 :@end
     70 %endm
     71 
     72 %macro if_ltz(ra, body)
     73 %bltz(ra, &@body)
     74 %b(&@end)
     75 :@body
     76 body
     77 :@end
     78 %endm
     79 
     80 # ---- %ifelse_<cc> -------------------------------------------------------
     81 
     82 %macro ifelse_eq(ra, rb, tblk, fblk)
     83 %beq(ra, rb, &@tblk)
     84 fblk
     85 %b(&@end)
     86 :@tblk
     87 tblk
     88 :@end
     89 %endm
     90 
     91 %macro ifelse_ne(ra, rb, tblk, fblk)
     92 %bne(ra, rb, &@tblk)
     93 fblk
     94 %b(&@end)
     95 :@tblk
     96 tblk
     97 :@end
     98 %endm
     99 
    100 %macro ifelse_lt(ra, rb, tblk, fblk)
    101 %blt(ra, rb, &@tblk)
    102 fblk
    103 %b(&@end)
    104 :@tblk
    105 tblk
    106 :@end
    107 %endm
    108 
    109 %macro ifelse_ltu(ra, rb, tblk, fblk)
    110 %bltu(ra, rb, &@tblk)
    111 fblk
    112 %b(&@end)
    113 :@tblk
    114 tblk
    115 :@end
    116 %endm
    117 
    118 %macro ifelse_eqz(ra, tblk, fblk)
    119 %beqz(ra, &@tblk)
    120 fblk
    121 %b(&@end)
    122 :@tblk
    123 tblk
    124 :@end
    125 %endm
    126 
    127 %macro ifelse_nez(ra, tblk, fblk)
    128 %bnez(ra, &@tblk)
    129 fblk
    130 %b(&@end)
    131 :@tblk
    132 tblk
    133 :@end
    134 %endm
    135 
    136 %macro ifelse_ltz(ra, tblk, fblk)
    137 %bltz(ra, &@tblk)
    138 fblk
    139 %b(&@end)
    140 :@tblk
    141 tblk
    142 :@end
    143 %endm
    144 
    145 # ---- %while_<cc> -------------------------------------------------------
    146 #
    147 # Jump-to-test layout: the body runs iff the positive-sense test holds,
    148 # and the test is compiled below the body so we only emit a forward
    149 # branch at entry.
    150 
    151 %macro while_eq(ra, rb, body)
    152 %b(&@test)
    153 :@body
    154 body
    155 :@test
    156 %beq(ra, rb, &@body)
    157 %endm
    158 
    159 %macro while_ne(ra, rb, body)
    160 %b(&@test)
    161 :@body
    162 body
    163 :@test
    164 %bne(ra, rb, &@body)
    165 %endm
    166 
    167 %macro while_lt(ra, rb, body)
    168 %b(&@test)
    169 :@body
    170 body
    171 :@test
    172 %blt(ra, rb, &@body)
    173 %endm
    174 
    175 %macro while_ltu(ra, rb, body)
    176 %b(&@test)
    177 :@body
    178 body
    179 :@test
    180 %bltu(ra, rb, &@body)
    181 %endm
    182 
    183 %macro while_eqz(ra, body)
    184 %b(&@test)
    185 :@body
    186 body
    187 :@test
    188 %beqz(ra, &@body)
    189 %endm
    190 
    191 %macro while_nez(ra, body)
    192 %b(&@test)
    193 :@body
    194 body
    195 :@test
    196 %bnez(ra, &@body)
    197 %endm
    198 
    199 %macro while_ltz(ra, body)
    200 %b(&@test)
    201 :@body
    202 body
    203 :@test
    204 %bltz(ra, &@body)
    205 %endm
    206 
    207 # ---- %do_while_<cc> ----------------------------------------------------
    208 
    209 %macro do_while_eq(ra, rb, body)
    210 :@body
    211 body
    212 %beq(ra, rb, &@body)
    213 %endm
    214 
    215 %macro do_while_ne(ra, rb, body)
    216 :@body
    217 body
    218 %bne(ra, rb, &@body)
    219 %endm
    220 
    221 %macro do_while_lt(ra, rb, body)
    222 :@body
    223 body
    224 %blt(ra, rb, &@body)
    225 %endm
    226 
    227 %macro do_while_ltu(ra, rb, body)
    228 :@body
    229 body
    230 %bltu(ra, rb, &@body)
    231 %endm
    232 
    233 %macro do_while_eqz(ra, body)
    234 :@body
    235 body
    236 %beqz(ra, &@body)
    237 %endm
    238 
    239 %macro do_while_nez(ra, body)
    240 :@body
    241 body
    242 %bnez(ra, &@body)
    243 %endm
    244 
    245 %macro do_while_ltz(ra, body)
    246 :@body
    247 body
    248 %bltz(ra, &@body)
    249 %endm
    250 
    251 # ---- %for_lt ------------------------------------------------------------
    252 
    253 %macro for_lt(i_reg, n_reg, body)
    254 %li(i_reg) $(0)
    255 %b(&@test)
    256 :@body
    257 body
    258 %addi(i_reg, i_reg, 1)
    259 :@test
    260 %blt(i_reg, n_reg, &@body)
    261 %endm
    262 
    263 # ---- %loop --------------------------------------------------------------
    264 
    265 %macro loop(body)
    266 :@top
    267 body
    268 %b(&@top)
    269 %endm
    270 
    271 # ---- Tagged loops -------------------------------------------------------
    272 #
    273 # Each tagged form emits two global labels `tag_top` and `tag_end`, built
    274 # by `##` paste so references cross every macro boundary cleanly.
    275 # `%break(tag)` jumps to `tag_end`; `%continue(tag)` jumps to `tag_top`.
    276 
    277 %macro loop_tag(tag, body)
    278 : ## tag ## _top
    279 body
    280 %b(& ## tag ## _top)
    281 : ## tag ## _end
    282 %endm
    283 
    284 %macro while_tag_eq(tag, ra, rb, body)
    285 %b(& ## tag ## _top)
    286 :@body
    287 body
    288 : ## tag ## _top
    289 %beq(ra, rb, &@body)
    290 : ## tag ## _end
    291 %endm
    292 
    293 %macro while_tag_ne(tag, ra, rb, body)
    294 %b(& ## tag ## _top)
    295 :@body
    296 body
    297 : ## tag ## _top
    298 %bne(ra, rb, &@body)
    299 : ## tag ## _end
    300 %endm
    301 
    302 %macro while_tag_lt(tag, ra, rb, body)
    303 %b(& ## tag ## _top)
    304 :@body
    305 body
    306 : ## tag ## _top
    307 %blt(ra, rb, &@body)
    308 : ## tag ## _end
    309 %endm
    310 
    311 %macro while_tag_ltu(tag, ra, rb, body)
    312 %b(& ## tag ## _top)
    313 :@body
    314 body
    315 : ## tag ## _top
    316 %bltu(ra, rb, &@body)
    317 : ## tag ## _end
    318 %endm
    319 
    320 %macro while_tag_eqz(tag, ra, body)
    321 %b(& ## tag ## _top)
    322 :@body
    323 body
    324 : ## tag ## _top
    325 %beqz(ra, &@body)
    326 : ## tag ## _end
    327 %endm
    328 
    329 %macro while_tag_nez(tag, ra, body)
    330 %b(& ## tag ## _top)
    331 :@body
    332 body
    333 : ## tag ## _top
    334 %bnez(ra, &@body)
    335 : ## tag ## _end
    336 %endm
    337 
    338 %macro while_tag_ltz(tag, ra, body)
    339 %b(& ## tag ## _top)
    340 :@body
    341 body
    342 : ## tag ## _top
    343 %bltz(ra, &@body)
    344 : ## tag ## _end
    345 %endm
    346 
    347 %macro for_lt_tag(tag, i_reg, n_reg, body)
    348 %li(i_reg) $(0)
    349 %b(& ## tag ## _test)
    350 :@body
    351 body
    352 : ## tag ## _top
    353 %addi(i_reg, i_reg, 1)
    354 : ## tag ## _test
    355 %blt(i_reg, n_reg, &@body)
    356 : ## tag ## _end
    357 %endm
    358 
    359 %macro break(tag)
    360 %b(& ## tag ## _end)
    361 %endm
    362 
    363 %macro continue(tag)
    364 %b(& ## tag ## _top)
    365 %endm
    366 
    367 # =========================================================================
    368 # %fn -- scope-introducing function definition
    369 # =========================================================================
    370 #
    371 # Pushes a scope named after the function so `::foo` inside the body
    372 # mangles to `name__foo`. The body is bracketed by %enter(size) and
    373 # %eret(), so functions defined with %fn always carry a standard frame.
    374 
    375 %macro fn(name, size, body)
    376 : ## name
    377 %scope name
    378 %enter(size)
    379 body
    380 %eret()
    381 %endscope
    382 %endm
    383 
    384 # =========================================================================
    385 # %assert_<cc> macros
    386 # =========================================================================
    387 #
    388 # Branch past the panic call when the condition holds; otherwise fall
    389 # through to `LA a0, msg; LA_BR &panic; CALL`. Each assert requires the
    390 # enclosing function to have an established frame.
    391 
    392 %macro assert_eq(ra, rb, msg)
    393 %beq(ra, rb, &@done)
    394 %la(a0, & ## msg)
    395 %call(&panic)
    396 :@done
    397 %endm
    398 
    399 %macro assert_ne(ra, rb, msg)
    400 %bne(ra, rb, &@done)
    401 %la(a0, & ## msg)
    402 %call(&panic)
    403 :@done
    404 %endm
    405 
    406 %macro assert_lt(ra, rb, msg)
    407 %blt(ra, rb, &@done)
    408 %la(a0, & ## msg)
    409 %call(&panic)
    410 :@done
    411 %endm
    412 
    413 %macro assert_ltu(ra, rb, msg)
    414 %bltu(ra, rb, &@done)
    415 %la(a0, & ## msg)
    416 %call(&panic)
    417 :@done
    418 %endm
    419 
    420 %macro assert_eqz(ra, msg)
    421 %beqz(ra, &@done)
    422 %la(a0, & ## msg)
    423 %call(&panic)
    424 :@done
    425 %endm
    426 
    427 %macro assert_nez(ra, msg)
    428 %bnez(ra, &@done)
    429 %la(a0, & ## msg)
    430 %call(&panic)
    431 :@done
    432 %endm
    433 
    434 %macro assert_ltz(ra, msg)
    435 %bltz(ra, &@done)
    436 %la(a0, & ## msg)
    437 %call(&panic)
    438 :@done
    439 %endm
    440 
    441 # =========================================================================
    442 # Memory and strings
    443 # =========================================================================
    444 
    445 # memcpy(dst=a0, src=a1, n=a2) -> dst (a0)
    446 # Leaf. Copies n bytes from src to dst. No overlap support where
    447 # dst > src && dst < src + n.
    448 :memcpy
    449 %scope memcpy
    450 %mov(a3, a0)
    451 %li(t0) $(0)
    452 ::loop
    453 %beq(t0, a2, &::done)
    454 %add(t1, a1, t0)
    455 %lb(t1, t1, 0)
    456 %add(t2, a3, t0)
    457 %sb(t1, t2, 0)
    458 %addi(t0, t0, 1)
    459 %b(&::loop)
    460 ::done
    461 %mov(a0, a3)
    462 %ret()
    463 %endscope
    464 
    465 # memset(dst=a0, byte=a1, n=a2) -> dst (a0)
    466 :memset
    467 %scope memset
    468 %mov(a3, a0)
    469 %li(t0) $(0)
    470 ::loop
    471 %beq(t0, a2, &::done)
    472 %add(t1, a3, t0)
    473 %sb(a1, t1, 0)
    474 %addi(t0, t0, 1)
    475 %b(&::loop)
    476 ::done
    477 %mov(a0, a3)
    478 %ret()
    479 %endscope
    480 
    481 # memcmp(a=a0, b=a1, n=a2) -> -1/0/1 (a0)
    482 :memcmp
    483 %scope memcmp
    484 %li(t0) $(0)
    485 ::loop
    486 %beq(t0, a2, &::eq)
    487 %add(t1, a0, t0)
    488 %lb(t1, t1, 0)
    489 %add(t2, a1, t0)
    490 %lb(t2, t2, 0)
    491 %bltu(t1, t2, &::lt)
    492 %bltu(t2, t1, &::gt)
    493 %addi(t0, t0, 1)
    494 %b(&::loop)
    495 ::lt
    496 %li(a0) $(-1)
    497 %ret()
    498 ::gt
    499 %li(a0) $(1)
    500 %ret()
    501 ::eq
    502 %li(a0) $(0)
    503 %ret()
    504 %endscope
    505 
    506 # strlen(cstr=a0) -> n (a0)
    507 :strlen
    508 %scope strlen
    509 %mov(a1, a0)
    510 ::loop
    511 %lb(t0, a1, 0)
    512 %beqz(t0, &::done)
    513 %addi(a1, a1, 1)
    514 %b(&::loop)
    515 ::done
    516 %sub(a0, a1, a0)
    517 %ret()
    518 %endscope
    519 
    520 # streq(a=a0, b=a1) -> 0 or 1
    521 :streq
    522 %scope streq
    523 ::loop
    524 %lb(t0, a0, 0)
    525 %lb(t1, a1, 0)
    526 %bne(t0, t1, &::ne)
    527 %beqz(t0, &::eq)
    528 %addi(a0, a0, 1)
    529 %addi(a1, a1, 1)
    530 %b(&::loop)
    531 ::ne
    532 %li(a0) $(0)
    533 %ret()
    534 ::eq
    535 %li(a0) $(1)
    536 %ret()
    537 %endscope
    538 
    539 # strcmp(a=a0, b=a1) -> -1/0/1
    540 :strcmp
    541 %scope strcmp
    542 ::loop
    543 %lb(t0, a0, 0)
    544 %lb(t1, a1, 0)
    545 %bltu(t0, t1, &::lt)
    546 %bltu(t1, t0, &::gt)
    547 %beqz(t0, &::eq)
    548 %addi(a0, a0, 1)
    549 %addi(a1, a1, 1)
    550 %b(&::loop)
    551 ::lt
    552 %li(a0) $(-1)
    553 %ret()
    554 ::gt
    555 %li(a0) $(1)
    556 %ret()
    557 ::eq
    558 %li(a0) $(0)
    559 %ret()
    560 %endscope
    561 
    562 # =========================================================================
    563 # Integer parsing and formatting
    564 # =========================================================================
    565 
    566 # parse_dec(buf=a0, len=a1) -> (value=a0, consumed=a1)
    567 # Uses an 8-byte frame slot to save buf_start; all hot-loop state lives
    568 # in caller-saved registers.
    569 :parse_dec
    570 %scope parse_dec
    571 %enter(8)
    572 %st(a0, sp, 0)
    573 %add(a3, a0, a1)
    574 %mov(a2, a0)
    575 %li(t0) $(0)
    576 %li(t1) $(0)
    577 
    578 %beq(a2, a3, &::after_sign)
    579 %lb(t2, a2, 0)
    580 %addi(t2, t2, -45)
    581 %bnez(t2, &::after_sign)
    582 %li(t0) $(1)
    583 %addi(a2, a2, 1)
    584 
    585 ::after_sign
    586 %mov(a1, a2)
    587 
    588 ::digit_loop
    589 %beq(a2, a3, &::digits_done)
    590 %lb(t2, a2, 0)
    591 %addi(t2, t2, -48)
    592 %bltz(t2, &::digits_done)
    593 %li(a0) $(9)
    594 %bltu(a0, t2, &::digits_done)
    595 %li(a0) $(10)
    596 %mul(t1, t1, a0)
    597 %add(t1, t1, t2)
    598 %addi(a2, a2, 1)
    599 %b(&::digit_loop)
    600 
    601 ::digits_done
    602 %beq(a2, a1, &::no_digits)
    603 
    604 %bnez(t0, &::apply_sign)
    605 %b(&::compute_return)
    606 ::apply_sign
    607 %li(a0) $(0)
    608 %sub(t1, a0, t1)
    609 
    610 ::compute_return
    611 %ld(a0, sp, 0)
    612 %sub(a1, a2, a0)
    613 %mov(a0, t1)
    614 %eret()
    615 
    616 ::no_digits
    617 %li(a0) $(0)
    618 %li(a1) $(0)
    619 %eret()
    620 %endscope
    621 
    622 # parse_hex(buf=a0, len=a1) -> (value=a0, consumed=a1)
    623 :parse_hex
    624 %scope parse_hex
    625 %enter(8)
    626 %st(a0, sp, 0)
    627 %add(a3, a0, a1)
    628 %mov(a2, a0)
    629 %li(t1) $(0)
    630 %mov(a1, a2)
    631 
    632 ::loop
    633 %beq(a2, a3, &::done)
    634 %lb(t2, a2, 0)
    635 
    636 %addi(t0, t2, -48)
    637 %bltz(t0, &::check_lower)
    638 %li(a0) $(9)
    639 %bltu(a0, t0, &::check_lower)
    640 %b(&::accept)
    641 
    642 ::check_lower
    643 %addi(t0, t2, -97)
    644 %bltz(t0, &::check_upper)
    645 %li(a0) $(5)
    646 %bltu(a0, t0, &::check_upper)
    647 %addi(t0, t0, 10)
    648 %b(&::accept)
    649 
    650 ::check_upper
    651 %addi(t0, t2, -65)
    652 %bltz(t0, &::done)
    653 %li(a0) $(5)
    654 %bltu(a0, t0, &::done)
    655 %addi(t0, t0, 10)
    656 
    657 ::accept
    658 %shli(t1, t1, 4)
    659 %or(t1, t1, t0)
    660 %addi(a2, a2, 1)
    661 %b(&::loop)
    662 
    663 ::done
    664 %beq(a2, a1, &::no_digits)
    665 %ld(a0, sp, 0)
    666 %sub(a1, a2, a0)
    667 %mov(a0, t1)
    668 %eret()
    669 
    670 ::no_digits
    671 %li(a0) $(0)
    672 %li(a1) $(0)
    673 %eret()
    674 %endscope
    675 
    676 # fmt_dec(buf=a0, value=a1) -> n_bytes (a0)
    677 #
    678 # Unified signed formatting: digits are written from the per-iteration
    679 # `value % 10`, negated when value is negative. This avoids the
    680 # INT_MIN-overflow trap that `value = -value` would hit.
    681 :fmt_dec
    682 %scope fmt_dec
    683 %enter(8)
    684 %st(a0, sp, 0)
    685 
    686 %bltz(a1, &::is_neg)
    687 %b(&::count)
    688 ::is_neg
    689 %li(t0) $(45)
    690 %sb(t0, a0, 0)
    691 %addi(a0, a0, 1)
    692 
    693 ::count
    694 %mov(t0, a1)
    695 %li(a2) $(1)
    696 %li(t1) $(10)
    697 ::count_loop
    698 %div(t0, t0, t1)
    699 %beqz(t0, &::count_done)
    700 %addi(a2, a2, 1)
    701 %b(&::count_loop)
    702 ::count_done
    703 
    704 %add(a3, a0, a2)
    705 
    706 ::dig_loop
    707 %addi(a3, a3, -1)
    708 %rem(t0, a1, t1)
    709 %bltz(t0, &::neg_digit)
    710 %b(&::write_digit)
    711 ::neg_digit
    712 %li(t2) $(0)
    713 %sub(t0, t2, t0)
    714 ::write_digit
    715 %addi(t0, t0, 48)
    716 %sb(t0, a3, 0)
    717 %div(a1, a1, t1)
    718 %bnez(a1, &::dig_loop)
    719 
    720 %ld(t2, sp, 0)
    721 %add(a0, a0, a2)
    722 %sub(a0, a0, t2)
    723 %eret()
    724 %endscope
    725 
    726 # fmt_hex(buf=a0, value=a1) -> n_bytes (a0)
    727 :fmt_hex
    728 %scope fmt_hex
    729 %enter(8)
    730 %st(a0, sp, 0)
    731 
    732 %bnez(a1, &::nonzero)
    733 %li(t0) $(48)
    734 %sb(t0, a0, 0)
    735 %li(a0) $(1)
    736 %eret()
    737 
    738 ::nonzero
    739 %mov(t0, a1)
    740 %li(a2) $(0)
    741 ::count_loop
    742 %addi(a2, a2, 1)
    743 %shri(t0, t0, 4)
    744 %bnez(t0, &::count_loop)
    745 
    746 %add(a3, a0, a2)
    747 
    748 ::dig_loop
    749 %addi(a3, a3, -1)
    750 %andi(t0, a1, 15)
    751 %li(t1) $(10)
    752 %bltu(t0, t1, &::is_letter)
    753 %addi(t0, t0, -10)
    754 %addi(t0, t0, 97)
    755 %b(&::write_digit)
    756 ::is_letter
    757 %addi(t0, t0, 48)
    758 ::write_digit
    759 %sb(t0, a3, 0)
    760 %shri(a1, a1, 4)
    761 %bnez(a1, &::dig_loop)
    762 
    763 %ld(t2, sp, 0)
    764 %add(a0, a0, a2)
    765 %sub(a0, a0, t2)
    766 %eret()
    767 %endscope
    768 
    769 # =========================================================================
    770 # Character predicates
    771 # =========================================================================
    772 
    773 # is_digit(c=a0) -> 0 or 1
    774 :is_digit
    775 %scope is_digit
    776 %addi(t0, a0, -48)
    777 %li(t1) $(10)
    778 %li(a0) $(1)
    779 %bltu(t0, t1, &::done)
    780 %li(a0) $(0)
    781 ::done
    782 %ret()
    783 %endscope
    784 
    785 # is_hex_digit(c=a0) -> 0 or 1
    786 :is_hex_digit
    787 %scope is_hex_digit
    788 %li(t2) $(1)
    789 %addi(t0, a0, -48)
    790 %li(t1) $(10)
    791 %bltu(t0, t1, &::done)
    792 %addi(t0, a0, -97)
    793 %li(t1) $(6)
    794 %bltu(t0, t1, &::done)
    795 %addi(t0, a0, -65)
    796 %bltu(t0, t1, &::done)
    797 %li(t2) $(0)
    798 ::done
    799 %mov(a0, t2)
    800 %ret()
    801 %endscope
    802 
    803 # is_space(c=a0) -> 0 or 1
    804 :is_space
    805 %scope is_space
    806 %li(t2) $(1)
    807 %addi(t0, a0, -32)
    808 %beqz(t0, &::done)
    809 %addi(t0, a0, -9)
    810 %li(t1) $(5)
    811 %bltu(t0, t1, &::done)
    812 %li(t2) $(0)
    813 ::done
    814 %mov(a0, t2)
    815 %ret()
    816 %endscope
    817 
    818 # is_alpha(c=a0) -> 0 or 1
    819 :is_alpha
    820 %scope is_alpha
    821 %li(t2) $(1)
    822 %addi(t0, a0, -97)
    823 %li(t1) $(26)
    824 %bltu(t0, t1, &::done)
    825 %addi(t0, a0, -65)
    826 %bltu(t0, t1, &::done)
    827 %li(t2) $(0)
    828 ::done
    829 %mov(a0, t2)
    830 %ret()
    831 %endscope
    832 
    833 # is_alnum(c=a0) -> 0 or 1
    834 :is_alnum
    835 %scope is_alnum
    836 %li(t2) $(1)
    837 %addi(t0, a0, -48)
    838 %li(t1) $(10)
    839 %bltu(t0, t1, &::done)
    840 %addi(t0, a0, -97)
    841 %li(t1) $(26)
    842 %bltu(t0, t1, &::done)
    843 %addi(t0, a0, -65)
    844 %bltu(t0, t1, &::done)
    845 %li(t2) $(0)
    846 ::done
    847 %mov(a0, t2)
    848 %ret()
    849 %endscope
    850 
    851 # =========================================================================
    852 # Raw syscall wrappers
    853 # =========================================================================
    854 #
    855 # Each wrapper shifts arguments into the syscall convention
    856 # (a0 = number, a1..a3/t0/s0/s1 = args 0..5), emits SYSCALL, and returns
    857 # the raw kernel result. Syscall clobbers only a0, so t0/s0/s1 do not
    858 # need saving.
    859 
    860 # sys_read(fd=a0, buf=a1, len=a2) -> n (a0)
    861 :sys_read
    862 %mov(a3, a2)
    863 %mov(a2, a1)
    864 %mov(a1, a0)
    865 %li(a0) %p1_sys_read()
    866 %syscall()
    867 %ret()
    868 
    869 # sys_write(fd=a0, buf=a1, len=a2) -> n (a0)
    870 :sys_write
    871 %mov(a3, a2)
    872 %mov(a2, a1)
    873 %mov(a1, a0)
    874 %li(a0) %p1_sys_write()
    875 %syscall()
    876 %ret()
    877 
    878 # sys_open(path=a0, flags=a1, mode=a2) -> fd (a0)
    879 # Implemented as openat(AT_FDCWD, path, flags, mode). AT_FDCWD = -100.
    880 :sys_open
    881 %mov(t0, a2)
    882 %mov(a3, a1)
    883 %mov(a2, a0)
    884 %li(a1) $(-100)
    885 %li(a0) %p1_sys_openat()
    886 %syscall()
    887 %ret()
    888 
    889 # sys_close(fd=a0) -> r (a0)
    890 :sys_close
    891 %mov(a1, a0)
    892 %li(a0) %p1_sys_close()
    893 %syscall()
    894 %ret()
    895 
    896 # sys_exit(code=a0) -> never returns
    897 :sys_exit
    898 %scope sys_exit
    899 %mov(a1, a0)
    900 %li(a0) %p1_sys_exit()
    901 %syscall()
    902 ::spin
    903 %b(&::spin)
    904 %endscope
    905 
    906 # =========================================================================
    907 # Print helpers
    908 # =========================================================================
    909 #
    910 # print(buf, len) and eprint(buf, len) loop on sys_write until all bytes
    911 # are written or the kernel reports an error. All other print helpers
    912 # compose on top of those two.
    913 
    914 %fn(print, 16, {
    915 %st(s0, sp, 0)
    916 %st(s1, sp, 8)
    917 %mov(s0, a0)
    918 %mov(s1, a1)
    919 
    920 ::loop
    921 %beqz(s1, &::done_ok)
    922 %li(a0) $(1)
    923 %mov(a1, s0)
    924 %mov(a2, s1)
    925 %call(&sys_write)
    926 %bltz(a0, &::done)
    927 %add(s0, s0, a0)
    928 %sub(s1, s1, a0)
    929 %b(&::loop)
    930 
    931 ::done_ok
    932 %li(a0) $(0)
    933 ::done
    934 %ld(s0, sp, 0)
    935 %ld(s1, sp, 8)
    936 })
    937 
    938 %fn(eprint, 16, {
    939 %st(s0, sp, 0)
    940 %st(s1, sp, 8)
    941 %mov(s0, a0)
    942 %mov(s1, a1)
    943 
    944 ::loop
    945 %beqz(s1, &::done_ok)
    946 %li(a0) $(2)
    947 %mov(a1, s0)
    948 %mov(a2, s1)
    949 %call(&sys_write)
    950 %bltz(a0, &::done)
    951 %add(s0, s0, a0)
    952 %sub(s1, s1, a0)
    953 %b(&::loop)
    954 
    955 ::done_ok
    956 %li(a0) $(0)
    957 ::done
    958 %ld(s0, sp, 0)
    959 %ld(s1, sp, 8)
    960 })
    961 
    962 %fn(println, 16, {
    963 %st(s0, sp, 0)
    964 
    965 %call(&print)
    966 %mov(s0, a0)
    967 %bltz(s0, &::done)
    968 
    969 %la(a0, &libp1pp__newline)
    970 %li(a1) $(1)
    971 %call(&print)
    972 %mov(s0, a0)
    973 
    974 ::done
    975 %mov(a0, s0)
    976 %ld(s0, sp, 0)
    977 })
    978 
    979 %fn(eprintln, 16, {
    980 %st(s0, sp, 0)
    981 
    982 %call(&eprint)
    983 %mov(s0, a0)
    984 %bltz(s0, &::done)
    985 
    986 %la(a0, &libp1pp__newline)
    987 %li(a1) $(1)
    988 %call(&eprint)
    989 %mov(s0, a0)
    990 
    991 ::done
    992 %mov(a0, s0)
    993 %ld(s0, sp, 0)
    994 })
    995 
    996 %fn(print_cstr, 16, {
    997 %st(s0, sp, 0)
    998 %mov(s0, a0)
    999 %call(&strlen)
   1000 %mov(a1, a0)
   1001 %mov(a0, s0)
   1002 %call(&print)
   1003 %ld(s0, sp, 0)
   1004 })
   1005 
   1006 %fn(eprint_cstr, 16, {
   1007 %st(s0, sp, 0)
   1008 %mov(s0, a0)
   1009 %call(&strlen)
   1010 %mov(a1, a0)
   1011 %mov(a0, s0)
   1012 %call(&eprint)
   1013 %ld(s0, sp, 0)
   1014 })
   1015 
   1016 %fn(print_int, 0, {
   1017 %mov(a1, a0)
   1018 %la(a0, &libp1pp__num_buf)
   1019 %call(&fmt_dec)
   1020 %mov(a1, a0)
   1021 %la(a0, &libp1pp__num_buf)
   1022 %call(&print)
   1023 })
   1024 
   1025 %fn(print_hex, 0, {
   1026 %mov(a1, a0)
   1027 %la(a0, &libp1pp__num_buf)
   1028 %call(&fmt_hex)
   1029 %mov(a1, a0)
   1030 %la(a0, &libp1pp__num_buf)
   1031 %call(&print)
   1032 })
   1033 
   1034 # =========================================================================
   1035 # File helpers
   1036 # =========================================================================
   1037 
   1038 # read_file(path=a0, buf=a1, cap=a2) -> n or -1
   1039 %fn(read_file, 32, {
   1040 %st(s0, sp, 0)
   1041 %st(s1, sp, 8)
   1042 %st(s2, sp, 16)
   1043 %st(s3, sp, 24)
   1044 
   1045 %mov(s1, a1)
   1046 %mov(s2, a2)
   1047 
   1048 %li(a1) $(0)
   1049 %li(a2) $(0)
   1050 %call(&sys_open)
   1051 %bltz(a0, &::open_fail)
   1052 %mov(s3, a0)
   1053 
   1054 %mov(a0, s3)
   1055 %mov(a1, s1)
   1056 %mov(a2, s2)
   1057 %call(&sys_read)
   1058 %mov(s0, a0)
   1059 
   1060 %mov(a0, s3)
   1061 %call(&sys_close)
   1062 
   1063 %mov(a0, s0)
   1064 %bltz(a0, &::read_fail)
   1065 %b(&::done)
   1066 
   1067 ::read_fail
   1068 %li(a0) $(-1)
   1069 %b(&::done)
   1070 
   1071 ::open_fail
   1072 %li(a0) $(-1)
   1073 
   1074 ::done
   1075 %ld(s0, sp, 0)
   1076 %ld(s1, sp, 8)
   1077 %ld(s2, sp, 16)
   1078 %ld(s3, sp, 24)
   1079 })
   1080 
   1081 # libp1pp__write_all(fd=a0, buf=a1, len=a2) -> 0 or <0 on error
   1082 #
   1083 # Loop on sys_write until all bytes are written. Used by print / eprint
   1084 # / write_file. Retries partial writes but returns the first negative
   1085 # kernel return unchanged.
   1086 %fn(libp1pp__write_all, 24, {
   1087 %st(s0, sp, 0)
   1088 %st(s1, sp, 8)
   1089 %st(s2, sp, 16)
   1090 
   1091 %mov(s0, a0)
   1092 %mov(s1, a1)
   1093 %mov(s2, a2)
   1094 
   1095 ::loop
   1096 %beqz(s2, &::done_ok)
   1097 %mov(a0, s0)
   1098 %mov(a1, s1)
   1099 %mov(a2, s2)
   1100 %call(&sys_write)
   1101 %bltz(a0, &::done)
   1102 %add(s1, s1, a0)
   1103 %sub(s2, s2, a0)
   1104 %b(&::loop)
   1105 
   1106 ::done_ok
   1107 %li(a0) $(0)
   1108 ::done
   1109 %ld(s0, sp, 0)
   1110 %ld(s1, sp, 8)
   1111 %ld(s2, sp, 16)
   1112 })
   1113 
   1114 # write_file(path=a0, buf=a1, len=a2) -> 0 or -1
   1115 #
   1116 # Flags: O_WRONLY|O_CREAT|O_TRUNC. On Linux these are 0x1 | 0x40 |
   1117 # 0x200 = 0x241. Mode 0644 octal = 0x1A4.
   1118 %fn(write_file, 24, {
   1119 %st(s0, sp, 0)
   1120 %st(s1, sp, 8)
   1121 %st(s2, sp, 16)
   1122 
   1123 %mov(s0, a1)
   1124 %mov(s1, a2)
   1125 
   1126 %li(a1) $(0x241)
   1127 %li(a2) $(0x1A4)
   1128 %call(&sys_open)
   1129 %bltz(a0, &::open_fail)
   1130 %mov(s2, a0)
   1131 
   1132 %mov(a0, s2)
   1133 %mov(a1, s0)
   1134 %mov(a2, s1)
   1135 %call(&libp1pp__write_all)
   1136 
   1137 %mov(s0, a0)
   1138 %mov(a0, s2)
   1139 %call(&sys_close)
   1140 
   1141 %mov(a0, s0)
   1142 %bltz(a0, &::fail_ret)
   1143 %li(a0) $(0)
   1144 %b(&::done)
   1145 
   1146 ::fail_ret
   1147 %li(a0) $(-1)
   1148 %b(&::done)
   1149 
   1150 ::open_fail
   1151 %li(a0) $(-1)
   1152 
   1153 ::done
   1154 %ld(s0, sp, 0)
   1155 %ld(s1, sp, 8)
   1156 %ld(s2, sp, 16)
   1157 })
   1158 
   1159 # =========================================================================
   1160 # Bump allocator
   1161 # =========================================================================
   1162 #
   1163 # Single global arena, bytes carved by monotonic cursor with 8-byte
   1164 # alignment. bump_alloc returns 0 when the request would overflow.
   1165 
   1166 # bump_init(base=a0, cap=a1) -> 0
   1167 :bump_init
   1168 %la(t0, &libp1pp__bump_base)
   1169 %st(a0, t0, 0)
   1170 %la(t0, &libp1pp__bump_cursor)
   1171 %st(a0, t0, 0)
   1172 %la(t0, &libp1pp__bump_cap)
   1173 %st(a1, t0, 0)
   1174 %li(a0) $(0)
   1175 %ret()
   1176 
   1177 # bump_alloc(n=a0) -> ptr (0 on exhaustion)
   1178 #
   1179 # Round n up to a multiple of 8, then admit iff cursor + n_rounded does
   1180 # not exceed base + cap. On success, advance the cursor and return the
   1181 # pre-advance value; on failure, leave the cursor untouched and return 0.
   1182 :bump_alloc
   1183 %scope bump_alloc
   1184 %addi(a0, a0, 7)
   1185 %li(t0) $(-8)
   1186 %and(a0, a0, t0)
   1187 %la(t0, &libp1pp__bump_cursor)
   1188 %ld(t1, t0, 0)
   1189 %add(t2, t1, a0)
   1190 %la(a1, &libp1pp__bump_base)
   1191 %ld(a2, a1, 0)
   1192 %la(a1, &libp1pp__bump_cap)
   1193 %ld(a3, a1, 0)
   1194 %add(a3, a2, a3)
   1195 %bltu(a3, t2, &::fail)
   1196 %st(t2, t0, 0)
   1197 %mov(a0, t1)
   1198 %ret()
   1199 ::fail
   1200 %li(a0) $(0)
   1201 %ret()
   1202 %endscope
   1203 
   1204 # bump_mark() -> saved
   1205 :bump_mark
   1206 %la(t0, &libp1pp__bump_cursor)
   1207 %ld(a0, t0, 0)
   1208 %ret()
   1209 
   1210 # bump_release(saved=a0) -> 0
   1211 :bump_release
   1212 %la(t0, &libp1pp__bump_cursor)
   1213 %st(a0, t0, 0)
   1214 %li(a0) $(0)
   1215 %ret()
   1216 
   1217 # bump_reset() -> 0
   1218 :bump_reset
   1219 %la(t0, &libp1pp__bump_base)
   1220 %ld(t1, t0, 0)
   1221 %la(t0, &libp1pp__bump_cursor)
   1222 %st(t1, t0, 0)
   1223 %li(a0) $(0)
   1224 %ret()
   1225 
   1226 # =========================================================================
   1227 # Panic
   1228 # =========================================================================
   1229 
   1230 # panic(msg_cstr=a0) -> never returns
   1231 %fn(panic, 0, {
   1232 %call(&eprint_cstr)
   1233 %la(a0, &libp1pp__newline)
   1234 %li(a1) $(1)
   1235 %call(&eprint)
   1236 %li(a0) $(1)
   1237 %call(&sys_exit)
   1238 ::spin
   1239 %b(&::spin)
   1240 })
   1241 
   1242 # =========================================================================
   1243 # Internal data
   1244 # =========================================================================
   1245 
   1246 # Single newline byte for println / eprintln / panic. Emitted as an
   1247 # 8-byte word (0x0A in the low byte, zeros above) so the following
   1248 # buffers and the user source that comes after libp1pp stay 8-byte
   1249 # aligned. sys_write reads only the one byte callers request.
   1250 :libp1pp__newline
   1251 $(10)
   1252 
   1253 # Scratch buffer used by print_int / print_hex. fmt_dec writes at most
   1254 # 20 bytes, fmt_hex at most 16, so 32 bytes with word alignment is
   1255 # comfortably above both.
   1256 :libp1pp__num_buf
   1257 $(0) $(0) $(0) $(0)
   1258 
   1259 # Bump-allocator state. Zero-initialized so bump_alloc returns 0 until
   1260 # bump_init installs an arena.
   1261 :libp1pp__bump_base
   1262 $(0)
   1263 :libp1pp__bump_cursor
   1264 $(0)
   1265 :libp1pp__bump_cap
   1266 $(0)