// RANDOM      Random Number Generator

// Function RANDOM(LOW,HIGH) returns a pseudorandom
// integer between LOW and HIGH. The random integer is
// generated by a multiplier/adder method, with "MOD" used
// to bring the integer into the range [LOW --> HIGH].
// Seed is initialized using the system time.
        MULT    = 3141592653589793221 // Multiplier
        TEN6    = 1000000         // 1,000,000 (one million)
        .global booth, uint64_rem_min_lat, gettimeofday
        .data                     // Declare storage
        .align  8                 // Desired alignment
SEED:   data8   0                 // Seed for algorithm
TIME:                             // For gettimeofday
SECS:   data8   0                 //  Seconds
USEC:   data8   0                 //  Microseconds
VOID:   data4   0                 // (extra time zone)
        data4   0                 // (info not needed)
        .text                     // Section for code
        .align  32                // Desired alignment
        .global random,random_    // These three lines
        .proc   random,random_    //  mark the mandatory
random:                           //   function entry
random_:                          // FORTRAN needs underscore
        .prologue 12,r32          // Mask for rp, ar.pfs only
        alloc   loc1 = ar.pfs,2,5,2,0  // ins, locals, outs
        .save   rp,loc0           // Must save return address
        mov     loc0 = b0         //  to our caller
        .body                     // Now we really begin...
first:  add     loc2 = @gprel(SEED),gp;;  // loc2 -> SEED
        ld8     loc3 = [loc2];;   // loc3 = SEED
        mov     loc4 = gp         // Save gp
        cmp.ne  p6,p0 = 0,loc3    // First time called?
   (p6) br.cond.sptk.many gen     //  Not usually!
        add     out0 = @gprel(TIME),gp  // out0 -> TIME
        add     out1 = @gprel(VOID),gp  // out1 -> VOID
        br.call.sptk.many b0 = gettimeofday  // System time
        mov     gp = loc4;;       // Restore gp
        add     r2 = @gprel(SECS),gp;;  // r2 -> SECS
        ld8     out0 = [r2]       // SECS is multiplicand
        movl    out1 = TEN6       // TEN6 is multiplier
        mov     loc4 = r1         // Save gp
        br.call.sptk.many b0 = booth  // r9,r8 = out0 * out1
        mov     gp = loc4;;       // Restore gp
        add     r2 = @gprel(USEC),gp;;  // r2 -> USEC
        ld8     loc3 = [r2];;     // USEC
        add     loc3 = loc3,r8;;  // Total microseconds
gen:    mov     out0 = loc3       // SEED is multiplicand
        movl    out1 = MULT       // MULT is multiplier
        mov     loc4 = gp         // Save gp
        br.call.sptk.many b0 = booth  // r9,r8 = out0 * out1
        mov     gp = loc4         // Restore gp
        add     r8 = 1,r8;;       // Product + 1 modulo 2^64
        st8     [loc2] = r8       // Store as next seed
        mov     out0 = r8         // RND
        sub     out1 = in1,in0;;  // HIGH - LOW
        add     out1 = 1,out1     // RANGE
        mov     loc4 = gp         // Save gp
        br.call.sptk.many b0 = uint64_rem_min_lat  // Modulo
        mov     gp = loc4         // Restore gp
        add     r8 = r8,in0       // LOW + (RND mod RANGE)
done:   mov     b0 = loc0         // Restore return address
        mov     ar.pfs = loc1     // Restore caller's ar.pfs
        br.ret.sptk.many b0;;     // Back to caller
        .endp   random            // Mark end of procedure
        .include "uint64_rem_min_lat.s"