Seeding the RNG

I've looked through the code of a lot of old BASIC programs, and they all seem to have one omission: the lack of an RNG seeding routine. When you first turn on an 8-bit computer, the RNG is always initialized to the same seed every time. If you don't reseed it, the random numbers will be the same every time you start a program after booting. I'm going to show you some common methods to seed the RNG.

The first, and easiest, method is to ask the user to input a number. This technique should work on any version of BASIC you can think of. Here's the randomization routine from the Altair 8800 version of the Battle Test:

1 REM RANDOMIZATION ROUTINE
2 PRINT "TYPE A RANDOM NUMBER:"
3 INPUT Z
4 Y=RND(-Z)

As you can see, it asks the user to type a random number, it inputs that number to a variable, and it uses that variable to seed the RNG by passing the negative of the number to the RND function. The way RND usually works is that RND(1) generates a new random number, RND(0) returns the last random number generated, and RND(-<number>) seeds the RNG with the number provided.

TRS-80 Level 1 BASIC uses a variant on this technique. Instead of seeding the RNG with the user provided number, it calls the RND function in a loop to move the number down the list of generated numbers.

1 REM RANDOMIZATION ROUTINE
2 INPUT "ENTER A NUMBER BETWEEN 1 AND 100:";Z
3 FOR Y=1 TO Z
4 A=RND(32767)
5 NEXT Y

The best way by far to seed the RNG is by using timing. In its simplest form, it uses a loop to increment the number that will be passed to the RND function. Here's an example from the CP/M BASIC-80 v5 version:

1 REM RANDOMIZATION ROUTINE
2 PRINT "PRESS A KEY"
3 Z=0
4 Z=Z+1
5 A$=INKEY$
6 IF A$="" THEN 4
7 Y=RND(-Z)

It sets a variable to zero, and then it enters a loop that increments the number by one each time, until the user presses a key. Some versions of BASIC, such as CBM BASIC, have a dedicated timer variable that can be referenced, obviating the need to increment:

1 REM RANDOMIZATION ROUTINE
2 PRINT "PRESS A KEY:"
3 GET A$
4 IF A$="" THEN 3
5 Z=RND(-TI)

With CBM BASIC, the timer variable is called TI. It results in the code being a little bit shorter. There are other versions of BASIC that have a system timer that isn't referenced by a variable. In those cases, you need to PEEK a pair of memory locations, and then turn those into a combined number like so:

1 REM RANDOMIZATION ROUTINE
2 PRINT "80-COLUMNS?"
3 GET A$
4 Z=RND(-1*(PEEK(78)+256*PEEK(79)))

The formula shown on line 4 can be used to turn any two 8-bit numbers into one 16-bit number.

Finally, there are some versions of BASIC that have a built-in command for randomization. For instance, TRS-80 Level 2 BASIC uses RANDOM to seed the RNG from the system timer. GW-BASIC uses RANDOMIZE TIMER to do the same thing. It's still good practice to wait on user input before seeding the RNG, as many emulators have an auto-start feature that might result in a program being loaded in such a way as to have the system timer be set identically every time you run the program.

There is one major exception to seeding the RNG, and that's with the Atari 8-bit computers. The RNG is automatically seeded on those from noise generated by the POKEY chip, and thus there is no need to seed the RNG on them, ever.

Comments

Popular posts from this blog

CoCo 3 NitrOS-9 Emulator VHD Install Disks

Overclocking the Raspberry Pi 4 for Fun and Games

Mac Emulator Videos