# uuid256.riscv64.s — UUID256 (README.md, random layout) in RISC-V 64 (RV64I) assembly for Linux (raw syscalls, no libc).
#
#   Build:  zig cc -target riscv64-linux-musl -nostdlib -static uuid256.riscv64.s -o /tmp/uuid256-rv64    (or GNU as/ld on RISC-V Linux)
#   Run:    /tmp/uuid256-rv64 5                    on RISC-V Linux
#           python3 emulate.py /tmp/uuid256-rv64 5 anywhere else (Unicorn CPU emulator; this Apple-silicon repo tests it that way)
#   Usage:  prog [N]     print N ids (default 10; 0 prints nothing; non-numeric → exit 2), one canonical 16-8-8-8-24 line each
#           prog -       test mode: read 32 raw bytes from stdin, apply §4, print canonical
#
# What it implements:
#   §5.1  32 bytes from getrandom(2)  (syscall 278)
#   §4    byte 12 = (byte 12 & 0x0F) | 0x40 (version 4);  byte 16 = (byte 16 & 0x3F) | 0x80 (variant 10)
#   §3.1  64 lowercase hex digits with hyphens after digits 16, 24, 32, 40
#
# Register use:  s1 = ids remaining · s2 = mode (0 random, 1 stdin) · s3 = &hexdigits · s4 = frame base
#                0(s4)..31 = the 32-byte id · 32(s4)..100 = the 68-char text + '\n'
# RISC-V Linux syscall ABI: a7 = number, a0..a5 = args, ecall, result in a0.

        .text
        .globl  _start
_start:                                         # 0(sp) = argc, 8(sp) = argv[0], 16(sp) = argv[1]
        ld      t0, 0(sp)                       # argc
        addi    t1, sp, 8                       # argv
        addi    sp, sp, -128
        mv      s4, sp                          # frame: id at 0(s4), text at 32(s4)
        li      s1, 10                          # N default
        li      s2, 0                           # mode: random
        la      s3, hexdigits
        li      t2, 2
        blt     t0, t2, Lloop                   # argc < 2 → defaults
        ld      t3, 8(t1)                       # argv[1]
        lbu     t4, 0(t3)
        li      t5, 45                          # '-'
        bne     t4, t5, Latoi
        lbu     t4, 1(t3)
        bnez    t4, Latoi
        li      s2, 1                           # test mode
        li      s1, 1
        j       Lloop
Latoi:                                          # N = decimal(argv[1]); 0 → print nothing; non-numeric → exit 2
        li      s1, 0
        li      t6, 10
Latoi_next:
        lbu     t4, 0(t3)
        beqz    t4, Latoi_done
        addi    t4, t4, -48                     # '0'
        li      t5, 9
        bltu    t5, t4, Latoi_bad               # t4 > 9 (unsigned) → not a digit
        mul     s1, s1, t6
        add     s1, s1, t4
        addi    t3, t3, 1
        j       Latoi_next
Latoi_bad:
        li      a0, 2                           # non-numeric argument → usage error, exit 2
        j       Lexit
Latoi_done:                                     # (N = 0 prints nothing and exits 0)

Lloop:
        beqz    s1, Ldone

        # ---- 32 bytes: §5.1 getrandom, or stdin in test mode -----------------------------
        bnez    s2, Lstdin
        li      a7, 278                         # SYS_getrandom(buf, len, flags)
        mv      a0, s4
        li      a1, 32
        li      a2, 0
        ecall
        li      t0, 32
        bne     a0, t0, Lfail
        j       Lhave
Lstdin:
        li      s5, 0                           # bytes read so far; loop until all 32 arrive (a pipe may deliver them in pieces)
Lread_more:
        li      a7, 63                          # SYS_read(0, buf + got, 32 - got)
        li      a0, 0
        add     a1, s4, s5
        li      a2, 32
        sub     a2, a2, s5
        ecall
        blez    a0, Lfail                       # EOF or error before 32 bytes
        add     s5, s5, a0
        li      t0, 32
        blt     s5, t0, Lread_more
Lhave:
        # ---- §4: version nibble (byte 12) and variant bits (byte 16) ---------------------
        lbu     t0, 12(s4)
        andi    t0, t0, 0x0F
        ori     t0, t0, 0x40                    # ver = 4  → hex digit 24 is '4'
        sb      t0, 12(s4)
        lbu     t0, 16(s4)
        andi    t0, t0, 0x3F
        ori     t0, t0, 0x80                    # var = 10 → hex digit 32 in 8..b
        sb      t0, 16(s4)

        # ---- §3.1: canonical text 16-8-8-8-24 --------------------------------------------
        addi    t1, s4, 32                      # output cursor
        li      t2, 0                           # byte index i
Lfmt:
        add     t3, s4, t2
        lbu     t3, 0(t3)                       # byte
        srli    t4, t3, 4
        add     t4, s3, t4
        lbu     t4, 0(t4)                       # high nibble → hex char
        sb      t4, 0(t1)
        andi    t4, t3, 0x0F
        add     t4, s3, t4
        lbu     t4, 0(t4)                       # low nibble  → hex char
        sb      t4, 1(t1)
        addi    t1, t1, 2
        addi    t2, t2, 1
        li      t5, 32
        beq     t2, t5, Lfmt_done
        li      t5, 8                           # hyphen after bytes 8, 12, 16, 20
        beq     t2, t5, Lhyphen                 #   (= hex digits 16, 24, 32, 40)
        li      t5, 12
        beq     t2, t5, Lhyphen
        li      t5, 16
        beq     t2, t5, Lhyphen
        li      t5, 20
        bne     t2, t5, Lfmt
Lhyphen:
        li      t5, 45                          # '-'
        sb      t5, 0(t1)
        addi    t1, t1, 1
        j       Lfmt
Lfmt_done:
        li      t5, 10                          # '\n'
        sb      t5, 0(t1)
        li      a7, 64                          # SYS_write(1, text, 69)
        li      a0, 1
        addi    a1, s4, 32
        li      a2, 69
        ecall
        addi    s1, s1, -1
        j       Lloop

Lfail:
        li      a0, 1                           # exit status 1
        j       Lexit
Ldone:
        li      a0, 0                           # exit status 0
Lexit:
        li      a7, 93                          # SYS_exit
        ecall

        .section .rodata
hexdigits:
        .ascii  "0123456789abcdef"
