The Challenge of Randomness

CPUs are deterministic: they do the same thing every time. To generate a truly unpredictable sequence, we need a source of entropy (unpredictable noise) from the outside world.

Method 1: Hardware Noise (Simple Seed)

The fastest way to get a simple random number is to read a register or port whose value is constantly changing due to external factors.

Noise Sources:

  1. I/O Port Reading: Reading from an unused or floating I/O port can return unpredictable bits of electrical noise.
  2. The Refresh Register (R): As discussed earlier, the R register increments automatically, making its low bits somewhat unpredictable depending on when it’s read.
  3. The Keyboard: Polling the keyboard port and using the lower bits of the input byte before any key is pressed.

Example: Reading an Unpredictable Port

RANDOM_PORT EQU 0FFH        ; Example: An unused I/O port address
    
GET_SEED:
    IN   A, (RANDOM_PORT)   ; Read the unpredictable noise into A
    AND  3FH                ; Mask off the high bits to get a smaller range (0-63)
    LD   RANDOM_SEED, A     ; Store A as our initial 'seed'
    RET

Method 2: The Linear Congruential Generator (LCG)

Once you have a starting point (the seed), you can use an algorithm like the Linear Congruential Generator (LCG) to produce a long sequence of numbers that appear random.

The LCG Formula: $$\text{Next Value} = (\text{Current Value} \times A + C) \bmod M$$ Where:

  • $A$ = Multiplier (A carefully chosen constant, e.g., $101$).
  • $C$ = Increment (A constant, e.g., $1$).
  • $M$ = Modulus (Determines the sequence length, e.g., $256$).

Z80 Implementation Focus: The Z80 code focuses on performing the 8-bit multiplication and addition, using the Carry Flag (ADC) and bitwise masking (AND) to manage the result within the 8-bit or 16-bit boundaries.

Advantage: LCGs are fast, repeatable (good for debugging), and produce sequences long enough for most 8-bit games.

Practical Usage (Limiting the Range)

Once you have a random byte in the Accumulator (A), you usually need to limit it to a small range (e.g., 0 to 5) for game logic.

Limiting Range: To get a random number between 0 and N:

  1. Perform the LCG generation.
  2. Divide the result by a constant M and use the remainder (the modulo operation).
  3. In Z80, the simplest way is often repeated subtraction or repeated shifting and masking.