GS WorldView: November MIM
...   back to GSWV main contents page
 
 

Apple II Random Number Generator

by David Empson
 
 

There was a series of articles published many years ago, which described
a cyclic shift/XOR-based random number generator.  It was published in
The Sourceror's Apprentice, Volume 1 Number 4 (April 1989).  The article
was called "Random Bytes", and was written by Robert C Moore of the Applied
Physics Laboratory at the John Hopkins University in Laurel, Maryland.

The method generates 8-bit random numbers from a 32-bit seed variable, and
has a repeat period of 2^32-1 (4194962795).  The seed can be any value
except zero, and could be derived from memory locations $4E and $4F, which
are incremented continuously while the system is waiting for a keypress
(with the standard monitor routines, as used by GET and INPUT from BASIC).

I've used this algorithm on various other processors, and it seems to
work well, as long as it is seeded correctly.
 

The article covers two designs for shift and exclusive OR random number
generators.  I'll used the simpler one here, which has a shorter period
of 2^25-1 (33554431).

To use this routine you need a four bytes to hold a the random number
seed.  I'll use memory locations $3C1 through $3C4 for the random number
working variable.  The variable is stored in little-endian order (low
order byte first).  After each call to the RANDOM routine, the new
random 8-bit number can be taken from the first byte of the variable (at
$03CC).  If you need a random number larger than 8 bits, it is necessary
to call the RANDOM routine once for each 8-bit result.  Don't try to use
the values stored in the second, third or fourth byte of the variable.

Before this routine can be used, the random number variable must be
seeded from an appropriate source, with a 25-bit to 32-bit value which
can be anything except zero.  (You will always get the same sequence of
results if you use the same seed value each time.)

If you can only provide a shorter seed value (e.g. 16 bits), then it is
a good idea to "kick start" the random number generator by generating
several random numbers before you start to use the result (otherwise you
are likely to get several zero bytes for the first few "random"
numbers).
 

Here is my Apple II random number routine in source form:
 

R1       EQU $03CC
R2       EQU $03CD
R3       EQU $03CE
R4       EQU $03CF

RANDOM   ROR R4           ; Bit 25 to carry
         LDA R3           ; Shift left 8 bits
         STA R4
         LDA R2
         STA R3
         LDA R1
         STA R2
         LDA R4           ; Get original bits 17-24
         ROR              ; Now bits 18-25 in ACC
         ROL R1           ; R1 holds bits 1-7
         EOR R1           ; Seven bits at once
         ROR R4           ; Shift right by one bit
         ROR R3
         ROR R2
         ROR
         STA R1
         RTS

Here is a routine to seed the random number generator with a
reasonable initial value:

INITRAND LDA $4E          ; Seed the random number generator
         STA R1           ; based on delay between keypresses
         STA R3
         LDA $4F
         STA R2
         STA R4
         LDX #$20         ; Generate a few random numbers
INITLOOP JSR RANDOM       ; to kick things off
         DEX
         BNE INITLOOP
         RTS

Now to wrap this in a "fill the hi-res graphics screen" routine:

NOISEGEN JSR INITRAND     ; Seed the random number generator
         LDA #$00
         STA $3C          ; Borrow monitor variables for a pointer
         LDA #$20
         STA $3D
         LDY #$00         ; Initialize the offset
         LDX #$20         ; and the page count
FILLLOOP JSR RANDOM
         STA ($3C),Y
         INY
         BNE FILLLOOP
         INC $3D
         DEX
         BNE FILLLOOP
         RTS

Converting this to machine code, I'll put the NOISEGEN routine
at $0300, the INITRAND routine at $0320, and the RANDOM routine
at $0340.  Here are the monitor commands necessary to enter this
program into memory:
 

0300:20 20 03 A9 00 85 3C A9 20 85 3D A0 00 A2 20 20
0310:40 03 91 3C C8 D0 F8 E6 3D CA D0 F3 60 EA EA EA

0320:A5 4E 8D C1 03 8D C3 03 A5 4F 8D C2 03 8D C4 03
0330:A2 20 20 40 03 CA D0 FA 60 EA EA EA EA EA EA EA

0340:6E C4 03 AD C3 03 8D C4 03 AD C2 03 8D C3 03 AD
0350:C1 03 8D C2 03 AD C4 03 6A 2E C1 03 4D C1 03 6E
0360:C4 03 6E C3 03 6E C2 03 6A 8D C1 03 60
 

If you want to save the whole thing, use:

BSAVE NOISEGEN,A$300,L$6D
 

Note: This routine doesn't have any connection to the Applesoft
      BASIC RND() function.
 

I've tried it out on my IIgs, and it seems to work fine.  With the
system set to Normal speed (1 MHz), it takes about 1 second to fill the
screen, which is somewhat slower than the clear screen routine, but not
enough to worry about.  It looks pretty random to me.

Note that if you want to do this again (getting a different pattern),
you should avoid calling INITRAND more than once.

You can use CALL 800 to call the INITRAND routine directly, then use
CALL 771 for each random fill.

If you only need to use it once, just CALL 768.
 
 

to top