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)