; ---------------------------------------------------------------------------
; FILE: MT19937.Asm
; DATE: December 20, 2011
; ---------------------------------------------------------------------------
UINT32 equ dd
DWORDS equ rd
; ---------------------------------------------------------------------------
virtual at 0
MT19937:
    .Index    UINT32   ?
    .Vector   DWORDS   624
    .size = $
end virtual

align PROC_ALIGN
MT19937_Init624:
; ---------------------------------------------------------------------------
; INPUT:
;   EBX = 'this' (points to instance of MT19937 structure)
; ---------------------------------------------------------------------------
    pusha
    ;
    ; Loop index (ECX) runs from 0 to 623
    ; ESI -> Vector of 624 DWORDs
    ;
    xor       ecx, ecx
    lea       esi, [ebx + MT19937.Vector]

.init_next:
    ;
    ; Y = (Vector[i] and 80000000h) or (Vector[i+1] and 7FFFFFFFh)
    ; NOTE: for i=623 (last DWORD) [i+1] will mean 1st DWORD
    ;
    mov       eax, [esi]
    cmp       ecx, 623
    je        .get_1st

    mov       edx, [esi + 4]

.calc_Y:
    and       eax, 80000000h
    and       edx, 7FFFFFFFh
    or        eax, edx
    ;
    ; Vector[i] = Vector[i+397] xor (Y >> 1)
    ; NOTE: [i+397] wraps around the vector
    ;
    lea       edx, [ecx + 397]
    cmp       edx, 623
    ja        .reduce_idx

.step2:
    mov       ebp, [ebx + edx*4 + MT19937.Vector]
    mov       edx, eax
    shr       edx, 1
    xor       ebp, edx
    ;
    ; if (Y is odd) then Vector[i] = Vector[i] xor 9908B0DFh
    ;
    test      eax, 1
    jz        .store_next

    xor       ebp, 9908B0DFh

.store_next:
    mov       [esi], ebp
    add       esi, 4
    add       ecx, 1
    cmp       ecx, 623
    jbe       .init_next

    popa
    ret

.reduce_idx:
    sub       edx, 624
    jmp       .step2

.get_1st:
    mov       edx, [ebx + MT19937.Vector]
    jmp       .calc_Y

align PROC_ALIGN
MT19937_GetUInt32:
; ---------------------------------------------------------------------------
; INPUT:
;   EBX = 'this' (points to instance of MT19937 structure)
; OUTPUT:
;   EAX = next unsigned 32-bit value from MT sequence
; ---------------------------------------------------------------------------
    push      ecx edx
    ;
    ; Regenerate as needed
    ;
    mov       ecx, [ebx + MT19937.Index]
    jecxz     .reset

.extract:
    mov       eax, [ebx + ecx*4 + MT19937.Vector]

    mov       edx, eax
    shr       edx, 11
    xor       eax, edx

    mov       edx, eax
    shl       edx, 7
    and       edx, 9D2C5680h
    xor       eax, edx

    mov       edx, eax
    shl       edx, 15
    and       edx, 0EFC60000h
    xor       eax, edx

    mov       edx, eax
    shr       edx, 18
    xor       eax, edx

    add       ecx, 1
    cmp       ecx, 624
    je        .clear_idx

@@:
    mov       [ebx + MT19937.Index], ecx
    pop       edx ecx
    ret

.clear_idx:
    xor       ecx, ecx
    jmp       @r

.reset:
    call      MT19937_Init624
    jmp       .extract

align PROC_ALIGN
MT19937_New:
; ---------------------------------------------------------------------------
; OUTPUT:
;   EBX = 'this' (points to new instance of MT19937 structure)
; ---------------------------------------------------------------------------
    pusha
    invoke    GetProcessHeap
    invoke    HeapAlloc, eax, HEAP_NO_SERIALIZE, MT19937.size
    mov       ebx, eax

    invoke    GetTickCount
    mov       edi, eax

    rdtsc
    xor       eax, edx
    xor       eax, edi
    call      MT19937_Seed

    xor       eax, eax
    mov       [ebx + MT19937.Index], eax
    mov       [esp + 16], ebx

    popa
    ret

align PROC_ALIGN
MT19937_Delete:
; ---------------------------------------------------------------------------
; INPUT:
;   EBX = 'this' (points to instance of MT19937 structure)
; ---------------------------------------------------------------------------
    pusha
    invoke    GetProcessHeap
    invoke    HeapFree, eax, HEAP_NO_SERIALIZE, ebx
    popa
    ret

align PROC_ALIGN
MT19937_RandomFloat:
; ---------------------------------------------------------------------------
; INPUT:
;   EBX = 'this' (points to instance of MT19937 structure)
; OUTPUT:
;   ST0 = random value in range ]0.0 - 1.0[
; ---------------------------------------------------------------------------
    push      eax
    call      MT19937_GetUInt32

    push      0 eax
    fild      qword [esp]

    push      1 0
    fild      qword [esp]

    add       esp, 16
    fdivp

    pop       eax
    ret

align PROC_ALIGN
MT19937_Seed:
; ---------------------------------------------------------------------------
; INPUT:
;   EBX = 'this' (points to instance of MT19937 structure)
;   EAX = seed value to start RNG
; ---------------------------------------------------------------------------
    pusha
    ;
    ; Write seed value into 1st cell of the vector
    ;
    lea       edi, [ebx + MT19937.Vector]
    stosd
    ;
    ; Each next cell is set by following algorithm:
    ;
    ; for (i=1 to 623)
    ; {
    ;   PREV = Vector[i-1]
    ;   Vector[i] = i + 6C078965h * (PREV xor (PREV shr 30))
    ; }
    ;
    xor       edx, edx          ; i
    mov       ecx, 6C078965h
    mov       ebx, 623

.apply_seed:
    inc       edx
    mov       eax, [edi - 4]    ; PREV

    shr       eax, 30
    xor       eax, [edi - 4]

    push      edx
    mul       ecx
    pop       edx
    add       eax, edx
    stosd

    cmp       edx, ebx
    jb        .apply_seed

    popa
    ret







