Olin Lathrop says:
I needed a random number on at 16F876 once. I used some sort of bit smashing algorithm, but also used timer 0 as input to randomize the output even more.For what it's worth, I've attached the routine. This particular routine is more toward the higher quality but slower speed end of things.
; 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 dbtvoRf 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
Comments: