boot2

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

P1pp.P1pp (21409B)


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