How to get a random number in ASM on the Commander X16 ?

harper

New Member
Nov 12, 2019
9
2
3
Has anyone a routine to do this?

Will the BASIC JSR from the C64 do the job? ($E097)

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

Thanks!
 

Wertyloo

Member
Sep 15, 2019
72
7
8
Are there any good sources of prng like timers or whatever I can use to source a function?
hiya!
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!
 

harper

New Member
Nov 12, 2019
9
2
3
It doesn't need to be secure, I just want a random stream of 0 or 1, e.g. 1 1 1 0 1 1 0 0 0 0 0 0 1 1 1 0 1 1 1 0 1 etc. For randomizing the initial state of an array. I looked at SID ones, and didn't think they'd work on the CX16.

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.
 

zdaddyo

New Member
Sep 24, 2019
6
4
3
Colorado
From the Programmers Reference Guide:

Commodore 64 API Compatibility
The KERNAL fully supports the C64 KERNAL API.

...

Time: $FFDE: RDTIM – read system clock $FFDB: SETTIM – write system clock $FFEA: UDTIM – advance clock

...and a real time clock...

I/O Area
This is the memory map of the I/O Area:
AddressesDescription
$9F00-$9F1FReserved for audio controller
$9F20-$9F3FVERA video controller
$9F40-$9F5FReserved
$9F60-$9F6FVIA I/O controller #1
$9F70-$9F7FVIA I/O controller #2
$9F80-$9F9FReal time clock
$9FA0-$9FBFFuture Expansion
$9FC0-$9FDFFuture Expansion
$9FE0-$9FFFFuture Expansion


Probably could use either clock.
 

JoshuaScholar

New Member
Nov 6, 2019
24
10
3
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 >.>
 
Last edited:

BruceMcF

Active Member
May 19, 2019
150
49
28
It also depends on your non pseudo random seed source at the head of the chain. If you start with Unix day xor second of the day xor the bottom two bytes of your timer, that has properties that will be better for some prng's than others. And high frequency timer seeds are often best if initial seeding follows a user input.
 

JoshuaScholar

New Member
Nov 6, 2019
24
10
3
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.
 
Last edited:

JoshuaScholar

New Member
Nov 6, 2019
24
10
3
I made a combination of 2 super short lagged Fibonacci generators (Knuth's algorithm), short to save memory. And so far it passed Small Crush, now I'm trying Big Crush which should take a day, but translated into English that means it's probably a REALLY GOOD random number generator. I'll make that my rng for the CommanderX16. Calculating one random number requires 1 subtract (times the number of bytes you want), 1 add (same), 1 exclusive or(same) four decrements, with branches for when they hit 0 and indexing 4 numbers as sources with 2 of them as the destination.
 

JoshuaScholar

New Member
Nov 6, 2019
24
10
3
I had to make it a bit longer and mess with it to pass Crush... Now trying BigCrush.
It's already way overkill. High quality RNG for a 6502
 

JoshuaScholar

New Member
Nov 6, 2019
24
10
3
I know I'm more or less talking to no one but this is getting cool.
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.
 
  • Like
Reactions: Torst

Schlowski

Member
Sep 24, 2019
47
26
18
I read your posts, wating for your final RND version. I'm very interested in a solid random number generator!
 

harper

New Member
Nov 12, 2019
9
2
3
I know I'm more or less talking to no one but this is getting cool.
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!

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.
 

JoshuaScholar

New Member
Nov 6, 2019
24
10
3
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.

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:
  • Like
Reactions: BruceMcF

JoshuaScholar

New Member
Nov 6, 2019
24
10
3
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.
 
Last edited:

JoshuaScholar

New Member
Nov 6, 2019
24
10
3
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).

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:
  • Like
Reactions: BruceMcF

JoshuaScholar

New Member
Nov 6, 2019
24
10
3
When I'm less busy I'll test it and I'll write a routine that will return a uniform deviate from 0-n, where you supply the number n.
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.
 
  • Like
Reactions: BruceMcF

BruceMcF

Active Member
May 19, 2019
150
49
28
When I'm less busy I'll test it and I'll write a routine that will return a uniform deviate from 0-n, where you supply the number n.

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.

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.
 

JoshuaScholar

New Member
Nov 6, 2019
24
10
3
Good point. Though if the output were shifted in parallel with testing the range, that would save having a lookup table. That said, having a "top bit" table would be useful in general, even if it's a lot of memory to use for this. Ie, getting the mask could be just a lookup or two.

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.
 

JoshuaScholar

New Member
Nov 6, 2019
24
10
3
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.

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:
  • Like
Reactions: BruceMcF