+Lee Stewart Posted April 23 Share Posted April 23 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: All numbers are integers. Fractional parts are truncated. If the variables listed below start in the ranges specified, they will also end in the ranges specified. Constants: 0 ≤ a2 < 5 x 106 0 ≤ a1 < 5 x 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 x 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 x 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 - 107 int(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 6 2 Quote Link to comment Share on other sites More sharing options...
Gary from OPA Posted April 23 Share Posted April 23 Are you going to compare the difference in randomizer math between like TI Basic and XB and other Basic's like Myarc, etc. 1 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted April 23 Author Share Posted April 23 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 fbForth’s PRNGs. As to the Geneve ABasic’s 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 2 1 Quote Link to comment Share on other sites More sharing options...
+Vorticon Posted April 23 Share Posted April 23 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. 3 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted April 23 Share Posted April 23 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.) 2 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted April 23 Author Share Posted April 23 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 XB’s 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 3 Quote Link to comment Share on other sites More sharing options...
Gary from OPA Posted April 23 Share Posted April 23 2 minutes ago, Lee Stewart said: The principal reason for this thread is to explain what XB’s 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. 2 Quote Link to comment Share on other sites More sharing options...
senior_falcon Posted April 23 Share Posted April 23 (edited) 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 April 23 by senior_falcon 2 Quote Link to comment Share on other sites More sharing options...
+adamantyr Posted April 23 Share Posted April 23 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. 1 Quote Link to comment Share on other sites More sharing options...
Gary from OPA Posted April 23 Share Posted April 23 (edited) 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 April 23 by Gary from OPA 2 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted April 23 Author Share Posted April 23 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. Knuth’s 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 1 1 Quote Link to comment Share on other sites More sharing options...
RXB Posted April 24 Share Posted April 24 (edited) 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 April 24 by RXB 2 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted April 26 Author Share Posted April 26 I updated the first post with Part 2. Soon, I will post the XB GPL source code for RND accompanied by a translation to ALC. ...lee 3 1 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted April 27 Author Share Posted April 27 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 4 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted Tuesday at 01:08 AM Author Share Posted Tuesday at 01:08 AM 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 5 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted 2 hours ago Author Share Posted 2 hours ago 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 2 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted 1 hour ago Share Posted 1 hour ago 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? Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.