// uuid256.s — UUID256 (README.md, random layout) in AArch64 assembly for macOS / Apple silicon.
//
//   Build:  cc -o uuid256-asm uuid256.s          (clang assembles it; links libSystem for
//                                                 getentropy / read / write — no C code)
//   Usage:  ./uuid256-asm [N]     print N ids (default 10; 0 prints nothing; non-numeric → exit 2), one canonical 16-8-8-8-24 line each
//           ./uuid256-asm -       test mode: read 32 raw bytes from stdin, apply §4, print canonical
//
// What it implements:
//   §5.1  32 bytes per id from the OS CSPRNG (getentropy(2)), fetched 8 ids (256 B) per call
//   §4    version nibble  → byte 12 = (byte 12 & 0x0F) | 0x40
//         variant bits    → byte 16 = (byte 16 & 0x3F) | 0x80
//   §3.1  canonical text: 64 lowercase hex digits with hyphens after digits 16, 24, 32, 40
//   Output is written one batch (up to 8 lines, 69 bytes each) per write(2) call.
//
// Register use:
//   x19 = ids remaining     x20 = mode (0 = getentropy, 1 = stdin)     x21 = stdin bytes read so far
//   x22 = ids in this batch (1..8)                                     x23 = id index within the batch
//   sp+0   .. sp+255 = up to 8 raw 32-byte ids
//   sp+256 .. sp+807 = up to 8 lines of 68 chars + '\n'

        .text
        .globl  _main
        .p2align 2
_main:
        stp     x29, x30, [sp, #-16]!       // frame + link register
        mov     x29, sp
        stp     x19, x20, [sp, #-16]!       // callee-saved we use
        stp     x21, x22, [sp, #-16]!
        str     x23, [sp, #-16]!            //   (x23 alone, 16-byte slot keeps sp aligned)
        sub     sp, sp, #816                // ids (256 B) + text (552 B), rounded to 16

        mov     x19, #10                    // N default
        mov     x20, #0                     // mode: random
        cmp     w0, #2                      // argc (an int: compare the 32-bit half) < 2 → defaults
        b.lt    Lloop
        ldr     x1, [x1, #8]                // argv[1]
        ldrb    w2, [x1]
        cmp     w2, #45                     // '-'  → test mode
        b.ne    Latoi
        ldrb    w3, [x1, #1]
        cbnz    w3, Latoi
        mov     x20, #1
        mov     x19, #1
        b       Lloop
Latoi:                                      // N = decimal(argv[1]); 0 → print nothing; non-numeric → exit 2
        mov     x19, #0
        mov     x4, #10
Latoi_next:
        ldrb    w2, [x1], #1
        cbz     w2, Latoi_done
        sub     w2, w2, #48                 // '0'
        cmp     w2, #9
        b.hi    Latoi_bad
        mul     x19, x19, x4
        add     x19, x19, x2
        b       Latoi_next
Latoi_bad:
        mov     w0, #2                      // non-numeric argument → usage error, exit 2
        b       Lexit
Latoi_done:                                 // (N = 0 prints nothing and exits 0)

Lloop:
        cbz     x19, Ldone
        mov     x22, #8                     // batch = min(remaining, 8)
        cmp     x19, x22
        csel    x22, x19, x22, lo

        // ---- raw bytes: §5.1 OS CSPRNG (batch × 32 B), or 32 B from stdin in test mode ------
        cbnz    x20, Lstdin
        mov     x0, sp                      // buf
        lsl     x1, x22, #5                 // len = batch * 32 (≤ 256, getentropy's maximum)
        bl      _getentropy
        cbnz    w0, Lfail
        b       Lhave
Lstdin:
        mov     x22, #1                     // one id per run in test mode
        mov     x21, #0                     // bytes read so far; loop until all 32 arrive
Lread_more:                                 //   (a pipe may deliver them in pieces)
        mov     x0, #0                      // fd 0
        add     x1, sp, x21                 // buf + got
        mov     x2, #32
        sub     x2, x2, x21                 // remaining
        bl      _read
        cmp     x0, #0
        b.le    Lfail                       // EOF or error before 32 bytes
        add     x21, x21, x0
        cmp     x21, #32
        b.lt    Lread_more
Lhave:
        adrp    x4, hexdigits@PAGE          // (x4 is caller-saved: reload after every bl)
        add     x4, x4, hexdigits@PAGEOFF
        add     x5, sp, #256                // output cursor
        mov     x23, #0                     // id index within the batch
Lid:
        lsl     x9, x23, #5
        add     x9, sp, x9                  // x9 → this id's 32 bytes

        // ---- §4: version nibble (byte 12) and variant bits (byte 16) --------------
        ldrb    w2, [x9, #12]
        and     w2, w2, #0x0F
        orr     w2, w2, #0x40               // ver = 4  → hex digit 24 is '4'
        strb    w2, [x9, #12]
        ldrb    w2, [x9, #16]
        and     w2, w2, #0x3F
        orr     w2, w2, #0x80               // var = 10 → hex digit 32 in 8..b
        strb    w2, [x9, #16]

        // ---- §3.1: canonical text 16-8-8-8-24 -----------------------------------
        mov     x6, #0                      // byte index i
Lfmt:
        ldrb    w7, [x9, x6]
        lsr     w8, w7, #4
        ldrb    w8, [x4, x8]                // high nibble → hex char
        strb    w8, [x5], #1
        and     w8, w7, #0x0F
        ldrb    w8, [x4, x8]                // low nibble  → hex char
        strb    w8, [x5], #1
        add     x6, x6, #1
        cmp     x6, #32
        b.eq    Lfmt_done
        cmp     x6, #8                      // hyphen after bytes 8, 12, 16, 20
        b.eq    Lhyphen                     //   (= hex digits 16, 24, 32, 40)
        cmp     x6, #12
        b.eq    Lhyphen
        cmp     x6, #16
        b.eq    Lhyphen
        cmp     x6, #20
        b.ne    Lfmt
Lhyphen:
        mov     w8, #45                     // '-'
        strb    w8, [x5], #1
        b       Lfmt
Lfmt_done:
        mov     w8, #10                     // '\n'
        strb    w8, [x5], #1
        add     x23, x23, #1
        cmp     x23, x22
        b.lo    Lid

        mov     x0, #1                      // fd 1
        add     x1, sp, #256
        mov     x2, #69                     // (68 chars + newline) * batch
        mul     x2, x2, x22
        bl      _write
        sub     x19, x19, x22
        b       Lloop

Lfail:
        mov     w0, #1
        b       Lexit
Ldone:
        mov     w0, #0
Lexit:
        add     sp, sp, #816
        ldr     x23, [sp], #16
        ldp     x21, x22, [sp], #16
        ldp     x19, x20, [sp], #16
        ldp     x29, x30, [sp], #16
        ret

        .section __TEXT,__const
hexdigits:
        .ascii  "0123456789abcdef"
