'===========================================================================
' Subject: RETURN A PSUEDO-RANDOM INTEGER     Date: 01-28-97 (19:59)       
'  Author: Brian Mclaughlin                   Code: ASM                    
'  Origin: comp.lang.basic.misc             Packet: ASMCODE.ABC
'===========================================================================
; RAND.ASM   - assemble with ASM 5.1 or compatible assembler
;            - for use with QB4.x only
;            - written by Brian McLaughlin. released to public domain.
;
; The Rand% FUNCTION is designed to return a psuedo-random integer between
; two integers (inclusive). Rand%(1, 10) may return: 1, 2, 3 ... 8, 9, 10. 
; Rand% detects which integer is greater, and swaps the two if necessary.
;
; The SeedRand SUB sets a new seed for the Rand% FUNCTION, from the timer.
; It is highly recommended to call SeedRand once before calling Rand%. If
; you don't, Rand's return series won't differ from one start-up to the next.
;
; Declare them:  DECLARE SUB SeedRand ()
;                DECLARE FUNCTION Rand% (Lower%, Upper%)
;
; Use them:      SeedRand     'seeds Rand% FUNCTION
;                RandValue% = Rand%(Lower%, Upper%)
;
; This function is MUCH faster than calling RND and then converting
; the floating point return value to an integer. The algorithm should
; produce adequately "random" distributions for most general purposes.


.MODEL MEDIUM, BASIC
.CODE
    RandSeed    DW 2663     ; supply a seed, in case user forgets to
    Multiplier  DW 1021     ; these two are important constants:
    Divisor     DW 32768    ; don't change them!

SeedRand PROC FAR USES ES
    Xor AX, AX
    Mov ES, AX         ; point ES at low memory
    Mov AX, ES:[046Ch] ; get low word of timer ticks count
    Mov RandSeed, AX   ; put it into RandSeed
    Ret
SeedRand EndP

Rand PROC FAR, Lower:WORD, Upper:WORD
    Mov AX, RandSeed   ; RandSeed into AX
    Mul Multiplier     ; multiply it by Multiplier, result in DX:AX
    Inc AX             ; add 1 to the result
    Div Divisor        ; divide result in DX:AX by Divisor, remainder in DX
    Mov RandSeed, DX   ; save remainder, to seed the next call to Rand
    Mov AX, DX         ; put remainder into AX
    Xor DX, DX         ; DX:AX now equals remainder as a long integer
    Mov BX, Upper      ; the address of Upper
    Mov CX, [BX]       ; CX = value of Upper limit
    Mov BX, Lower      ; address of Lower
    Mov BX, [BX]       ; BX = value of Lower
    Cmp BX, CX         ; are they in correct order?
    Jg  SwapEm         ; if Lower > Upper, go swap them
Resume:
    Inc CX             ; this allows remainders up to Upper limit
    Sub CX, BX         ; CX = range from Lower to Upper
    Div CX             ; divides DX:AX by CX, puts remainder into DX
    Mov AX, BX         ; AX = lower limit, DX is our random number
    Add AX, DX         ; AX = random number between Lower and Upper
    Ret                ; return AX to BASIC program
SwapEm:
    Xchg BX, CX    
    Jmp SHORT Resume
Rand ENDP
     END
