// uuid256.i386.s — UUID256 (README.md, random layout) in 32-bit x86 assembly for Linux (AT&T syntax, raw syscalls, no libc).
//
//   Build:  zig cc -target x86-linux-musl -nostdlib -static uuid256.i386.s -o /tmp/uuid256-i386     (or: as --32 + ld -m elf_i386 on Linux)
//   Run:    /tmp/uuid256-i386 5                on any 32-bit-capable x86 Linux
//           python3 emulate.py /tmp/uuid256-i386 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 355)
//   §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:  esi = ids remaining · edi = mode (0 random, 1 stdin) · ebp = frame base
//                0(%ebp)..31 = the 32-byte id · 32(%ebp)..100 = the 68-char text + '\n'
// i386 syscall ABI: eax = number, ebx/ecx/edx = args, int $0x80, result in eax.

        .text
        .globl  _start
_start:                                         // [esp] = argc, 4(%esp) = argv[0], 8(%esp) = argv[1]
        movl    (%esp), %eax                    // argc
        leal    4(%esp), %ecx                   // argv
        subl    $128, %esp
        movl    %esp, %ebp                      // frame: id at 0(%ebp), text at 32(%ebp)
        movl    $10, %esi                       // N default
        xorl    %edi, %edi                      // mode: random
        cmpl    $2, %eax                        // argc < 2 → defaults
        jl      Lloop
        movl    4(%ecx), %ebx                   // argv[1]
        cmpb    $45, (%ebx)                     // '-' → test mode
        jne     Latoi
        cmpb    $0, 1(%ebx)
        jne     Latoi
        movl    $1, %edi
        movl    $1, %esi
        jmp     Lloop
Latoi:                                          // N = decimal(argv[1]); 0 → print nothing; non-numeric → exit 2
        xorl    %esi, %esi
Latoi_next:
        movzbl  (%ebx), %eax
        testl   %eax, %eax
        jz      Latoi_done
        subl    $48, %eax                       // '0'
        cmpl    $9, %eax
        ja      Latoi_bad
        imull   $10, %esi, %esi
        addl    %eax, %esi
        incl    %ebx
        jmp     Latoi_next
Latoi_bad:
        movl    $2, %ebx                        // non-numeric argument → usage error, exit 2
        jmp     Lexit
Latoi_done:                                     // (N = 0 prints nothing and exits 0)

Lloop:
        testl   %esi, %esi
        jz      Ldone

        // ---- 32 bytes: §5.1 getrandom, or stdin in test mode --------------------------
        testl   %edi, %edi
        jnz     Lstdin
        movl    $355, %eax                      // SYS_getrandom(buf, len, flags)
        movl    %ebp, %ebx
        movl    $32, %ecx
        xorl    %edx, %edx
        int     $0x80
        cmpl    $32, %eax
        jne     Lfail
        jmp     Lhave
Lstdin:
        movl    $0, 104(%ebp)                   // bytes read so far (frame slot); loop until all 32 arrive
Lread_more:                                     //   (a pipe may deliver them in pieces)
        movl    $3, %eax                        // SYS_read(0, buf + got, 32 - got)
        xorl    %ebx, %ebx
        movl    %ebp, %ecx
        addl    104(%ebp), %ecx
        movl    $32, %edx
        subl    104(%ebp), %edx
        int     $0x80
        testl   %eax, %eax
        jle     Lfail                           // EOF or error before 32 bytes
        addl    %eax, 104(%ebp)
        cmpl    $32, 104(%ebp)
        jl      Lread_more
Lhave:
        // ---- §4: version nibble (byte 12) and variant bits (byte 16) ------------------
        andb    $0x0F, 12(%ebp)
        orb     $0x40, 12(%ebp)                 // ver = 4  → hex digit 24 is '4'
        andb    $0x3F, 16(%ebp)
        orb     $0x80, 16(%ebp)                 // var = 10 → hex digit 32 in 8..b

        // ---- §3.1: canonical text 16-8-8-8-24 -----------------------------------------
        leal    32(%ebp), %ebx                  // output cursor
        xorl    %ecx, %ecx                      // byte index i
Lfmt:
        movzbl  (%ebp,%ecx), %eax
        movl    %eax, %edx
        shrl    $4, %eax
        movb    hexdigits(%eax), %al            // high nibble → hex char (absolute address: static ELF)
        movb    %al, (%ebx)
        andl    $0x0F, %edx
        movb    hexdigits(%edx), %dl            // low nibble  → hex char
        movb    %dl, 1(%ebx)
        addl    $2, %ebx
        incl    %ecx
        cmpl    $32, %ecx
        je      Lfmt_done
        cmpl    $8, %ecx                        // hyphen after bytes 8, 12, 16, 20
        je      Lhyphen                         //   (= hex digits 16, 24, 32, 40)
        cmpl    $12, %ecx
        je      Lhyphen
        cmpl    $16, %ecx
        je      Lhyphen
        cmpl    $20, %ecx
        jne     Lfmt
Lhyphen:
        movb    $45, (%ebx)                     // '-'
        incl    %ebx
        jmp     Lfmt
Lfmt_done:
        movb    $10, (%ebx)                     // '\n'
        movl    $4, %eax                        // SYS_write(1, text, 69)
        movl    $1, %ebx
        leal    32(%ebp), %ecx
        movl    $69, %edx
        int     $0x80
        decl    %esi
        jmp     Lloop

Lfail:
        movl    $1, %ebx                        // exit status 1
        jmp     Lexit
Ldone:
        xorl    %ebx, %ebx                      // exit status 0
Lexit:
        movl    $1, %eax                        // SYS_exit
        int     $0x80

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