The terrible random number generation in the Commodore 64 (and 128)
By Michael Doornbos
- 4 minutes read - 641 wordsQuite a while ago, I started playing with random numbers on 8 Bit machines. I don’t think anyone is doing “serious” work on these machines, but playing with Ciphers and Crypto got me at least curious about how a Commodore 64 generates random numbers.
There are many ways to determine randomness. Humans are pretty good at picking out patterns in visual representations. Luckily our beloved machines have easy screen memory access. Let’s poke some random stuff to the screen and see what we see.
10 REM CHECK HOW RANDOM RND VALUES ARE
20 C=0:REM TEST 0,.,1
30 S=1024 :REM SCREEN MEMORY START
40 PRINTCHR$(147)
50 FORJ=0 TO 999
60 R=INT(RND(C)*1000)
70 N=PEEK(S+R):IFN=32THENN=47
80 POKE S+R,N+1:NEXT
90 REM POKE S+R,48:NEXT
100 GETK$:IFK$="" GOTO100
110 PRINTCHR$(147)
We’ll do some variation of this over and over.
Oh, that isn’t pleasant
Very little randomness here. Patterns are bad…
Maybe they fixed it on the 128?
Uhhh
But it doesn’t happen on many other machines
I tried the PET first since it was on my desk.
The PET looks MUCH more random.
Even the Plus/4 is okayish.
VIC-20 does fine
Is this a Commodore thing?
I don’t have a capture device for this one, and CRTs and iPhone cameras don’t play nice. I think the shot is good enough for you to get the idea though Nope. The Apple IIe (Applesoft BASIC) seems okay.
It gets worse
If we get a random float and then get a bunch more, we get the same nine-digit number several times in just 100k samples. Bleh.
A little review
There are three ways to call RND
from BASIC on a Commodore.
RND(1)
Pick the next number out of the LFSR running sequence. The seed always starts at .185564016
RND(0)
uses the VIA(PET and VIC-20)/CIA(64 and 128) timer and gives a much better random value. This is a problem on the C64 (and 128).
RND(negative number)
the random number algorithm is reseeded with whatever the argument is.
So what gives
I scratched my head about this for quite some time. I consulted what I consider to be the Commodore 64 Bible. There are books by the same author for the PET and VIC-20, and it says almost the same thing for them.
Programming the PET Raeto Collin West p. 448
Programming the VIC Raeto Collin West p.53
Programming the C64 Raeto Collin West p.56
According to this source, it appears that RND(0) should get randomness from the CIA timer and give a decent pseudo random sequence. We can see from testing that it does not. So what’s going on here?
Old books to the rescue
Another of the best books ever created for the C64: Mapping the C64 (there is the same book for the VIC )
In it, we have the answer. It’s broken. Very broken.
P 207 of Compute’s Mapping the C64 by Sheldon Leemon
The next page
So the short-short version is: the CIA timer isn’t running on startup, and starting it doesn’t really help because the C64/128 uses BCD for this register. This limits the number of possible numbers out of the random number generator severely. So severely that we got the same nine-digit precision decimal number 5 times out of 100k samples.
Try this
You won’t EVER get it to return the number 1.
But it’ll give you zero lots of times
What to do?
The easiest thing is to do what people have been doing since the BASIC version 1 in the PET. You can get a reasonably random seed by using TI. Early PETs also had a bug in RND, so it became common to reseed the RND(1) and use it. So something like this works pretty well:
No repeats in 1 million tries. Much better.
Is it random? Not REALLY. Is it good enough for what you’re doing on a Commodore 64? Probably.