;name: uuid.asm
;
;description: Generates a UUID
;
;build: nasm -felf64 uuid.asm -o uuid.o
;       ld -melf_x86_64 uuid.o -o uuid
;
;remark: Because UUID need to be unique (that's why U stands for) the user need to
;        check each generated UUID against a table which contains already generated
;        UUID's. This algorithm applied 10000 gives 51 same UUIDs, 100000 times
;        gives 135 same UUIDs, 1000000 gives us 18884 same UUIDs. So check the presence
;        of an already generated uuid. Another possibility is the use of mysql uuid
;        but requires the installation of mysql server.

bits 64

%include "unistd.inc"

;IOV structure definition
struc IOV_STRUC
    .iov_base:  resq    1
    .iov_len:   resq    1
endstruc

;macro to use IOV in a less complex manner
%macro IOV 3
    %define %1.base     %1+IOV_STRUC.base
    %define %1.len      %1+IOV_STRUC.len

    %1: istruc IOV_STRUC
    at IOV_STRUC.iov_base, dq   %2
    at IOV_STRUC.iov_len, dq    %3
    iend
%endmacro

section .bss
    ;keep all groups together to create 32 values at once
    uuid:           
    .group1:        resb      8
    .group2:        resb      4
    .group3:        resb      4
    .group4:        resb      4
    .group5:        resb      12

section .data

iov:
    IOV s1,uuid.group1,8
    IOV s2,hyphen,1
    IOV s3,uuid.group2,4
    IOV s4,hyphen,1
    IOV s5,uuid.group3,4
    IOV s6,hyphen,1
    IOV s7,uuid.group4,4
    IOV s8,hyphen,1
    IOV s9,uuid.group5,12
    IOV s10,crlf,1

    crlf:           db      10
    hyphen:         db      "-"
    
section .text

global _start
_start:
    mov     rdi, uuid 
    call    generate
    ; convert to ascii
    mov     rsi, uuid
    mov     rdi, uuid
    mov     rcx, 32
nextDigit:      
    cld
    lodsb
    call    nibbletoasciihex
    stosb
    loop    nextDigit
    syscall writev,stdout,iov,10
    syscall exit, 0
    
generate:
;in  : rdi = pointer to buffer to store 32 bytes
;out : rdi = pointer to buffer with GUID
    mov	    rcx,32                  ;32 characters to generate
.repeat:      
    xor	    rax,rax                 ;lower boundary of interval
    mov     rdx,0xF                 ;higher boundary of interval
    call    GenerateRandom
    cld
    stosb
    loop    .repeat
    ret

nibbletoasciihex:
; in  :: AL = NIBBLE or 4 bits (least significant)
; out :: AL = Hexadecimal ASCII of NIBBLE   
    and     al,0x0F
    or      al,"0"
    cmp     al,"9"
    jbe     .done
    add     al,39
.done:
    ret

GenerateRandom:
;in rdi : lower boundary of interval
;   rsi : higher boundary of interval
;interval length is 0xF
    mov	    rbx,0xF
    rdtsc                               ;read cpu time stamp counter
    push    rax
    rdtsc                               ;read cpu time stamp counter
    mov     rax,rdx
    pop     rax     
    rol     rdx,32                      ;mov EDX in high 32 bits of RAX
    or      rax,rdx                     ;RAX = seed
    call    XorShift                    ;get a pseudo random number
    and     rax,0xF
    ret
    
XorShift:
    mov     rdx,rax                     ;XORSHIFT algorithm
    shl     rax,13
    xor     rax,rdx
    mov     rdx,rax
    shr     rax,17
    xor     rax,rdx
    mov     rdx,rax
    shl     rax,5
    xor     rax,rdx                     ;rax random 64 bit value
    ret