- Thread starter harper
- Start date

hiya!Are there any good sources of prng like timers or whatever I can use to source a function?

1st you need to know what type of rnd you want:

like pseudo-random[mathematical gen.ed] or real[enthropy]-like

for pseudo, you can get many of the maths from a simple googling

for real, uhh, if you dont want spec. hw then read a noisewaveform register[soundchip],if thats possible

DO NOT raster or timer-based ones!

goodluck!

The maths is okay, but most methods rely on being able to get a counter or something from somewhere, and I didn't know where to get that on this platform. I'll keep digging around.

The KERNAL fully supports the C64 KERNAL API.

...

...and a real time clock...

This is the memory map of the I/O Area:

Addresses | Description |
---|---|

$9F00-$9F1F | Reserved for audio controller |

$9F20-$9F3F | VERA video controller |

$9F40-$9F5F | Reserved |

$9F60-$9F6F | VIA I/O controller #1 |

$9F70-$9F7F | VIA I/O controller #2 |

$9F80-$9F9F | Real time clock |

$9FA0-$9FBF | Future Expansion |

$9FC0-$9FDF | Future Expansion |

$9FE0-$9FFF | Future Expansion |

Probably could use either clock.

For high speed on a 6502, Knuth's subtractive random number generator is probably the best, but it's only a great RNG when it's seeded from ANOTHER very high quality RNG.

I worked on this a bit, and came up with a pyramid of relatively high quality RNGS to initialize each other that don't need multiplication or mod or division.

I used xoshiro256ss to initialize Knuth's algorithm and I used splitmix64 to initialize xoshiro256ss >.> These are all algorithms you can google.

I know this sounds insane, but if your goal is very high quality random numbers fast on a slow processor that doesn't have multiply or divide you have to deal with the fact that algorithms that have the right qualities are fragile and need to be initialized carefully.

Edit, looking at my code it's worse that that.

I was reading articles on the weaknesses of these algorithms and how they test when I was writing this and I actually have a fourth algorithm in my little experiment. Obviously I have time on my hands.

It went splitmix64 -> xorshift128 -> xoshiro256ss -> Knuth.

Also the rngs I'm using to see DO have multiplication but Knuth just has subtraction and indexing.

I admit that's nuts >.>

I worked on this a bit, and came up with a pyramid of relatively high quality RNGS to initialize each other that don't need multiplication or mod or division.

I used xoshiro256ss to initialize Knuth's algorithm and I used splitmix64 to initialize xoshiro256ss >.> These are all algorithms you can google.

I know this sounds insane, but if your goal is very high quality random numbers fast on a slow processor that doesn't have multiply or divide you have to deal with the fact that algorithms that have the right qualities are fragile and need to be initialized carefully.

Edit, looking at my code it's worse that that.

I was reading articles on the weaknesses of these algorithms and how they test when I was writing this and I actually have a fourth algorithm in my little experiment. Obviously I have time on my hands.

It went splitmix64 -> xorshift128 -> xoshiro256ss -> Knuth.

Also the rngs I'm using to see DO have multiplication but Knuth just has subtraction and indexing.

I admit that's nuts >.>

Last edited:

Looking at the code, I also notice that I came up with this RNG (imagine that this is C but you have a 3 byte unsigned int size which is conveniently not too big or small on a 6502) then the following is an RNG that goes through every value except 0. I have no memory if this sort of thing is good or not. Use the previous output as the next input. You could also call this a hash.

inline uint24_t xrng(uint24_t x)

{

x ^= (x << 3);

x ^= (x >> 15);

x ^= (x << 11);

return x;

}

1,7,9 or 9,7,1 also work instead of 3,15,11 and have the advantage of being only single bit shifts (and one byte shift). also 13,5,8 and 11,1,8 and 8,15,11, and 6,1,9...

Anyway knuth is cheaper and better, but I think if you combined three of these properly you'd have something worthy of initializing knuth for serious use.

inline uint24_t xrng(uint24_t x)

{

x ^= (x << 3);

x ^= (x >> 15);

x ^= (x << 11);

return x;

}

1,7,9 or 9,7,1 also work instead of 3,15,11 and have the advantage of being only single bit shifts (and one byte shift). also 13,5,8 and 11,1,8 and 8,15,11, and 6,1,9...

Anyway knuth is cheaper and better, but I think if you combined three of these properly you'd have something worthy of initializing knuth for serious use.

Last edited:

It's already way overkill. High quality RNG for a 6502

I've refined it down to a ONE byte wide RNG that generates one byte at a time pretty efficiently (not sure how many cycles since it isn't 6502 yet).

The cool part is that the stream of bytes is high enough quality that if you string them together into 32 bit words, the resulting stream passes the BigCrunch tests of random generators. Also the 1 byte wide version takes 1/3 as much buffer space as the 3 byte wide version.

I'm reading them!

I've refined it down to a ONE byte wide RNG that generates one byte at a time pretty efficiently (not sure how many cycles since it isn't 6502 yet).

The cool part is that the stream of bytes is high enough quality that if you string them together into 32 bit words, the resulting stream passes the BigCrunch tests of random generators. Also the 1 byte wide version takes 1/3 as much buffer space as the 3 byte wide version.

It's interesting and I'm going to need an RNG at some point shortly, so the more information the better.

I'm working with text mode on the CX16 so mostly I need some RNG for starting positions of characters within an 80x60 (0-79 x 0-59) grid.

Ok, I've written it, but I'd be evil to paste the code now as the 6502 version isn't tested or even assembled once, but I'll do it anyway.

I estimate it's a about 167 cycles per call, more occasionally.

To use, put 3 bytes of initialization seed in rngseed1 and another 3 bytes of initiazation seed in rngseed2 and call seedrandom;

From then on just call rndbyte for each bytes you need.

It can pass bigcrush or not depending on the seed. I just tried a seed of 0 and it failed one test slightly, but it passed crush and smallcrush.

I think the random number generators most people use on PCs (let alone 8 bit processors) wouldn't pass smallcrush or the rest since most people use simple linear congruential generators. I can't guarantee that there isn't some specific seed where the quality of the output will be poor, but I think it will never be HORRIBLE. It is in this version a combination of three medium sized fibonacci lag generators, using a little over 100 bytes in state. The really unusual part is that the width or mod is only one byte. That's something that probably hasn't been studied. Also this kind of generator was recommended by Knuth, but since then people have realized that it's not the best design if you want an RNG where it's easy to guarantee that for a given seed the output is high quality - also computers are SO MUCH FASTER than when Knuth made his suggestion - except for ours. This one is slightly more sophisticated than Knuth's because it has carry between the operations. I've seen examples of this, but no one talks about what effect that alteration has on the RNG. But it stands to reason that it makes the lower bits better.

I estimate it's a about 167 cycles per call, more occasionally.

To use, put 3 bytes of initialization seed in rngseed1 and another 3 bytes of initiazation seed in rngseed2 and call seedrandom;

From then on just call rndbyte for each bytes you need.

It can pass bigcrush or not depending on the seed. I just tried a seed of 0 and it failed one test slightly, but it passed crush and smallcrush.

I think the random number generators most people use on PCs (let alone 8 bit processors) wouldn't pass smallcrush or the rest since most people use simple linear congruential generators. I can't guarantee that there isn't some specific seed where the quality of the output will be poor, but I think it will never be HORRIBLE. It is in this version a combination of three medium sized fibonacci lag generators, using a little over 100 bytes in state. The really unusual part is that the width or mod is only one byte. That's something that probably hasn't been studied. Also this kind of generator was recommended by Knuth, but since then people have realized that it's not the best design if you want an RNG where it's easy to guarantee that for a given seed the output is high quality - also computers are SO MUCH FASTER than when Knuth made his suggestion - except for ours. This one is slightly more sophisticated than Knuth's because it has carry between the operations. I've seen examples of this, but no one talks about what effect that alteration has on the RNG. But it stands to reason that it makes the lower bits better.

Code:

```
MLEN1 = 71
MLAG2 = 65
MLEN2 = 31
MLAG2 = 13
MLEN3 = 55
MLAG3 = 24
rngstate1 .res MLEN1
rngstate2 .res MLEN2
rngstate3 .res MLEN3
rngcache .res 1
rngcarry1 .res 1
rngcarry2 .res 2
rngcarry3 .res 3
rngi1 .res 1
rngs1 .res 1
rngi2 .res 1
rngs2 .res 1
rngi3. res 1
rngs3. res 1
rngseed1 .res 3
rngseed2 .res 3
;three bytes each, temps used by seedshiftreggen
rngseed1tmp = $2
rngseed2tmp = $5
rngseed3tmp = $8
rngseed4tmp = $b
rngseed5tmp = $e
rngtmp = $10 ;one byte
;seedrandom
;a complex combination of shift reg generators
;that use rngseed to generate single bytes in
;order to initialize the somewhat faster fibonacci
;rand number generator
;call condition before using seedshiftreggen to generate the
;the intial data for the lagged fibonacci generator
conditionshiftseedgen:
;randomize the seed input bits a little so that
;inputs like 1 or 2 won't be kind of poor
lda #$b5
eor rngseed1tmp
sta rngseed1tmp
lda #$b0
eor rngseed1tmp+1
sta rngseed1tmp+1
lda #$cc
eor rngseed1tmp+2
sta rngseed1tmp+2
ora rngseed1tmp+1
ora rngseed1tmp ;if seed is 0 then load something else
bne :+
lda #$f3
sta rngseed1tmp
sta rngseed1tmp+1
sta rngseed1tmp+2
: lda #$c7
eor rngseed2tmp
sta rngseed2tmp
lda #$49
eor rngseed2tmp+1
sta rngseed2tmp+1
lda #$d2
eor rngseed2tmp+2
sta rngseed2tmp+2
ora rngseed2tmp+1
ora rngseed2tmp
bne :+
lda #$5a
sta rngseed2tmp
sta rngseed2tmp+1
sta rngseed2tmp+2
: rts
;input address in x()
;number of bits to shift in y
shft3left:
ldy #0
: asl 0,x
rol 1,x
rol 2,x
dey
bne :-
rts
;input address in (x)
;number of bits to shift in y
shft3right:
ldy #2
: lsr 0,x
ror 1,x
ror 2,x
dey
bne :-
rts
;input address in (x)
;number of bits to shift in y
rot3right:
: lda 0,x
lsr a
ror 2,x
ror 1,x
ror 0,x
dey
bne :-
rts
;do one tap
;num bits to shift in y
;input/output (x)
;uses rngseed3tmp
eor3shftright:
lda 0,x
sta rngseed3tmp
lda 1,x
sta rngseed3tmp+1
lda 2,x
sta rngseed3tmp+2
jsr shft3right
lda 0,x
eor rngseed3tmp
sta 0,x
lda 1,x
eor rngseed3tmp+1
sta 1,x
lda 2,x
eor rngseed3tmp+2
sta 2,x
rts
;do one tap
;num bits to shift in x
;input/output (shiftparam)
;uses rngseed3tmp
eor3shftleft:
lda 0,x
sta rngseed3tmp
lda 1,x
sta rngseed3tmp+1
lda 2,x
sta rngseed3tmp+2
jsr shft3left
lda 0,x
eor rngseed3tmp
sta 0,x
lda 1,x
eor rngseed3tmp+1
sta 1,x
lda 2,x
eor rngseed3tmp+2
sta 2,x
rts
;generate next initialization byte
;state in rngseed1tmp,rngseed2tmp
;output in a
;uses rngseed1tmp-rngseed5tmp
;does not touch x,y
seedshiftreggen:
phx
phy
ldx #rngseed1tmp
ldy #3
jsr eor3shftleft
ldy #15
jsr eor3shftright
ldy #11
jsr eor3shftleft
lda rngseed1tmp
sta rngseed4tmp
lda rngseed1tmp+1
sta rngseed4tmp+1
lda rngseed1tmp+2
sta rngseed4tmp+2
ldx #rngseed4tmp
ldy #11
jsr rot3right
ldy #6
jsr eor3shftleft
ldy #1
jsr eor3shftright
ldy #9
jsr eor3shftleft
ldx #rngseed2tmp
ldy #13
jsr eor3shftleft
ldy #5
jsr eor3shftright
ldy #8
jsr eor3shftleft
lda rngseed2tmp
sta rngseed5tmp
lda rngseed2tmp+1
sta rngseed5tmp+1
lda rngseed2tmp+2
sta rngseed5tmp+2
ldx #rngseed5tmp
ldy #15
jsr rot3right
ldy #11
jsr eor3shftleft
ldy #1
jsr eor3shftright
ldy #8
jsr eor3shftleft
clc
lda rngseed5tmp
adc rngseed4tmp
sta rngseed5tmp
lda rngseed5tmp+1
adc rngseed4tmp+1
sta rngseed5tmp+1
lda rngseed5tmp+2
adc rngseed4tmp+2
sta rngseed5tmp+2
lda #4
jsr shft3right
lda rngseed5tmp+1
ply
plx
rts
; initialize seed
; input: rngseed1,rngseed2
; output state in:
; rngstate1-rngstate3, rngcarry1-rngcarry3,rngi1-rngi3, rngs1-rngs3, rngcache
; uses all registers
seedrandom:
jsr conditionshiftseedgen
ldx #MLEN1
ldy #0
: jsr seedshiftreggen
sta rngstate1,y
iny
dex
bne :-
ldx #MLEN2
ldy #0
: jsr seedshiftreggen
sta rngstate2,y
iny
dex
bne :-
ldx #MLEN3
ldy #0
: jsr seedshiftreggen
sta rngstate3,y
iny
dex
bne :-
stz rngi1
stz rngi2
stz rngi3
stz rngcache
;initializing the carries isn't necessary but except for testing
lda #$ff
sta rngcarry1
stz rngcarry2
stz rngcarry3
lda #MLAG1
sta rngs1
lda #MLAG2
sta rngs2
lda #MLAG2
sta rngs3
;x is still 0, do 256 warm up
: jsr rndbyte
dex
bne :-
rts
;the important random function
;generate one random byte
;output in a
;changes state in
; rngstate1-rngstate3, rngcarry1-rngcarry3,rngi1-rngi3, rngs1-rngs3, rngcache
; uses rngtmp
; does not effect x and y
rndbyte:
phx
dec rngi1
bpl :+
lda #MLEN1-1
sta rngi1
: dec rngs1
bpl pastcachechange
lda #MLEN1-1
sta rngs1
dec rngi2
bpl :+
lda #MLEN2-1
sta rngi2
: dec rngs2
bpl :+
lda #MLEN2-1
sta rngs2
: lsr rngcarry2
ldx rngs2
lda rngstate2,x
ldx rngi2
adc rngstate2,x
sta rngstate2,x
rol rngcarry2 ;save the carry
sta rngcache
pastcachechange:
lsr rngcarry1
ldx rngi1
lda rngstate1,x
ldx rngs1
sbc rngstate1,x
ldx rngi1
sta rngstate1,x
rol rngcarry1 ;save the carry
eor rngcache
sta rngtmp
dec rngi3
bpl :+
lda #MLEN3-1
sta rngi3
: dec rngs3
bpl :+
lda #MLEN3-1
sta rngs3
: lsr rngcarry3
ldx rngs3
lda rngstate3,x
ldx rngi3
adc rngstate3,x
sta rngstate3,x
rol rngcarry3 ;save the carry
sec
sbc rngtmp
plx
rts
```

Last edited:

I'm checking again with a design that might almost double the speed. Last time I tried it it failed one test spectacularly, but there were bugs and I've also made the lags bigger, so it's worth trying out again. Though it's also a test that might not matter either >.>

It did good enough with only two longer flgs, one of them only running once per whole buffer iteration of the other. That should make it about twice as fast.

It did good enough with only two longer flgs, one of them only running once per whole buffer iteration of the other. That should make it about twice as fast.

Last edited:

About 66 cycles on most calls. Passed BigCrush.

Update, a version has been tested and verified though on a different assembler and I made sure this copy DOES assemble without errors on

ca65. Be sure to use the --cpu 65SC02 flag, because it uses some 65c02 instructions.

I say 65SC02 instead of 65C02 because that is a subset of instructions that are compatible with a 65816, while a total 65c02 instruction set has 4 instructions that a 65816 lacks (the direct bit ones).

Update, a version has been tested and verified though on a different assembler and I made sure this copy DOES assemble without errors on

ca65. Be sure to use the --cpu 65SC02 flag, because it uses some 65c02 instructions.

I say 65SC02 instead of 65C02 because that is a subset of instructions that are compatible with a 65816, while a total 65c02 instruction set has 4 instructions that a 65816 lacks (the direct bit ones).

Code:

```
MLEN1 = 71
MLAG1= 65
MLEN2 = 55
MLAG2 = 24
rngstate1: .res MLEN1
rngstate2: .res MLEN2
rngcache: .res 1
rngcarry1: .res 1
rngcarry2: .res 2
rngi1: .res 1
rngs1: .res 1
rngi2: .res 1
rngs2: .res 1
;three bytes each, temps used by seedshiftreggen
rngseed1 = $2
rngseed2 = $5
rngseed3tmp = $8
rngseed4tmp = $b
rngseed5tmp = $e
;seedrandom
;a complex combination of shift reg generators
;that use rngseed to generate single bytes in
;order to initialize the somewhat faster fibonacci
;rand number generator
;call condition before using seedshiftreggen to generate the
;the intial data for the lagged fibonacci generator
conditionshiftseedgen:
;randomize the seed input bits a little so that
;inputs like 1 or 2 won't be kind of poor
lda #$b5
eor rngseed1
sta rngseed1
lda #$b0
eor rngseed1+1
sta rngseed1+1
lda #$cc
eor rngseed1+2
sta rngseed1+2
ora rngseed1+1
ora rngseed1 ;if seed is 0 then load something else
bne :+
lda #$f3
sta rngseed1
sta rngseed1+1
sta rngseed1+2
: lda #$c7
eor rngseed2
sta rngseed2
lda #$49
eor rngseed2+1
sta rngseed2+1
lda #$d2
eor rngseed2+2
sta rngseed2+2
ora rngseed2+1
ora rngseed2
bne :+
lda #$5a
sta rngseed2
sta rngseed2+1
sta rngseed2+2
: rts
;input address in x()
;number of bits to shift in y
shft3left:
: asl 0,x
rol 1,x
rol 2,x
dey
bne :-
rts
;input address in (x)
;number of bits to shift in y
shft3right:
: lsr 2,x
ror 1,x
ror 0,x
dey
bne :-
rts
;input address in (x)
;number of bits to shift in y
rot3right:
: lda 0,x
lsr a
ror 2,x
ror 1,x
ror 0,x
dey
bne :-
rts
;do one tap
;num bits to shift in y
;input/output (x)
;uses rngseed3tmp
eor3shftright:
lda 0,x
sta rngseed3tmp
lda 1,x
sta rngseed3tmp+1
lda 2,x
sta rngseed3tmp+2
jsr shft3right
lda 0,x
eor rngseed3tmp
sta 0,x
lda 1,x
eor rngseed3tmp+1
sta 1,x
lda 2,x
eor rngseed3tmp+2
sta 2,x
rts
;do one tap
;num bits to shift in x
;input/output (shiftparam)
;uses rngseed3tmp
eor3shftleft:
lda 0,x
sta rngseed3tmp
lda 1,x
sta rngseed3tmp+1
lda 2,x
sta rngseed3tmp+2
jsr shft3left
lda 0,x
eor rngseed3tmp
sta 0,x
lda 1,x
eor rngseed3tmp+1
sta 1,x
lda 2,x
eor rngseed3tmp+2
sta 2,x
rts
;generate next initialization byte
;state in rngseed1,rngseed2
;output in a
;uses rngseed1-rngseed5tmp
;does not touch x,y
seedshiftreggen:
phx
phy
ldx #rngseed1
ldy #3
jsr eor3shftleft
ldy #15
jsr eor3shftright
ldy #11
jsr eor3shftleft
lda rngseed1
sta rngseed4tmp
lda rngseed1+1
sta rngseed4tmp+1
lda rngseed1+2
sta rngseed4tmp+2
ldx #rngseed4tmp
ldy #6
jsr eor3shftleft
ldy #1
jsr eor3shftright
ldy #9
jsr eor3shftleft
ldx #rngseed2
ldy #13
jsr eor3shftleft
ldy #5
jsr eor3shftright
ldy #8
jsr eor3shftleft
lda rngseed2
sta rngseed5tmp
lda rngseed2+1
sta rngseed5tmp+1
lda rngseed2+2
sta rngseed5tmp+2
ldx #rngseed5tmp
ldy #11
jsr eor3shftleft
ldy #1
jsr eor3shftright
ldy #8
jsr eor3shftleft
clc
lda rngseed5tmp
adc rngseed4tmp
sta rngseed5tmp
lda rngseed5tmp+1
adc rngseed4tmp+1
sta rngseed5tmp+1
lda rngseed5tmp+2
adc rngseed4tmp+2
sta rngseed5tmp+2
ldy #4
jsr shft3right
lda rngseed5tmp+1
ply
plx
rts
; initialize seed
; input: rngseed1,rngseed2
; output state in:
; rngstate1-rngstate3, rngcarry1-rngcarry3,rngi1-rngi3, rngs1-rngs3, rngcache
; uses all registers
seedrandom:
jsr conditionshiftseedgen
ldx #MLEN1
ldy #0
: jsr seedshiftreggen
sta rngstate1,y
iny
dex
bne :-
ldx #MLEN2
ldy #0
: jsr seedshiftreggen
sta rngstate2,y
iny
dex
bne :-
stz rngi1
stz rngi2
stz rngcache
;initializing the carries isn't necessary but except for testing
lda #$ff
sta rngcarry2
stz rngcarry1
lda #MLAG1
sta rngs1
lda #MLAG2
sta rngs2
;x is still 0, do 256 warm up
: jsr rndbyte
dex
bne :-
rts
;the important random function
;generate one random byte
;output in a
;changes state in
; rngstate1-rngstate2, rngcarry1-rngcarry2,rngi1-rngi2, rngs1-rngs2, rngcache
; does not effect x and y
rndbyte:
phx
dec rngi1
bmi :++
dec rngs1
bmi :+++
: lsr rngcarry1
ldx rngs1
lda rngstate1,x
ldx rngi1
adc rngstate1,x
sta rngstate1,x
rol rngcarry1 ;save the carry
eor rngcache
plx
rts
: lda #MLEN1-1
sta rngi1
dec rngs1
bpl:--
: lda #MLEN1-1
sta rngs1
dec rngi2
bpl :+
lda #MLEN2-1
sta rngi2
: dec rngs2
bpl :+
lda #MLEN2-1
sta rngs2
: lsr rngcarry2
ldx rngi2
lda rngstate2,x
ldx rngs2
sbc rngstate2,x
ldx rngi2
sta rngstate2,x
rol rngcarry2 ;save the carry
sta rngcache
bra :-----
```

Last edited:

The better algorithm for that is not to multiply the range down, but to shift so that the top maximum bit is the same as n, then to just reject and retry any numbers over n.

So if you want numbers 1 to 6, you request 0-5, the main algorithm returns 0-7 and then if it's 6 or 7 retry.

The better algorithm for that is not to multiply the range down, but to shift so that the top maximum bit is the same as n, then to just reject and retry any numbers over n.

Do you have to shift?

If it's a good RNG, then the bottom n bits ought to also be a good RNG, so at first blush it seems like you could just mask off the high bits. A byte-wise mask generation routine would be something like:

```

STA temp

STA mask

CMP #0

BRA ++

LDA #$FF

- ASL mask

BCS +

ROR A

BRA -

+ STA mask

LDA temp

++ RTS

```

... run it on each byte of n for the full mask. Then if the RNG routine remembers the previous N, it only has to generate a new mask for a new N.

I'm running BigCrush again with reversed bits, that will help test if the low bits are good. I'll be disappointed if they aren't.

Also I fixed a couple typos in the code above.

Bitreversed passed Crush so far and is on bigcrush.

Here's a doesn't-use-any-tables version of pick a byte up to n. Also the original code defined a zeropage variable called rngtmp but never used it, here I use it and other called rngtmp2.

Here's a doesn't-use-any-tables version of pick a byte up to n. Also the original code defined a zeropage variable called rngtmp but never used it, here I use it and other called rngtmp2.

Code:

```
rndbytevalue:
tay
bne @notzero
lda #0
rts
@notzero:
sta rngtmp
inc a
bne @notff
jmp rndbyte
@notff:
sta rngtmp2
lda #$20
bit rngtmp
bmi @mff
bvs @m7f
bne @m3f
tya
asl a
asl a
asl a
sta rngtmp
lda #$20
bit rngtmp
bmi @m1f
bvs @m0f
bne @m07
lda #$10
bit rngtmp
beq @m01
lda #3
bra @gotmask
@mff:
lda #$ff
bra @gotmask
@m7f:
lda #$7f
bra @gotmask
@m3f:
lda #$3f
bra @gotmask
@m1f:
lda #$1f
bra @gotmask
@m0f:
lda #$f
bra @gotmask
@m07:
lda #$7
bra @gotmask
@m01:
lda #$1
@gotmask:
sta rngtmp
@tryrng:
jsr rndbyte
and rngtmp
cmp rngtmp2
bpl @tryrng
rts
```

Last edited: