Previously I tested Bob Jenkins 8-bit Small Fast PRNG, but it translates to bigger 6502 code and only passes PractRand 0.94, not 0.95, as it fails the new "mod3n(0):(0,9-6)" test at 2^18 bytes.
Is that the jsf8 code below ? This code uses a different strategy. It's not an iterator+output function, but a chaotic feedback function, which suffers from the problem that some bad seeds can cause short periods. They work around that problem by the "raninit" function that forces a restriction on possible seed values, so that they guarantee a long (but less than 4GB) period. The advantage of this method is that you have the potential for better randomness in fewer operations. However, the rot8() is not well suited to 6502, so the translation to 6502 assembly adds a bunch of cycles again.
It would be interesting to explore the concept, but starting from scratch with 6502 targeted operations. Edit: I tried a few things, but not much hope. The nice thing about the ADC/STA sequence is that the accumulator is already loaded with the value that will be used to modify the next state byte. If you try to do something like the code below, you're going to need some LDAs, which seem like a waste of time, because they are only moving data around, not actually changing it. Maybe there is a solution, but at best I would expect a tiny improvement in cycles, at the bigger cost of not being able to use any random seed
Code: Select all
typedef uint8_t u1;
typedef struct ranctx { u1 a; u1 b; u1 c; u1 d; } ranctx;
#define rot8(x,k) (((x)<<(k))|((x)>>(8-(k))))
u1 ranval( ranctx *x ) {
u1 e = x->a - rot8(x->b, 1);
x->a = x->b ^ rot8(x->c, 4);
x->b = x->c + x->d;
x->c = x->d + e;
x->d = e + x->a;
return x->d;
void raninit( ranctx *x, u1 seed ) {
x->a = 0xed, x->b = x->c = x->d = seed;
for (u1 i=0; i<20; ++i) {
(void)ranval(x);
}
}