Skip to content

LFSR Random Numbers

Linear Feedback Shift Register for fast, deterministic pseudo-random numbers.

randomlfsrprngmath

Overview

A Linear Feedback Shift Register (LFSR) generates pseudo-random numbers by shifting bits and XORing feedback taps. It’s fast (no multiplication), deterministic (same seed = same sequence), and produces a long cycle before repeating.

The 8-bit Galois LFSR shown here cycles through all 255 non-zero values before repeating — perfect for games.

Algorithm

; 8-bit Galois LFSR with taps at bits 7, 5, 4, 3
; Polynomial: x^8 + x^6 + x^5 + x^4 + 1

random_seed: byte = $01    ; Must be non-zero!

get_random:
    load random_seed
    shift right            ; Carry = bit 0
    if carry set:
        xor #$B4           ; Feedback polynomial
    store random_seed
    return                 ; Result in accumulator

Pseudocode

; Initialise with any non-zero value
random_seed = $01

; Get next random number (0-255, never 0)
get_random:
    seed = random_seed
    carry = seed AND 1
    seed = seed >> 1
    if carry:
        seed = seed XOR $B4
    random_seed = seed
    return seed

; Get random in range 0 to max-1 (for powers of 2)
get_random_max(max):
    return get_random AND (max - 1)

; Example: random 0-15
direction = get_random AND $0F

Implementation Notes

6502:

get_random:
    lda random_seed
    lsr              ; Shift right, bit 0 -> carry
    bcc +            ; Skip XOR if carry clear
    eor #$B4         ; Apply feedback
+   sta random_seed
    rts

Z80:

get_random:
    ld a,(random_seed)
    srl a            ; Shift right, bit 0 -> carry
    jr nc,.no_xor
    xor $B4
.no_xor:
    ld (random_seed),a
    ret

68000:

get_random:
    move.b  random_seed,d0
    lsr.b   #1,d0
    bcc.s   .no_xor
    eor.b   #$B4,d0
.no_xor:
    move.b  d0,random_seed
    rts

Trade-offs

AspectCost
CPU~10-20 cycles
Memory1 byte for seed
Period255 values before repeat
DistributionUniform across 1-255 (never generates 0)

When to use: Enemy spawn positions, random delays, shuffling, procedural generation.

When to avoid: Cryptography (this is not secure!), or when you need 0 in your range.

Seeding

For variety between play sessions, seed from player input timing:

; Count frames until player presses start
seed_counter = 0
wait_for_start:
    seed_counter = seed_counter + 1
    if not start_pressed: goto wait_for_start
    random_seed = seed_counter
    if random_seed == 0: random_seed = 1  ; Never zero!

Larger Periods

For 16-bit LFSR (65,535 values):

  • Use polynomial $B400 (taps at 15, 13, 12, 10)
  • Same algorithm, just with 16-bit operations