PIC Microcontoller Math Method

Random number generator for the 16F876 using timer 0

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: