Jump to content
IGNORED

XB PseudoRandom Number Generator Discussion—RND and RANDOMIZE...updated 26APR2024 again


Recommended Posts

It is my intention, here, to explain the details of the pseudorandom number generator (PRNG) in TI Extended Basic (XB). Over the next few days, I will expand this first post to include my analysis to date in an effort to make it as nearly complete as I am able. I will also make any updates relevant to further insights from the ensuing discussion:

 

═══XB RND function explanation═══Part 1═══════════22APR2024═══

 

XB’s PRNG is a linear congruential  generator (LCG) with the following recurrence equation:

 

xnew = (ax + c) mod m                                                                                                                          (1)

where

x = current seed, which is initialized at power-up to 335212624223

a = 14389820420821

c = 21132486540519

m = 1014

xnew = pseudorandom number (PRN) sought and the new seed for the next go-round.

 

The above converts the above PRNG to the following equation:

 

xnew = (14389820420821x + 21132486540519) mod 1014                                                           (2)

 

To preserve precision for the radix-100, floating point calculations, all of the above numbers were split into a least-significant half (LSH), with subscript 1, and a most-significant half (MSH), with subscript 2, such that

 

x = 107x2 + x1                                                                                                                                          (3)

a = 107a2 + a1                                                                                                                                         (4)

c = 107c2 + c1                                                                                                                                           (5)

 

which converts (1) to

 

xnew = ((107a2 + a1)(107x2 + x1) + 107c2 + c1) mod 1014

xnew = (1014a2x2 + 107a2x1 + 107a1x2 + a1x1 +107c2 + c1) mod 1014                                             (6)

 

At the very least, the first term drops out because

 

1014a2x2 mod 1014 = 0

 

simplifying (6) to

 

xnew = (107a2x1 + 107a1x2 + a1x1 +107c2 + c1) mod 1014                                                                 (7)

 

═══XB RND function explanation═══Part 2═══════════26APR2024═══

 

Before we go any further, the following is among the comments the TI programmers included ahead of the code for calculating (7), with the t variables being intermediate programming results discussed later:

Assumptions:

  1. All numbers are integers. Fractional parts are truncated.
  2. If the variables listed below start in the ranges specified, they will also end in the ranges specified.

Constants: 

0 ≤ a2 < 5 106
0 ≤ a1 < 5 
106
0 ≤ c2 < 107
0 ≤ c1 < 107

Variables:  

0 ≤ x2 < 107
0 ≤ x1 < 107
0 ≤ t1 ≤ 1014
0 ≤ t2 < 107
0 ≤ t3 ≤ 1014
0 ≤ t4 < 107

 

We must remember that the recurrence equation (7) starts and ends with integers. Because the recurrence equation preserves only the remainder of division by 1014, we must preserve all of the LS digits, but we don’t care about any MS digits above 1014, because they do not contribute to the remainder. To further aid preserving precision during calculation, the terms of (7) were grouped by their contributions to the MSH of xnew (x2new) and the LSH of xnew (x1new). All terms can contribute to x2new, but only a1x1 and c1 contribute to x1new. To see why this is so, consider that

  • Each factor of the terms in the parenthetical expression contributes a factor up to 107 or as many as 7 digits to their term,
  • All terms with 107 as factor cannot contribute to x1new because that factor guarantees the LSH is all zeros, leaving only a1x1 and c1 with digits that can appear in the x1new.

 

FP operations that can result in more than 14 digits must be avoided or we will lose precision, i.e., digits will fall off the right side of the result. We must also avoid changing the radix-10 exponent of a 14-digit number from its even exponent to an odd one because this will shift the radix-100 number (centimal) one decimal digit right, resulting in a loss of precision from 14 digits to 13.

The reason for this is that non-zero FP numbers are always stored in normalized centimal notation, i.e., the first centimal digit (equivalent to 2 decimal digits) in the mantissa must be 1..99. This  means that, for an odd number of decimal digits before the centimal point, the first byte has only one significant decimal digit.

 

This is what would happen with the shift in even to odd number of pre-centimal digits:  Integer 12345678911234 is 12.345678911234 1006 in normalized centimal notation and >460C >2238 >4E5B >0C22 in hexadecimal. The first byte is the sign bit and the 7-bit, excess-64 exponent. If we multiplied this by 10-7, the result would be 1234567.891123, with the normalized result as 1.234567891123 1003 and stored as >4301 >172D >4359 >0B17, costing us the least significant decimal digit. Of course, had we started with 0 as the rightmost digit, it would not have mattered, as you will see below.

 

If we define MSH(x) as a function meaning “MSH of x and similarly define LSH(x), where x is a 14-digit integer, we have

 

MSH(x) = int(10-7 x)                                                                                                                                                            (8)
LSH(x) = x - 10int(10-7 x)                                                                                                                                                 (9)

 

xnew = (107 LSH(a2x1 + a1x2 + c2 + MSH(a1x1 + c1))) + LSH(a1x1 + c1)) mod 1014             from (7),(8),(9)
                      \------------------x2new-------------------/       \----x1new----/

 

xnew = 107 x2new + x1new                                                                                                                   from (3)

 

To make unwieldy equations more tractable and to incorporate the same intermediate variables (shown above) used by the TI programmers,

 

t1 = a1x1 + c1
t2 = int(10-7 t1)
x1new = (t1 - 107 t2)
t3 = a2x1 + a1x2 + c2 + t2
t4 = int(10-7 t3)
x2new = t3 - 107 t4

 

and using the maximum values from the assumptions above,

 

t1 = a1x1 + c1 = 4999999(9999999) + 9999999 = 49999995000000
t2 = int(10-7 t1) = int(4999999.5000000) = 4999999
x1new = (t1 - 107 t2) = 49999995000000 - 49999990000000 = 5000000
t3 = a2x1 + a1x2 + c2 + t2 = 4999999(9999999) + 4999999(9999999) +9999999 + 4999999 = 99999985000000
t4 = int(10-7 t3) = int(9999998.5000000) = 9999998
x2new = t3 - 107 t4 = 99999985000000 - 99999980000000 = 5000000

 

which shows that at no time do the FP calculations exceed 14 digits, thus preserving precision, regardless of the values. Note that, even though there are a couple of places where a 14-digit number is multiplied by 10-7, which causes a loss of the last digit, that last digit is always 0.

 

═══GPL code for XB RND and RANDOMIZE functions════26APR2024═══

 

The spoiler has the GPL code for XB RND and RANDOMIZE along with the ALC of the console ROM code for the GPL RAND function. I have augmented the comments to make them clearer, hopefully. My additional comments include the stack contents on the right, indicated by S:.... The comments following <--NO!! YES-->, on four lines, are mine—whoever commented those lines before I got to it had it wrong.

 

You can probably figure out what is going on in RANDOMIZE without my explanation, but I plan to post my explanation in a few days. First, I want to convert the GPL code for RND and RANDOMIZE into ALC, which will likely be easier to understand for those of you unfamiliar with GPL.

 

Spoiler
*******************************************************************
* ...at Extended Basic startup                                    *
* Initialize Seed for Pseudorandom Number Generator               *
*******************************************************************
*
INTRND MOVE 10,G@X2SEED,V@RNDX2  * copy from GROM to VRAM
       RTN
X2SEED BYTE >42,>03,>23,>15,>00  * =   33521, X2 initial value
X1SEED BYTE >43,>02,>3E,>2A,>17  * = 2624223, X1 initial value

*******************************************************************
* Pseudorandom Number Generator                                   *
*******************************************************************
*                                                                 *
*     X[N+1] = (A*X[N]+C) MOD M;  RND = X/M                       *
*                                                                 *
*     where:  X = X2 * 1E7 + X1                                   *
*             A = A2 * 1E7 + A1                                   *
*             C = C2 * 1E7 + C1                                   *
*             M = 1E14                                            *
*                                                                 *
* Assumptions:                                                    *
*  (1) All numbers are integers; fractional parts are             *
*      truncated                                                  *
*  (2) If the variables listed below start in the ranges          *
*      specified. They will also end in the ranges specified      *
*                                                                 *
* Constants: 0 <= A2 <  5E6  ; 0 <= A1 < 5E6                      *
*            0 <= C2 <  1E7  ; 0 <= C1 < 1E7                      *
* Variables: 0 <= X2 <  1E7  ; 0 <= X1 < 1E7                      *
*            0 <= T1 <= 1E14 ; 0 <= T2 < 1E7                      *
*            0 <= T3 <= 1E14 ; 0 <= T4 < 1E7                      *
*                                                                 *
* Stack usage:                                                    *
*            Constant Refs      Constant Refs   Constant Refs     *
* +---------+      In/Out            In/Out          In/Out       *
* | STACK+4 | X2*A1(F)(H)       --    ----      --    ----        *
* +---------+                                                     *
* | STACK+3 |   T2 (C)(J)       --    ----      --    ----        *
* +---------+                                                     *
* | STACK+2 |   T1 (B)(D)      X1new (E)(N)     --    ----        *
* +---------+                                                     *
* | STACK+1 |old X1(A)(G)       T3   (K)(L)    X2new (M)(P)       *
* +---------+                                                     *
*******************************************************************
* Compute new value for X1..save it in V@RNDX1
*
*                          Stack usage....................................
*                                 FAC/ARG                Stack:  {} shows
*                          Srefs  Contents               hidden stack items
*                          -----  -----------------      ------------------
NRND   MOVE 5,V@RNDX1,@FAC        FAC = X1
       CLR  @FAC5                FAC5 = >00
       DCLR @FAC6          FAC6..FAC7 = >0000
       XML  VPUSH          (A)    FAC = X1                 S:x1
       MOVE 8,G@RNDA1,@ARG        ARG = A1
       XML  FMUL                  FAC = X1*A1
       MOVE 8,G@RNDC1,@ARG        ARG = C1
       XML  FADD               T1=FAC = X1*A1+C1
       XML  VPUSH          (B)    FAC = T1                 S:x1 t1
       MOVE 8,G@RNDEM,@ARG        ARG = 1/1E7
       XML  FMUL                  FAC = T1/1E7
       CALL GRINT              T2=FAC = INT(T1/1E7)
       XML  VPUSH          (C)    FAC = T2                 S:x1 t1 t2
       MOVE 8,G@RNDEP,@ARG        ARG = 1E7                
       XML  FMUL                  FAC = T2*1E7             S:x1 t1 t2
       DSUB 8,@VSPTR                                       S:x1 t1 {t2}
       XML  SSUB        (D) X1new=FAC = T1-T2*1E7          S:x1 {t1 t2}
       MOVE 5,@FAC,V@RNDX1        FAC = X1new      
       XML  VPUSH          (E)    FAC = X1new              S:x1 x1new {t2}
*
* Compute new value for X2, save it in V@RNDX2
*
       MOVE 5,V@RNDX2,@FAC        FAC = X2
       CLR  @FAC5                FAC5 = >00
       DCLR @FAC6          FAC6..FAC7 = >0000
       MOVE 8,G@RNDA1,@ARG        ARG = A1
       XML  FMUL                  FAC = X2*A1
       DADD 8,@VSPTR                                     S:x1 x1new t2
       XML  VPUSH          (F)    FAC = X2*A1            S:x1 x1new t2 x2*a1
       DSUB 24,@VSPTR                                    S:x1 {x1new t2 x2*a1}
       XML  VPOP           (G)    FAC = X1               S:{x1 x1new t2 x2*a1}
       DADD 32,@VSPTR                                    S:x1 x1new t2 x2*a1
       MOVE 8,G@RNDA2,@ARG        ARG = A2
       XML  FMUL                  FAC = X1*A2
       XML  SADD           (H)    FAC = X2*A1+X1*A2        S:x1 x1new t2 
       MOVE 8,G@RNDC2,@ARG        ARG = C2
       XML  FADD                  FAC = X2*A1+X1*A2+C2
       XML  SADD           (J) T3=FAC = X2*A1+X1*A2+C2+T2  S:x1 x1new 
       DSUB 16,@VSPTR                                      S:{x1 x1new}
       XML  VPUSH          (K)    FAC = T3                 S:t3 {x1new}
       MOVE 8,G@RNDEM,@ARG        ARG = 1/1E7
       XML  FMUL                  FAC = T3/1E7
       CALL GRINT              T4=FAC = INT(T3/1E7)
       MOVE 8,G@RNDEP,@ARG        ARG = 1E7
       XML  FMUL                  FAC = T4*1E7
       XML  SSUB        (L) X2new=FAC = T3-T4*1E7          S:t3 {x1new}
       MOVE 5,@FAC,V@RNDX2        FAC = X2new
* 
* Compute new value for RND, leave it in FAC for return to caller
*     0 <= RND < 1
*
       MOVE 8,G@RNDEM,@ARG        ARG = 1/1E7
       XML  FMUL                  FAC = X2new/1E7
       XML  VPUSH          (M)    FAC = X2new/1E7          S:x2new/1e7 {x1new}
       DADD 8,@VSPTR                                       S:x2new/1e7 x1new
       XML  VPOP           (N)    FAC = X1new              S:x2new/1e7 
       XML  FMUL                  FAC = X1new/1E7
       XML  FMUL                  FAC = X1new/1E14
       XML  SADD           (P)RND=FAC = (X2new/1E7)+(X1new/1E14)
       XML  CONT   * Return

***********************************************************
* Constants in GROM for the Random Number Routine
RNDA2  BYTE >43,>01,>2B,>59,>52,>00,>00,>00 * = 1438982
RNDA1  BYTE >42,>2A,>08,>15,>00,>00,>00,>00 * = 0420821
RNDC2  BYTE >43,>02,>0B,>20,>30,>00,>00,>00 * = 2113248
RNDC1  BYTE >43,>06,>36,>05,>13,>00,>00,>00 * = 6540519
RNDEP  BYTE >43,>0A,>00,>00,>00,>00,>00,>00 * = 1E7
RNDEM  BYTE >3C,>0A,>00,>00,>00,>00,>00,>00 * = 1/1E7
***********************************************************


***********************************************************
*                   RANDOMIZE Statement
***********************************************************
NRNDMZ CALL CHKEND            Seed provided?
       BS   RNDM1             No
* RANDOMIZE given a seed value
* (99,000,000,000,001 possible starting positions)
* (Place-value is ignored in the input number)
       XML  PARSE             Parse the seed
       BYTE TREMZ           * Up to end of statement
       CALL CKSTNM
       DCZ  @FAC              Check FAC for zero
       BS   GA3B6
       ST   >46,@FAC          0 < FAC < 1E14
       XML  VPUSH             Let FAC = X2*1E7+X1
       MOVE 8,G@RNDEM,@ARG        ARG = 1/1E7
       XML  FMUL                  FAC = X2+X1/1E7
       CALL GRINT                 FAC = X2
       MOVE 5,@FAC,V@RNDX2        FAC = X2
       MOVE 8,G@RNDEP,@ARG        ARG = 1E7
       XML  FMUL                  FAC = X2*1E7
       XML  SSUB                  FAC = X1
       MOVE 5,@FAC,V@RNDX1        FAC = X1
       XML  CONT   * Return       FAC = X1

GA3B6  DST  @FAC,V@RNDX2          FAC = 0
       DST  @FAC,V@RNDX1          FAC = 0
       XML  CONT   * Return

**************************************************************************
* RANDOMIZE given number seed value (uses GPL RAND function)
* (16K possible starting positions)
*
RANDOM EQU  >8378

RNDM1  DST  >4201,@FAC     FAC = >4201 (sign+exponent+1st digit fixed at 1)
       CLR  @FAC4          FAC4= >00
       CALL RNDMZ          Set up new seed..
       DATA RNDX1          ..for X1
       CALL RNDMZ          Set up new seed..
       DATA RNDX2          ..for X2
       XML  CONT           * Return X: 100000010000 <= X <= 124240012424
* '*'...fixed, '?'...variable:         *????***????         *????***????
* Get next 4 pseudorandom digits
*
RNDMZ  FETCH @FAC8         Fetch address of seed (MSB)
       FETCH @FAC9         Fetch address of seed (LSB)
       RAND 99             GPL Randomize returns 0<=RANDOM<=99
       ST   @RANDOM,@FAC2  >00<=FAC+2<=FF <--NO!! YES--> 0<=FAC+2<=99[63h]
       SRL  2,@FAC2        >00<=FAC+2<=3F <--NO!! YES--> 0<=FAC+2<=24[18h]
       RAND 99             GPL Randomize returns 0<=RANDOM<=99
       ST   @RANDOM,@FAC3  >00<=FAC+3<=FF <--NO!! YES--> 0<=FAC+3<=99[63h]
       SRL  2,@FAC3        >00<=FAC+3<=3F <--NO!! YES--> 0<=FAC+3<=24[18h]
       MOVE 5,@FAC,V*FAC8  Put in seed..max of >4201181800 = 12424
       RTN                 Return to caller

CKSTNM CEQ  >65,@FAC2      Error exit
       BS   ERRSNM
       RTN                 Return to caller

*--ALC for GPL RAND from Console ROM 0---------------------------------*
*----------------------------------------------------------------------*
*--- Generate A Pseudorandom Number -----------------------------------*
*----------------------------------------------------------------------*
WKSC   EQU  >83C0             ISR workspace (R0 = PRN SEED)
WKSE   EQU  >83E0             GPL workspace
R5LSB  EQU  WKSE+R5+R5+1      low byte of R5 of GPL WS
RANDOM EQU  >8378             return location for RAND's PRN return byte

RAND   LI   R4,>6FE5          28645 to R4
       MPY  @WKSC,R4          SEED * 28645
       AI   R5,>7AB9          PRN = SEED * 28645 + 31417
       MOV  R5,@WKSC          PRN is stored as new SEED in >83C0
       MOVB *R13,R6           load limit number (byte) from caller to R6
       SRL  R6,8              shift to LSB
       INC  R6                make modulus (lim+1)
       CLR  R4                clear upper half of dividend
       SWPB R5                swap bytes of PRN to get PRN'
       DIV  R6,R4             perform division by modulus (lim+1)
       MOVB @R5LSB,@RANDOM    store LSB of remainder in >8378
       B    @NEXT             return to caller for next GPL instruction

 

 

...lee

 

  • Like 6
  • Thanks 2
Link to comment
Share on other sites

3 hours ago, Gary from OPA said:

Are you going to compare the difference in randomizer math between like TI Basic and XB and other Basic's like Myarc, etc.

 

More or less—I have in mind to make some sort of comparison with TI Basic’s PRNG, which is discussed here, and TI Forth and fbForths PRNGs. As to the Geneve ABasics PRNG, it is virtually identical to XB, with GPL code converted to ALC. I understand per @InsaneMultitasker that Myarc Basic II was the source for Abasic, in which case the PRNG is certainly the same. I would need that source code to be sure. That is, currently, the extent of my comparison plans.

 

...lee

  • Like 2
  • Thanks 1
Link to comment
Share on other sites

11 hours ago, Lee Stewart said:

It is my intention, here, to explain the details of the pseudorandom number generator (PRNG) in TI Extended Basic (XB). Over the next few days, I will expand this first post to include my analysis to date in an effort to make it as nearly complete as I am able. I will also make any updates relevant to further insights from the ensuing discussion:

 

XB’s PRNG is a linear congruential  generator (LCG) with the following recurrence equation:

 

xnew = (ax + c) mod m                                                                                                                         (1)

where

x = current seed, which is initialized at power-up to 335212624223

a = 14389820420821

c = 21132486540519

m = 1014

xnew = pseudorandom number (PRN) sought and the new seed for the next go-round.

 

The above converts the above PRNG to the following equation:

 

xnew = (14389820420821x + 21132486540519) mod 1014                                                           (2)

 

To preserve precision for the radix-100, floating point calculations, all of the above numbers were split into a least-significant half (LSH), with subscript 1, and a most-significant half (MSH), with subscript 2, such that

 

x = 107x2 + x1

a = 107a2 + a1

c = 107c2 + c1

 

which converts (1) to

 

xnew = ((107a2 + a1)(107x2 + x1) + 107c2 + c1) mod 1014

xnew = (1014a2x2 + 107a2x1 + 107a1x2 + a1x1 +107c2 + c1) mod 1014                                             (3)

 

At the very least, the first term drops out because

 

1014a2x2 mod 1014 = 0

 

simplifying (3) to

 

xnew = (107a2x1 + 107a1x2 + a1x1 +107c2 + c1) mod 1014                                                                  (4)

 

...lee

 

It's nice to have a smart person around the house. :) 

That's really neat to see that explained so clearly. 

 

Your work gives me the impression that this should be a very good PRNG. 

My burning question:  When we dumb it all down to a >300 byte screen position or a random direction vector,

is there a way to know that the BASIC or XB PRNGs would provide a material benefit versus a simpler solution?

 

(You helped me with a simple analysis tool a few years back, but I am a humble business weanie so the amount of rigour involved was slightly less.

One might say it was completely devoid of rigour.) :) 

 

  • Like 2
Link to comment
Share on other sites

4 hours ago, Vorticon said:

It would be good to also include UCSD Pascal in the comparison. Not sure what type of randomizer function it uses. I can help with the test code.

 

The principal reason for this thread is to explain what XBs PRNG is and how it is calculated—mainly, because there seems to have been a lot of misunderstanding over the years about it. I (we) will certainly be comparing it to other PRNGs, but it is not the primary purpose.

 

...lee

  • Like 3
Link to comment
Share on other sites

2 minutes ago, Lee Stewart said:

 

The principal reason for this thread is to explain what XBs PRNG is and how it is calculated—mainly, because there seems to have been a lot of misunderstanding over the years about it. I (we) will certainly be comparing it to other PRNGs, but it is not the primary purpose.

 

...lee

Maybe add some timing also long doing a rnd or randomize in original XB takes for those using it often in their games.

 

One final thought does converting your XB program into compiled XB to run faster does it change the functionality in anyway or is it still the same PRNG math magic.

  • Like 2
Link to comment
Share on other sites

2 hours ago, Gary from OPA said:

Maybe add some timing also long doing a rnd or randomize in original XB takes for those using it often in their games.

One final thought does converting your XB program into compiled XB to run faster does it change the functionality in anyway or is it still the same PRNG math magic.

The RND in BASIC is over 5x faster than RND in XB. Both RXB and XB2.9 G.E.M. now use the faster TI BASIC random number routine. I have a feeling that it may not be as random as the XB one though.

Random numbers in a compiled XB program are different than they are in a BASIC/XB program. Compiled code can only use integer arithmetic. That is a problem, because in BASIC a random number is from 0 to .9999999

The work around is to multiply the random number by another number. i.e. instead of:

10 IF RND<.5 THEN 100 ELSE 200    you would use

10 IF RND*2<1 THEN 100 ELSE 200      

RND*2 gives a random integer from 0 to 1

RND*6 gives a random integer from 0 to 5

and so on....

Edited by senior_falcon
  • Like 2
Link to comment
Share on other sites

9 minutes ago, adamantyr said:

Interesting. That's basically the same style RND generator that Tombstone City had. A quick test in Classic99 though shows it avoids the "even/odd" alternating, which is nice.

yes, I read your blog last Saturday, as well, as this whole topic about "random" started during last zoom call thingy, and got into it for a bit. But it is good to see how it all works out, looking forward to Lee updating his first post with more info. I used similar RND code in my "3d string art" program I released the other day, which is similar to the math in Tombstone City as well, with the fixed integer math values.

 

http://www.adamantyr.com/blog/index.php/2018/08/30/random-thoughts/

Edited by Gary from OPA
  • Like 2
Link to comment
Share on other sites

3 hours ago, TheBF said:

It's nice to have a smart person around the house. :) 

That's really neat to see that explained so clearly. 

 

Thank you.  :)

 

3 hours ago, TheBF said:

Your work gives me the impression that this should be a very good PRNG. 

 

Well, you might be disappointed. It may well be good enough for things TI-99/4A, but LCGs are, generally, not considered good PRNGs, anymore. Here is a comment made by the authors (W. H. Press, S. A. Teukolsky, W. T. Vetterling, B. P. Flannery) of Numerical Recipes: The Art of Scientific Computing, 3rd Edition, 2007, in Chapter 7, “Random Numbers”

 

With hindsight, it seems clear that the whole field of random number generation was mesmerized, for far too long, by the simple recurrence equation

Ij+1 = aIj  +  c    (mod m)                                          (7.1.1)

 

You can read the entire chapter (or book, for that matter) at the Numerical Recipes website. The above-referenced chapter as well as Chapter 3, “Random Numbers” in D. E. Knuths The Art of Computer Programming, Volume 2: Seminumerical Algorithms, 3rd Edition, 1998, are very good, comprehensive discussions and references on the subject.

 

3 hours ago, TheBF said:

My burning question:  When we dumb it all down to a >300 byte screen position or a random direction vector,

is there a way to know that the BASIC or XB PRNGs would provide a material benefit versus a simpler solution?

 

There are, certainly, tests for assessing the quality of PRNGs, but, again, my primary mission in this thread is to explain the XB PRNG.

 

...lee

  • Like 1
  • Thanks 1
Link to comment
Share on other sites

Well you can add XB3 to that list of ways to get RND working I am currently disassembling all of XB3 GPL GROMs and ROMs.

 

Reposting my times:


90 ! ROUTINE SET UP SPRITES TEST SO NOT PART OF TIMING ROUTINE
100 CALL CLEAR
110 OPEN #1:"CLOCK"
120 INPUT #1:A$,B$,C$
130 FOR C=1 TO 10000
140 ! ROUTINE INSERTED HERE TO TEST
150 NEXT C
160 INPUT #1:D$,E$,F$
170 PRINT A$,D$:B$,E$,C$,F$
180 END 

   XB       XB3     XBGEM   MyarcXB2   RXB   command
-------------------------------------------------------------------------
  11:5      9:10     3:11      5:6     3:21  CALL CHAR(65,"FFFFFFFF")
2:57:15    34:18     5:33      6:19    4:42  CALL CHAR(65,Z$) ! LENGTH 64
  11:32     7:19     4:1       5:14    4:5   CALL CHARPAT(65,Z$)
  32:44     33:6  1:11:47      5:1     4:8   CALL CHARSET
   3:49     3:29     3:43      8:48    3:41  CALL CLEAR
   6:25     6:25     6:24      7:22    6:19  CALL COINC(#1,20,20,8,Z)
   3:0      3:0      3:0       4:3     3:4   CALL COINC(ALL,Z)
   4:33     4:15     4:15      5:4     4:24  CALL COLOR(1,2,8)
   3:9      3:9      3:9       4:6     2:36  CALL DELSPRITE(#1)
   6:22     6:23     6:22      4:18    5:33  CALL DELSPRITE(ALL)  
   6:5      6:6      6:6       6:0     6:1   CALL DISTANCE(#1,20,20,Z)
   6:6      3:7      4:10      5:9     4:2   CALL GCHAR(1,1,Z)
   4:11     3:1      4:5       5:5     4:17  CALL HCHAR(1,1,65)
4:37:35  4:39:17     9:31     14:12    7:33  CALL HCHAR(1,1,65,768)
   5:0      3:2      5:6       6:43    5:7   CALL JOYST(1,X,Y)
   4:37     3:25     4:38      5:16    5:2   CALL KEY(1,K,S)
   4:4      4:4      4:4       5:13    4:17  CALL LOCATE(#1,20,20)
   2:17     2:11     2:14      3:45    2:14  CALL MAGNIFY(1)
   4:44     4:44     4:44      6:42    4:41  CALL MOTION(#1,20,20)  
   3:8      3:17     3:17      5:27    3:4   CALL PATTERN(#1,66)
   5:22     5:22     5:31      5:25    5:9   CALL POSITION(#1,X,Y)
   3:44     2:11     2:6       4:3     2:17  CALL SCREEN(5)
   5:9      5:8      5:9       6:30    5:7   CALL SPRITE(#1,65,2,10,10)
   3:55     3.0      4:5       5:5     4:17  CALL VCHAR(1,1,65)
4:30:22  4:30:41    18:29     18:11   21:3   CALL VCHAR(1,1,65,768)
  10:2      8:13    10:1       6:18   10:3   DISPLAY AT(9,9):C
  21:10    13:41    11:49     11:29   12:13  DISPLAY AT(9,9):RND
   8:22     6:43     8:22      6:17    8:21  DISPLAY AT(9,9):"TEST"
  12:12     5:3      3:8       7:16    3:9   A=RND
******************************************************************************
* RXB COMMANDS
 DISPLAY  DISPLAY  DISPLAY  DISPLAY    5:6   CALL HPUT(11,11,C)
 DISPLAY  DISPLAY  DISPLAY  DISPLAY    7:12  CALL HPUT(11,11,RND)
 DISPLAY  DISPLAY  DISPLAY  DISPLAY    4:29  CALL HPUT(11,11,"TEST")
  14:17    10:10    15:42     16:16   11:50  CALL JOYLOCATE(1,X,Y,8,8,#1,RW,CL,K) GOTO 
  15:53    10:20    15:3      16:1    10:2   CALL JOYMOTION(1,X,Y,#1,9,9,K) GOTO
   7:7      5:11     7:7      11:3     9:33  CALL ONKEY("ABC",1,K,S) GOTO 1,2,3
   N/A      N/A      N/A       N/A     7:26  CALL COLLIDE(#1,20,20,8,X,Y)
   N/A      N/A     10:31      N/A    11:39  CALL MOVES("RR",1024,8192,12288) 
******************************************************************************
80 ! JOYLOCATE FOR OTHERS   
90 CALL SPRITE(#1,65,2,20,20) :: XL,YL=20
100 CALL CLEAR
110 OPEN #1:"CLOCK"
120 INPUT #1:A$,B$,C$
130 FOR C=1 TO 10000
140 CALL JOYST(1,X,Y) :: CALL LOCATE(#1,X+XL,Y+YL) :: CALL KEY(1,K,S) 
:: IF K=18 THEN 150
150 NEXT C
160 INPUT #1:D$,E$,F$
170 PRINT A$,D$:B$,E$,C$,F$
180 END
*****************************************************************************
80 ! JOYMOTION FOR OTHERS
90 CALL SPRITE(#1,65,2,20,20) :: XL,YL=20
100 CALL CLEAR
110 OPEN #1:"CLOCK"
120 INPUT #1:A$,B$,C$
130 FOR C=1 TO 10000
140 CALL JOYST(1,X,Y) :: CALL MOTION(#1,X*9,Y*9) :: CALL KEY(1,K,S) 
:: IF K=18 THEN 150
150 NEXT C
160 INPUT #1:D$,E$,F$
170 PRINT A$,D$:B$,E$,C$,F$
180 END
****************************************************************************
80 ! ONKEY FOR OTHERS
90 CALL SPRITE(#1,65,2,20,20) :: XL,YL=20
100 CALL CLEAR
110 OPEN #1:"CLOCK"
120 INPUT #1:A$,B$,C$
130 FOR C=1 TO 10000
140 CALL KEY(1,K,S) :: IF K=65 THEN 150 ELSE IF K=66 THEN 150 ELSE IF 
K=67 THEN 150 ELSE 150
150 NEXT C
160 INPUT #1:D$,E$,F$
170 PRINT A$,D$:B$,E$,C$,F$
180 END
****************************************************************************

 

Edited by RXB
  • Like 2
Link to comment
Share on other sites

21 hours ago, Lee Stewart said:

Soon, I will post the XB GPL source code for RND accompanied by a translation to ALC.

 

Posted the GPL code with additional comments by yours truly—no ALC translation, yet.

 

...lee

  • Like 4
Link to comment
Share on other sites

I just discovered a bug in XB’s RANDOMIZE. There is no check for passing it a negative number. If you pass it any negative number, it screws up the seed such that the first call to RND computes the next seed to be 0 and returns 0 to the caller. This happens because the first thing RANDOMIZE does after being passed a value, is to overstore the sign+exponent byte with >46, which, for a positive number, merely forces the mantissa to be an integer, regardless of whether the passed number had a fractional part, but for a negative number, the first word (2 bytes!) must be dealt with or strange things will happen. In this case, the first byte of the mantissa is now greater than 99 (>63)—an illegal radix-100 digit.

 

I will post a brief (?) discussion of this bug and a fix for my ALC port of the GPL after I finish the code. I just thought I should post it here before I forgot it.

 

...lee

  • Like 5
Link to comment
Share on other sites

I have completed the translation of XB’s GPL code for RND and RANDOMIZE. I have also tested it and it duplicates XB’s execution. The ALC is 722 bytes so it is definitely too big for the fbForth 3.0 kernel, even if split up over several banks—oh, well. RND is about 3.3x faster than XB’s GPL code.

 

Eventually, I will post the code in the first post along with a discussion, but, for now, I will just post it here for your delectation:

Spoiler
*******************************************************************
* PRNG_FBF exposes 5 functions as ALC equivalents for XB's        *
*     GPL code:                                                   *
* opcode fxn     XB fxn  description                              *
* ------ ------  ------- ---------------------------------------- *
*    0   NRND....NRND....GPL code called by RND                   *
*    1   RNZNEW..NRNDMZ..GPL code called by RANDOMIZE <user seed> *
*    2   RNZOLD..NRNDMZ..GPL code called by RANDOMIZE             *
*    3   SEEDIN..Initialize FP seed (done at XB startup           *
*    4   RND99...RAND 99..Console ROM ALC for GPL "RAND 99"...    *
*           ...returns "random" 1-digit centimal number in R0 MSB *
*******************************************************************

*******************************************************************
* Necessary EQUates
*
FPSTK  EQU  R13      FP stack pointer
RSTK   EQU  R15      return stack pointer
FPSTK0 EQU  >8378    bottom of FP stack in scratchpad RAM (grows up)
RSTK0  EQU  >83BE    bottom of return stack in scratchpad RAM (grows down)

FADD   EQU  >0D80    console ROM FP ADDition subroutine
FSUB   EQU  >0D7C    console ROM FP SUBtraction subroutine
FMUL   EQU  >0E88    console ROM FP MULtiplication subroutine
FAC    EQU  >834A
ARG    EQU  >835C
GPLWS  EQU  >83E0

* RNDX1, RNDX2, SAVWP are at top of low-level support RAM for fbForth,
* just above system PPBs. If using for some other system, these can
* be anywhere in modifiable RAM.
RNDX1  EQU  >3C1A    PRNG seed (lower half)
RNDX2  EQU  >3C22    PRNG seed (upper half)
PRNGWS EQU  >3C2A    PRNG workspace (only using R13,R14,R15)

       AORG >A032    HERE at fbForth 3.0 startup..
*++                  ..put wherever is convenient

******************************************************************
*++ PRNG: Vector for BLWP to the FP PRNG functions.
*++   Input...R0: opcode
*++
*++   Called with
*++      BLWP @PRNG
*
PRNG   DATA PRNGWS,PRNGEN
*
*++ Entry point, PRNGEN, and exit, PRNGEX, are farther below to 
*++ facilitate JuMPs over Branches.
*++
******************************************************************
* Subroutine call table:
*
PRNGTB DATA NRND,RNZNEW,RNZOLD,SEEDIN,RND99
*++ opcode:  0     1      2      3      4
*++
*******************************************************************
* SPUSH pushes 8 bytes (usually an FPN) from FAC to Scratchpad    *
* stack, pointed to by FPSTK. There is only room for 4 FPNs. The  *
* user must check for overflow/underflow.                         *
*******************************************************************
*     FPSTK: FP stack pointer
*
SPUSH  DECT RSTK           reserve return stack space
       MOV  R11,*RSTK      save return
       AI   FPSTK,8        reserve FP stack space
       LI   R0,FAC         source (FAC)
       MOV  FPSTK,R1       destination
       BL   @FPMOVE        copy FAC to top of stack
       MOV  *RSTK+,R11     pop return
       RT                  return to caller

*******************************************************************
* SPOP pops 8 bytes (usually an FPN) from Scratchpad stack,       *
* pointed to by FPSTK, to FAC. There is only room for 4 FPNs. The *
* user must check for overflow/underflow.                         *
*******************************************************************
*     FPSTK: FP stack pointer
*
SPOP   DECT RSTK           reserve return stack space
       MOV  R11,*RSTK      save return
       MOV  FPSTK,R0       source
       LI   R1,FAC         destination
       BL   @FPMOVE        copy top of stack to FAC
       AI   FPSTK,-8       pop FP stack
       MOV  *RSTK+,R11     pop return
       RT                  return to caller

*******************************************************************
* SSUB first negates FPN in FAC, then proceeds with SADD. SADD    *
* pops FP value from FP stack to ARG and runs FADD to add FAC to  *
* ARG. Result is in FAC: ARG-FAC if SSUB or ARG+FAC if SADD.      *
*******************************************************************
*
SSUB   NEG  @FAC           negate FP number in FAC
SADD   DECT RSTK           reserve return stack space
       MOV  R11,*RSTK      save return
       MOV  FPSTK,R0       source
       LI   R1,ARG         destination
       BL   @FPMOVE        copy top of stack to ARG
       AI   FPSTK,-8       pop FP stack
       BL   @FADD          console ROM FP addition subroutine
       MOV  *RSTK+,R11     pop return
       RT                  return to caller

*******************************************************************
* INTPOS truncates a positive FPN in FAC. Zero or negative FPNs   *
* remain unchanged.                                               *
*     Exponent Fraction Bytes                                     *
*     -------- --------------                                     *
*     > 46h       0                                               *
*     = 46h       0                                               *
*     < 46h       1..7                                            *
*******************************************************************
*     uses R0,R1
*
INTPOS MOVB @FAC,R0        get sign+exp byte..negative?
       JLT  INTPSX         yes..we're outta here
       SRL  R0,8           to LSB, clearing MSB
       LI   R1,>0046       load exp comparison value
       S    R0,R1          fraction part?                R1=46h-exp
       JGT  INTPS1         yes..zero it                  
       RT                  no..return to caller
INTPS1 CI   R1,6           integer part?                 
       JGT  INTPS3         no..go zero number
       LI   R0,FAC+7       start at last byte
INTPS2 MOVB R1,*R0         zero next byte (R1 MSB = 0)
       DEC  R0             next byte left
       DEC  R1             done?
       JNE  INTPS2         no..zero another byte
       RT                  yes..return to caller
INTPS3 CLR  @FAC           return 0..no integer part
INTPSX RT                  return to caller

*******************************************************************
* FPMOVE copies 8 bytes from source in R0 to destination in R1.   *
* FRMFAC copies 8 bytes from FAC to destination passed via R11,   *
*        bumping R11 once.                                        *
* TOARG  copies 8 bytes from source passed via R11 to ARG,        *
*        bumping R11 once.                                        *
* TOFAC  copies 8 bytes from source passed via R11 to FAC,        *
*        bumping R11 once.                                        *
*******************************************************************
*     R0: source address
*     R1: destination address
*
FRMFAC LI   R0,FAC      FAC is source
       JMP  FPMV1       do it
TOARG  LI   R1,ARG      ARG is destination
       JMP  TOFAC1      get source address
       JMP  FPMOVE      do it
TOFAC  LI   R1,FAC      FAC is destination
TOFAC1 MOV  *R11+,R0    get source address
       JMP  FPMOVE      do it
FPMV1  MOV  *R11+,R1    get destination address
FPMOVE MOV  *R0+,*R1+   copy bytes 0,1
       MOV  *R0+,*R1+   copy bytes 2,3
       MOV  *R0+,*R1+   copy bytes 4,5
       MOV  *R0,*R1     copy bytes 6,7
       RT               return to caller

*******************************************************************
* Pseudorandom Number Generator                                   *
*******************************************************************
*                                                                 *
*     X[N+1] = (A*X[N]+C) MOD M;  RND = X/M                       *
*                                                                 *
*     where:  X = X2 * 1E7 + X1                                   *
*             A = A2 * 1E7 + A1                                   *
*             C = C2 * 1E7 + C1                                   *
*             M = 1E14                                            *
*                                                                 *
* Assumptions:                                                    *
*  (1) All numbers are integers; fractional parts are             *
*      truncated                                                  *
*  (2) If the variables listed below start in the ranges          *
*      specified. They will also end in the ranges specified      *
*                                                                 *
* Constants: 0 <= A2 <  5E6  ; 0 <= A1 < 5E6                      *
*            0 <= C2 <  1E7  ; 0 <= C1 < 1E7                      *
* Variables: 0 <= X2 <  1E7  ; 0 <= X1 < 1E7                      *
*            0 <= T1 <= 1E14 ; 0 <= T2 < 1E7                      *
*            0 <= T3 <= 1E14 ; 0 <= T4 < 1E7                      *
*                                                                 *
* Stack usage:                                                    *
*            Constant Refs      Constant Refs   Constant Refs     *
* +---------+      In/Out            In/Out          In/Out       *
* | STACK+4 | X2*A1(F)(H)       --    ----      --    ----        *
* +---------+                                                     *
* | STACK+3 |   T2 (C)(J)       --    ----      --    ----        *
* +---------+                                                     *
* | STACK+2 |   T1 (B)(D)      X1new (E)(N)     --    ----        *
* +---------+                                                     *
* | STACK+1 |old X1(A)(G)       T3   (K)(L)    X2new (M)(P)       *
* +---------+                                                     *
*******************************************************************
* NRND expects to be called from GPL workspace (>83E0).
*
* Compute new value for X1..save it in @RNDX1
*
*                          Stack usage....................................
*                                 FAC/ARG 
*                          Srefs  Contents               Stack
*                          -----  -----------------      -----------------
NRND   BL   @TOFAC                FAC = X1
       DATA RNDX1
       BL   @SPUSH         (A)    FAC = X1                 S:x1
       BL   @TOARG                ARG = A1
       DATA RNDA1
       BL   @FMUL                 FAC = X1*A1
       BL   @TOARG                ARG = C1
       DATA RNDC1
       BL   @FADD              T1=FAC = X1*A1+C1
       BL   @SPUSH         (B)    FAC = T1                 S:x1 t1
       BL   @TOARG                ARG = 1/1E7
       DATA RNDEM
       BL   @FMUL                 FAC = T1/1E7
       BL   @INTPOS            T2=FAC = INT(T1/1E7)
       BL   @SPUSH         (C)    FAC = T2                 S:x1 t1 t2
       BL   @TOARG                ARG = 1E7
       DATA RNDEP
       BL   @FMUL                 FAC = T2*1E7             S:x1 t1 t2
       AI   FPSTK,-8    hide T2 so T1 TOS for SSUB         S:x1 t1 {t2}
       BL   @SSUB       (D) X1new=FAC = T1-T2*1E7          S:x1 {t1 t2}
       BL   @FRMFAC               FAC = X1new
       DATA RNDX1
       BL   @SPUSH          (E)   FAC = X1new              S:x1 x1new {t2}
*
* Compute new value for X2, save it in @RNDX2
*
       BL   @TOFAC                FAC = X2
       DATA RNDX2
       CLR  @FAC+6         FAC6..FAC7 = >0000
       MOVB @FAC+6,@FAC+5        FAC5 = >00
       BL   @TOARG                ARG = A1
       DATA RNDA1
       BL   @FMUL                 FAC = X2*A1
       AI   FPSTK,8     expose T2 for later              S:x1 x1new t2
       BL   @SPUSH         (F)    FAC = X2*A1            S:x1 x1new t2 x2*a1
       AI   FPSTK,-24   hide top 3 FPNs on stack         S:x1 {x1new t2 x2*a1}
       BL   @SPOP          (G)    FAC = X1               S:{x1 x1new t2 x2*a1}
       AI   FPSTK,32    expose all four FPNs on stack    S:x1 x1new t2 x2*a1
       BL   @TOARG                ARG = A2
       DATA RNDA2
       BL   @FMUL                 FAC = X1*A2
       BL   @SADD           (H)   FAC = X2*A1+X1*A2        S:x1 x1new t2 
       BL   @TOARG                ARG = C2
       DATA RNDC2
       BL   @FADD                 FAC = X2*A1+X1*A2+C2
       BL   @SADD          (J) T3=FAC = X2*A1+X1*A2+C2+T2  S:x1 x1new 
       AI   FPSTK,-16   hide FPNs on stack                 S:{x1 x1new}
       BL   @SPUSH         (K)    FAC = T3                 S:t3 {x1new}
       BL   @TOARG                ARG = 1/1E7
       DATA RNDEM
       BL   @FMUL                 FAC = T3/1E7
       BL   @INTPOS            T4=FAC = INT(T3/1E7)
       BL   @TOARG                ARG = 1E7
       DATA RNDEP
       BL   @FMUL                 FAC = T4*1E7
       BL   @SSUB          (L) X2new=FAC = T3-T4*1E7       S:{x1new}
       BL   @FRMFAC               FAC = X2new
       DATA RNDX2
* 
* Compute new value for RND, leave it in FAC for return to caller
*     0 <= RND < 1
*
       BL   @TOARG                ARG = 1/1E7
       DATA RNDEM
       BL   @FMUL                 FAC = X2new/1E7
       BL   @SPUSH         (M)    FAC = X2new/1E7          S:x2new/1e7 {x1new}
       AI   FPSTK,8     expose X1new on stack              S:x2new/1e7 x1new
       BL   @SPOP          (N)    FAC = X1new              S:x2new/1e7 
       BL   @FMUL                 FAC = X1new/1E7
       BL   @FMUL                 FAC = X1new/1E14
       BL   @SADD          (P)RND=FAC = (X2new/1E7)+(X1new/1E14)
       JMP  PRNGEX      return to executive

***********************************************************
* Constants for the Random Number Routine
RNDA2  DATA >4301,>2B59,>5200,>0000    ; = 1438982
RNDA1  DATA >422A,>0815,>0000,>0000    ; = 0420821
RNDC2  DATA >4302,>0B20,>3000,>0000    ; = 2113248
RNDC1  DATA >4306,>3605,>1300,>0000    ; = 6540519
RNDEP  DATA >430A,>0000,>0000,>0000    ; = 1E7
RNDEM  DATA >3C0A,>0000,>0000,>0000    ; = 1/1E7
X2SEED DATA >4203,>2315,>0000,>0000    ; =   33521, X2 initial value
X1SEED DATA >4302,>3E2A,>1700,>0000    ; = 2624223, X1 initial value
***********************************************************

******************************************************************
* PRNGEN is the entry point for the 5 PRNG routines,
*     NRND: opcode = 0
*   RNZNEW: opcode = 1 and user's seed in FAC
*   RNXOLD: opcode = 2
*   SEEDIN: opcode = 3 initializes FP seed to XB startup value
*    RND99: opcode = 4 calls RAND99 internally
*
PRNGEN LIMI 0                 disable interrupts
       MOV  *R13,@GPLWS+2     save opcode from caller's R0 to GPLWS R1
       LWPI GPLWS             switch to GPL workspace
       MOV  R1,R1             negative?
       JLT  PRNGEX            yes..bad opcode
       CI   R1,4              no..too high?
       JGT  PRNGEX            yes..bad opcode
       LI   FPSTK,FPSTK0      initialize FP stack (grows up)
       LI   RSTK,RSTK0        initialize return stack (grows down)
       SLA  R1,1              make opcode an index
       MOV  @PRNGTB(R1),R1    get branch address of opcode
       B    *R1               do it
   
*******************************************************************
* SEEDIN run at system startup                                    *
* Initialize Seed for Pseudorandom Number Generator               *
*******************************************************************
*
SEEDIN LI   R0,X2SEED      source address
       LI   R1,RNDX2       destination address
       BL   @FPMOVE        copy startup seed X2
       LI   R0,X1SEED      source address
       LI   R1,RNDX1       destination address
       BL   @FPMOVE        copy startup seed X1
* Fall through to PRNGEX to clean up and return to caller...

******************************************************************
* PRNGEX: Clean up and return to caller
*
PRNGEX LI   FPSTK,>9800       restore R13
       LI   RSTK,>8C02        restore R15
       LWPI PRNGWS
       RTWP


************************************************************
*                   RANDOMIZE Statement (2 subroutines)
************************************************************
* RNZNEW expects to be called with the user-supplied seed in
* FAC. Place value is ignored in the input number.
* (99,000,000,000,001 possible starting positions)
*++ [LES note: Actually, 10^14 = 100,000,000,000,000 are
*++            possible, but who's counting?!]
*
RNZNEW MOV  @FAC,R0           FAC = 0?
       JEQ  GA3B6             yes..we're done
       JGT  RNZNW1            > 0..get on with it
       NEG  @FAC              first make it positive
RNZNW1 LI   R0,>4600          exponent for integer only
       MOVB R0,@FAC           0 < FAC < 1E14
       BL   @SPUSH            Let FAC = X2*1E7+X1           S:x2*1e7+x1
       BL   @TOARG                ARG = 1/1E7
       DATA RNDEM
       BL   @FMUL                 FAC = X2+X1/1E7
       BL   @INTPOS               FAC = X2
       BL   @FRMFAC               FAC = X2
       DATA RNDX2
       BL   @TOARG                ARG = 1E7
       DATA RNDEP
       BL   @FMUL                 FAC = X2*1E7              S:x2*1e7+x1
       BL   @SSUB              X1=FAC = X2*1E7+X1-X2*1E7    S:
       BL   @FRMFAC               FAC = X1
       DATA RNDX1
       JMP  PRNGEX     return to executive

GA3B6  MOV  @FAC,@RNDX2           FAC = 0
       MOV  @FAC,@RNDX1           FAC = 0
       JMP  PRNGEX     return to executive

**************************************************************
* RNZOLD will calculate a new seed from the old seed. It calls
* RAND99 twice each for X1 and X2. RAND99 is modified from GPL
* RAND to obviate needing to pass the max centimal digit as
* well as to include XB's "SRL R0,2" shift.
*     NOPE-->(16K possible starting positions)<--NOPE
* ACTUALLY-->(25^4 = 390,625 possible positions)<--ACTUALLY
*
*++ If TI had not added the "SRL R0,2" for each call to
*++ "RAND 99", it would have been 100^4 = 100,000,000
*
RANDOM EQU  >8378

RNZOLD LI   R0,>4201
       MOV  R0,@FAC     FAC = >4201 (sign+exponent+1st digit fixed at 1)
       CLR  @FAC+4      FAC+4..
       CLR  @FAC+6      ..FAC+7 = >00000000
       BL   @RNDMZ      Set up new seed..
       DATA RNDX1       ..for X1
       BL   @RNDMZ      Set up new seed..
       DATA RNDX2       ..for X2
       JMP  PRNGEX     return to executive
*                       ..Xnew = 100000010000..124240012424

* Get next 4 pseudorandom digits
*
RNDMZ  DECT RSTK        reserve return stack space
       MOV  *R11+,R1    Get address of seed-half destination for FPMOVE
       MOV  R11,*RSTK   save new return
       BL   @RAND99     GPL Randomize (max of 2 decimal digits)
       MOVB R0,@FAC+2   0<=FAC+2<=24[18h]
       BL   @RAND99     GPL Randomize (max of 2 decimal digits)
       MOVB R0,@FAC+3   0<=FAC+3<=24[18h]
       LI   R0,FAC      source
       BL   @FPMOVE     Put in seed half..max: >4201181800 = 12424
       MOV  *RSTK+,R11  pop return
       RT               return to caller

*--RAND99 is based on Console ROM 0's ALC for GPL RAND, with the
*--addition of the 2-bit right shift of XB's RNDMZ---------------
*----------------------------------------------------------------
*------ Generate A Pseudorandom, Centimal Number in R0 MSB ------
*----------------------------------------------------------------
WKSC   EQU  >83C0             ISR workspace (R0 = PRN SEED)
WKSE   EQU  >83E0             GPL workspace
R5LSB  EQU  WKSE+R5+R5+1      low byte of R5 of GPL WS

RND99  BL   @RAND99     perform GPL RAND 99 equivalent
       JMP  PRNGEX      return to executive

RAND99 LI   R4,>6FE5          28645 to R4
       MPY  @WKSC,R4          SEED * 28645
       AI   R5,>7AB9          PRN = SEED * 28645 + 31417
       MOV  R5,@WKSC          PRN is stored as new SEED in >83C0
       LI   R6,100            load radix-100 modulus       
       CLR  R4                clear upper half of dividend
       SWPB R5                swap bytes of PRN to get PRN'
       DIV  R6,R4             perform division by modulus (lim+1)
       MOVB @R5LSB,R0         LSB of remainder to MSB of R0
* XB comments say >00<=R0<=FF <--WRONG!! ACTUALLY--> 0<=R0<=99[63h]
       SRL  R0,2
* XB comments say >00<=R0<=3F <--WRONG!! ACTUALLY--> 0<=R0<=24[18h]
*++
*++ The above SRL will eventually be removed to get back to maximum
*++ possible centimal digits of 99. The SWPB may also be removed in
*++ favor of the "SRC R0,5" of TI Forth.

       RT      Return random centimal digit to caller, unfortunately,
*++            limited to 24 by someone who did not understand what
*++            "RAND 99" was actually doing! The XB comments indicate
*++            the programmers thought 99 was a seed for RAND, rather
*++            than the limit for the returned byte that it is!

       END

 

 

The ALC is extensively commented and hopefully fairly easy to understand, though that is not for me to say. At least, it should be easier to follow for those unfamiliar with GPL.

 

...lee

  • Like 2
Link to comment
Share on other sites

That's a monster. You spent some time on that I reckon. 

 

Do we have any idea if it would be better than say, a 32 bit integer PRNG scaled and converted to a float?

I know it would be a different sequence, but does the float version have any better characteristics?

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...