; ***************************************************************
; * Copyright (c) 2001, Embed Inc (http://www.embedinc.com) *
; * *
; * Permission to copy this file is granted as long as this *
; * copyright notice is included in its entirety at the *
; * beginning of the file, whether the file is copied in whole *
; * or in part and regardless of whether other information is *
; * added to the copy. *
; * *
; * The contents of this file may be used in any way, *
; * commercial or otherwise. This file is provided "as is", *
; * and Embed Inc makes no claims of suitability for a *
; * particular purpose nor assumes any liability resulting from *
; * its use. *
; ***************************************************************
;
; Random number generator.
;
include "hal.inc"
extern lookofs ;lookup at REG2,REG1 with offset REG3, REG3 incremented
;
;***********************************************************************
;
; Configuration constants.
;
lbank equ 0 ;register bank for the local state of this module
size1 equ 17 ;number of entries in RAND1 table
size2 equ 19 ;number of entries in RAND2 table
;
; Derived constants.
;
lbankadr equ bankadr(lbank) ;address within local state register bank
;
;***********************************************************************
;
; Global state. All this state is assumed to be in the GBANK register
; bank by other modules.
;
.bank#v(gbank) udata
;
;***********************************************************************
;
; Local state.
;
if lbank != gbank
.bank#v(lbank) udata
endif
ind1 res 1 ;0 to SIZE1-1 index into RAND1 table
ind2 res 1 ;0 to SIZE2-1 index into RAND2 table
hash res 1 ;hash value for making random number
.rand code
;
;***********************************************************************
;
; Subroutine RAND_INIT
;
; Initialize the hardware and software state managed by this module.
;
glbsub rand_init, noregs
;
; Set up timer 0 to free run from the instruction clock.
;
dbankif option_reg
movf option_reg, w ;get current OPTION register value
andlw b'11000000' ;mask off all the timer 0 control bits
iorlw b'00001000'
; 00------ not timer 0 control bits, leave alone by ORing with 0
; --0----- timer 0 source is instruction clock
; ---X---- external timer 0 source edge select
; ----1--- assign prescaler to watchdog timer, not timer 0
; -----XXX prescaler setting
movwf option_reg
;
; Initialize the local state managed by this module.
;
dbankif tmr0
movf tmr0, w ;get current timer 0 value into W
dbankif lbankadr
movwf hash ;init hash mask
clrf ind1 ;init RAND1 table index
clrf ind2 ;init RAND2 table index
leaverest
;
;***********************************************************************
;
; Subroutine RAND
;
; Return a random byte value in REG0.
;
glbsub rand, regf1 | regf2 | regf3
;
; Update HASH with the random number from table 1.
;
movlw low rand1 ;pass table start address
movwf reg1
movlw high rand1
movwf reg2
dbankif lbankadr
movf ind1, w ;pass offset to look up entry at
movwf reg3
gcallr lookofs, reg0 ;get random number from table 1 into REG0
movf reg0, w ;get the random value into W
dbankif lbankadr
xorwf hash ;update HASH with this random number
;
; Update HASH with the random number from table 2.
;
movlw low rand2 ;pass table start address
movwf reg1
movlw high rand2
movwf reg2
dbankif lbankadr
movf ind2, w ;pass offset to look up entry at
movwf reg3
gcallr lookofs, reg0 ;get random number from table 1 into REG0
movf reg0, w ;get the random value into W
dbankif lbankadr
addwf hash ;update HASH with this random number
;
; Update HASH with the current timer 0 value.
;
dbankif tmr0
movf tmr0, w ;get timer 0 value
addwf hash ;update HASH with the current timer 0 value
;
; Update the random number table indicies.
;
dbankif lbankadr
incf ind1 ;increment table 1 index
movf ind1, w ;get the new index value
sublw size1-1 ;compare to last valid index
skip_wle ;new index is still within table ?
clrf ind1 ;no, wrap back to start of table
incf ind2 ;increment table 2 index
movf ind2, w ;get the new index value
sublw size2-1 ;compare to last valid index
skip_wle ;new index is still within table ?
clrf ind2 ;no, wrap back to start of table
;
; Pass back the final value.
;
dbankif lbankadr
movf hash, w ;get the updated hash value
movwf reg0 ;pass it back as the random number
leaverest
;
;***********************************************************************
;
; Tables RAND1 and RAND2
;
; These tables contain random byte values. The length of each of the
; tables is SIZE1 and SIZE2, respectively.
;
rand1
retlw h'BD'
retlw h'66'
retlw h'C6'
retlw h'9A'
retlw h'79'
retlw h'E1'
retlw h'50'
retlw h'1A'
retlw h'59'
retlw h'A1'
retlw h'10'
retlw h'14'
retlw h'B0'
retlw h'FE'
retlw h'B1'
retlw h'C7'
retlw h'53'
rand2
retlw h'6D'
retlw h'82'
retlw h'2B'
retlw h'16'
retlw h'CE'
retlw h'90'
retlw h'9C'
retlw h'8A'
retlw h'A1'
retlw h'57'
retlw h'93'
retlw h'AF'
retlw h'26'
retlw h'C5'
retlw h'32'
retlw h'F0'
retlw h'45'
retlw h'97'
retlw h'99'
;
;***********************************************************************
;
; Subroutine RAND_MAX
;
; Returns a random number in REG0 from 0 thru REG1. For example, if REG1
; is 3, then REG0 will randomly be set to 0, 1, 2, or 3.
;
glbsub rand_max, regf2
;
; Make the smallest 2**N-1 mask that will still mask in the max valid
; random number. This mask is used to mask off high bits of the random
; value to reduce the number of rejections. The mask will be left in
; REG2.
;
clrf reg2 ;init to smallest possible mask (all bits off)
movf reg1, w ;get max valid random value
movwf reg0 ;save corruptable copy of max value
rmax_loop_mask ;back here each larger mask
movf reg0 ;set Z if all bits shifted out of max value
skip_nz ;all bits not yet shifted out, keep looping
goto rmax_mask ;minimum mask all set in REG2
bcf status, c ;shift max value one more bit right
rrf reg0
bsf status, c ;shift mask one more bit left
rlf reg2
goto rmax_loop_mask ;back to test REG0 value again
rmax_mask ;mask value in REG2 all set
;
; Get a random number that is no larger than REG1. REG2 is the smallest
; possible mask that masks in the entire range from 0 thru REG1. This mask
; will be used to eliminate the upper bits of candidate random numbers.
; This guarantees that at least half the masked random number will be
; within the desired range. Random numbers outside this range are discarded,
; and another random number is tried until one is found within the desired
; range.
;
rmax_loop ;back here until find valid random number
mcall rand ;get random byte value into REG0
movf reg2, w ;get mask for max valid value
andwf reg0 ;mask off bits that must be 0 anyway
movf reg0, w ;get the masked random number
subwf reg1, w ;compare to the max valid value
skip_wle ;this random value is within range ?
goto rmax_loop ;this random value is out of range, try another
leaverest
end