.def templ1 = r0 ; two temporary registers
.def templ2 = r1
.def zero = r2 ; always zero
.def pinkc = r3 ; counter for CTZ table indexing
.def bufpi = r4 ; play buffer index
; pink noise
.def rndl = r6 ; left channel's value
.def rndr = r7 ; right channel
.def temph1 = r16 ; two more temporary variables
.def temph2 = r17
.def tap0 = r18 ; LSFR taps
.def tap2 = r19
.def tap30 = r20
;---------------------------------------
; RAM variables
.dseg
.org SRAM_START
; lfsr address is aligned to 32 bytes bonduary
lfsr_pad:
.org (((lfsr_pad + 0x1f) >> 5) << 5)
lfsr: .byte 32 ; LSFR storage
; white noise octave decimator storage.
; aligned to 16 bytes
octdec_pad:
.org (((octdec_pad + 0x0f) >> 4) << 4)
octdec:
octdecl: .byte 8 ; left channel
octdecr: .byte 8 ; right channel
.cseg
;----------------------------------
init:
clr zero ; this register is always zero
ldi tap0, 0 ; setup tap positions
ldi tap2, 2
ldi tap30, 30
clr pinkc ; clear CTZ counter
; load pseudo-random generator seed
; seed table contains values in range 0..255
; these values are and'ed with 0x1f to make the generator
; output only numbers in range 0..31
ldi YH, HIGH(lfsr) ; load lfsr table pointer into register pair
ldi YL, LOW(lfsr) ; this is zero
ldi ZL, LOW(rnd_seed<<1) ; rnd init values from program memory (word address)
ldi ZH, HIGH(rnd_seed<<1)
ldi temph1, 32 ; repeat loop 32 times
seed_loop:
lpm temph2, Z+
andi temph2, 0x1f ; VERY important.
st Y+, temph2
dec temph1
brne seed_loop
; the rndl, and rndr variables MUST be initialized to 0
; any other value can cause overflow while summing
; values from decimators
mov rndl, zero
mov rndr, zero
; clear decimator storage
ldi temph1, 16 ; repeat loop 16 times (for both octdec tables)
ldi ZL, LOW(octdecl)
ldi ZH, HIGH(octdecl)
octdec_loop:
st Z+, zero
dec temph1
brne octdec_loop
;----------------------------------
;---- pink noise
; Uses Voss-McCartney algorithm
pink:
; CTZ table is aligned to 256 bytes bonduary,
; so index in this table is LSB of address
ldi ZH, HIGH(ctz8<<1) ;1 load MSB of CTZ table address
mov ZL, pinkc ;1 'add' CTZ table index
lpm YL, Z ;3 get octave number
subi YL, LOW(-octdec) ;1 add left decimator array address
sbci YH, HIGH(-octdec) ;1
inc pinkc ;1 increment CTZ counter (index)
ldd templ1, Y+0 ;2 load previous random value from left decimator
ldd templ2, Y+8 ;2 and from right decimator
; get new random value for left channel
ldi ZH, HIGH(lfsr) ;1 get lfsr table pointer
ldi ZL, LOW(lfsr) ;1
add ZL, tap2 ;1 add tap2 index (aligned - no carry)
ld temph1, Z ;2 load value from table
ldi ZL, LOW(lfsr) ;1
add ZL, tap30 ;1 add tap 30 position
ld temph2, Z ;2
eor temph1, temph2 ;1 Ex-OR both taps
; shift LSFR
; in fact we don't shift the register, but position of each tap
subi tap0, 1 ;1
andi tap0, 0x1f ;1
subi tap2, 1 ;1
andi tap2, 0x1f ;1
subi tap30, 1 ;1
andi tap30, 0x1f ;1
ldi ZL, LOW(lfsr) ;1
add ZL, tap0 ;1 tap 0 position
st Z, temph1 ;2 store new value at tap0
std Y+0, temph1 ;2 replace old value in decimator
sub rndl, templ1 ;1 subtract old value from random number
add rndl, temph1 ;1 add new one
; compute new random value for right channel
ldi ZL, LOW(lfsr) ;1
add ZL, tap2 ;1 add tap2 index (aligned - no carry)
ld temph1, Z ;2 load value from table
ldi ZL, LOW(lfsr) ;1
add ZL, tap30 ;1 add tap 30 position
ld temph2, Z ;2
eor temph1, temph2 ;1 Ex-OR both taps
; shift LSFR
subi tap0, 1 ;1
andi tap0, 0x1f ;1
subi tap2, 1 ;1
andi tap2, 0x1f ;1
subi tap30, 1 ;1
andi tap30, 0x1f ;1
ldi ZL, LOW(lfsr) ;1
add ZL, tap0 ;1 tap 0 position
st Z, temph1 ;2 store new value at tap0
std Y+8, temph1 ;2
sub rndr, templ2 ;1
add rndr, temph1 ;1
ret
;=======================================
; data tables
; pseudo-random generator seed table
rnd_seed:
.db 0xec, 0x07, 0x13, 0x42, 0x55, 0x73, 0x2b, 0x60
.db 0x08, 0xf4, 0x26, 0x07, 0x59, 0x37, 0x9b, 0x40
.db 0x04, 0xa6, 0x49, 0xe7, 0xe6, 0x31, 0x7d, 0x60
.db 0x70, 0x02, 0xce, 0x92, 0x7e, 0xfc, 0x26, 0x1a
; Results of Count_Trailing_Zeros(i | 0x80) for i = 0..255.
; These are the numbers of rnd decimators to update in each cycle.
; First, and 128th number is 7, but this decimator should be updated
; only once. The function to generate this table should be CTZ(i)
; Probably no decimator should be updated when i==0,
; as suggested by Phil Burk [1]. However original Voss algorithm
; has no stage in cycle in wich no decimator is updated.
; store at BYTE address aligned to 256
ctz8_pad:
.org (((ctz8_pad + 0x7f) >> 7) << 7) ; align WORD address to 128
ctz8:
.db 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
.db 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
.db 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
.db 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
.db 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
.db 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
.db 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
.db 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
.db 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
.db 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
.db 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
.db 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
.db 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
.db 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
.db 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
.db 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0