131 lines
2.4 KiB
Text
131 lines
2.4 KiB
Text
|
|
#IFNDEF __lib_random
|
|
#DEFINE __lib_random 1
|
|
|
|
GOTO __skip_lib_random
|
|
|
|
// LFSR Random Number Generator
|
|
// Combined 16-bit (period 65535) and 15-bit (period 32767) generators
|
|
// Total period: ~2.1 billion (65535 * 32767 = 2,147,385,345)
|
|
//
|
|
// Based on Hanno Behrens' implementation (LGPL)
|
|
// Adapted for c65 by using local variable storage
|
|
//
|
|
// Usage:
|
|
// BYTE rnd
|
|
// rand(rnd) // rnd now contains 0-255
|
|
//
|
|
// For ZP acceleration, change the variable declarations below
|
|
// from regular addresses to ZP addresses (e.g., @ $f6)
|
|
|
|
// Random state - initialized with default seeds
|
|
WORD rand_sr1 = $a55a
|
|
WORD rand_sr2 = $7653
|
|
|
|
// Initialize the random generator with default seeds
|
|
// Call once at startup (or just use rand_seed() with custom seed)
|
|
FUNC rand_init
|
|
rand_sr1 = $a55a
|
|
rand_sr2 = $7653
|
|
FEND
|
|
|
|
// Initialize with a custom seed value
|
|
// Good for seeding from user input timing
|
|
FUNC rand_seed({WORD seed})
|
|
rand_sr1 = seed ^ $a55a
|
|
rand_sr2 = seed ^ $7653
|
|
|
|
// Ensure neither seed is zero
|
|
IF rand_sr1 == 0
|
|
rand_sr1 = $a55a
|
|
ENDIF
|
|
IF rand_sr2 == 0
|
|
rand_sr2 = $7653
|
|
ENDIF
|
|
FEND
|
|
|
|
// Internal: 16-bit LFSR with period 65535
|
|
// Taps: 10, 12, 13, 15 (polynomial)
|
|
FUNC rand_lfsr64k
|
|
ASM
|
|
lda |rand_sr1|+1
|
|
asl
|
|
asl
|
|
eor |rand_sr1|+1
|
|
asl
|
|
eor |rand_sr1|+1
|
|
asl
|
|
asl
|
|
eor |rand_sr1|+1
|
|
asl
|
|
rol |rand_sr1|
|
|
rol |rand_sr1|+1
|
|
ENDASM
|
|
FEND
|
|
|
|
// Internal: 15-bit LFSR with period 32767
|
|
// Taps: 13, 14 (polynomial)
|
|
FUNC rand_lfsr32k
|
|
ASM
|
|
lda |rand_sr2|+1
|
|
asl
|
|
eor |rand_sr2|+1
|
|
asl
|
|
asl
|
|
ror |rand_sr2|
|
|
rol |rand_sr2|+1
|
|
ENDASM
|
|
FEND
|
|
|
|
// Get random byte (0-255)
|
|
// Result returned in 'result' parameter
|
|
FUNC rand(out:{BYTE result})
|
|
rand_lfsr64k()
|
|
rand_lfsr32k()
|
|
|
|
ASM
|
|
lda |rand_sr1|
|
|
eor |rand_sr2|
|
|
sta |result|
|
|
ENDASM
|
|
FEND
|
|
|
|
// Get random byte in range [0, max)
|
|
// Uses rejection sampling for unbiased results
|
|
FUNC rand_max({BYTE max} out:{BYTE result})
|
|
BYTE r
|
|
BYTE mask
|
|
|
|
// Calculate mask (smear highest bit down to get next power of 2 minus 1)
|
|
mask = max - 1
|
|
ASM
|
|
lda |mask|
|
|
lsr
|
|
ora |mask|
|
|
sta |mask|
|
|
lsr
|
|
lsr
|
|
ora |mask|
|
|
sta |mask|
|
|
lsr
|
|
lsr
|
|
lsr
|
|
lsr
|
|
ora |mask|
|
|
sta |mask|
|
|
ENDASM
|
|
|
|
// Rejection sampling loop
|
|
WHILE 1
|
|
rand(r)
|
|
r = r & mask
|
|
IF r < max
|
|
result = r
|
|
EXIT
|
|
ENDIF
|
|
WEND
|
|
FEND
|
|
|
|
LABEL __skip_lib_random
|
|
|
|
#IFEND
|