Jump to content
IGNORED

TI Mini-Memory programs


Retrospect

Recommended Posts

Yes. I think auto motion would work. I was young and not very experienced. It was my first assembly project on the 4A. I whince at the code: using MPY to multiply by 32. Oh dear!

 

I always did my assembly code with a pen then tried it out. Rinse and repeat until it worked. Even now I tend to work things out away from the computer!

Link to comment
Share on other sites

It looks like TI BASIC with the MiniMemory is very well suited for using the text mode. According to the memory map in MG Explorer the VDP roll out area starts at >03C0, just missing the 960 byte long screen image table for text mode which would run from >0000 to >03BF, with no bytes needed for a color table. The crunch buffer from >0320 to >036F would not cause problems because you wouldn't be using INPUT when running a program. So there shouldn't be any conflicts in VDP memory use. The assembly subroutines would be fairly straightforward. You'd need ACCEPT AT, DISPLAY AT, COLOR, CLEAR. Windowing and scrolling might be nice but are not essential. This would be a good project for someone who was getting into assembly language and wanted to try their hand at writing some assembly language extensions.

(It is tougher to use text mode in XB because the Extended BASIC system area is from >0370 to >03BF which conflicts with the screen image table.)

Link to comment
Share on other sites

(It is tougher to use text mode in XB because the Extended BASIC system area is from >0370 to >03BF which conflicts with the screen image table.)

XXB (Barry Traver) pulls it off okay, though, I remember playing with that one a lot. :) I never looked at how it does it, but the docs do note:

 

40-column ("text") mode in Extended BASIC has advantages but also

limitations: you may have to experiment to discover what can and what cannot

safely be done (e.g.; you should avoid file operations while in text mode).

it also notes:

 

Warning: text mode is an area in

which the Extended BASIC programmer must walk with care. Two things to

remember: (1) You must use CALL LINK("GRAPH") or its equivalent in order to

return safely to graphics mode; and (2) any kind of error in text mode can

throw you into never-never land!

and

 

Many CALLs accessed while in text mode create a screen "glitch;" so CALL LINK

("KEYR";RESPONSE$) is provided to avoid that problem. (One limitation that

comes with working with text mode from Extended BASIC is that apparently all

CALLs to user-defined subprograms will cause such glitches.)

... which probably explains most of it right there. They just let it run and avoid functions that cause issues. :) I know I wrote a full sector editing system in XXB back in the day which used the 40 column mode - I loved it. :)

 

Attached on the off chance nobody has it, though it's off topic for this thread. ;)

XXB.DSK

  • Like 2
Link to comment
Share on other sites

Hmm there is a Text mode in XHI for XB using a 9938 that has 128K of VDP memory.

 

Funnel Web took advantage of this feature that could be loaded from EA, TI BASIC or from XB.

 

 

 

 

I am hopeful the Color Pallet gets fixed in MESS for the EVPC.

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

OK—I have TI Basic's RND in an ALC routine that can be called from TI Basic with

 

CALL LINK("RND",X)

 

where X is the name of the variable in which the user wants the random number returned. Harry is certainly right about the slowness of CALL.

 

Recall from post #9 the code for GPL's RAND, which returns an 8-bit random number such that 0 <= n <= max (max is the maximum 8-bit number to be returned):

 

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* GPL RAND *
* Uses GPL workspace. *
* First, generates an unsigned, 16-bit "random" number and *
* stores it at 83C0h. *
* Calculation for new seed is *
* ((seed * 6FE5h) & FFFFh + 7AB9h) & FFFFh *
* Then, generates an unsigned, 8-bit random number (RN) such that *
* 0 <= RN <= lim, where 0 <= lim <= 255 and supplied by caller. *
* Stores random number byte at 8378h to be retrieved by caller. *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
RAND LI R4,>6FE5 Generate random number
MPY @>83C0,R4 RN = seed * 6FE5h
AI R5,>7AB9 RN = (RN & FFFFh) + 7AB9h
MOV R5,@>83C0 Load random number seed = RN
*
* At this point, 83C0h contains an unsigned, 16-bit pseudo-random number
*
MOVB *R13,R6 Fetch limit (0-255) from next GROM byte
SRL R6,8 ...in Lbyte
INC R6 ...+1
CLR R4
SWPB R5 swap bytes of RN
DIV R6,R4 adjust to limit
MOVB @>83EB,@>8378 LSB of remainder in R5 to 8378h
*
* Return to GPL interpreter...

 

The following is the GPL for the TI Basic interpreter's RND, which returns a floating point random number such that 0 <= n < 1, i.e., a floating point fraction:

 

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Basic RND *
* Calls RAND at least 7 times, once for each radix-100, floating- *
* point digit. If first radix-100 digit is 0, exponent is re- *
* duced and RAND is called again. If the exponent ever gets to *
* 0, the first word (16 bits) of the floating-point result is *
* set to 0. *
* Result is built in the floating point accumulator (FAC = >834A). *
* *
* Command order (dst,src) below is from Heiner Martin *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
RND ST @>834A,>3F Exponent for 100^0
ST @>8310,>4B Loop counter (points to >834B)
RND01 RAND >63 call GPL RAND with lim = 99 (RAND byte to >8378)
CZ @>8378 0?
BR GROM@RND03 No, go on
DEC @>834A decrement exponent for next try
CZ @>834A 0?
BS GROM@RND04 End with 0 if exp = 0
BR GROM@RND01 Try again
RND02 RAND >63 call GPL RAND with lim = 99
RND03 ST *>8310,@>8378 store RAND in next radix-100 digit
CEQ @>8310,>51 Last digit? (>8351 is last byte of FAC)
BS GROM@RNDXIT Yes; we're outta here
INC @>8310 No; increase loop counter
BR GROM@RND02 Get next radix-100 digit
RND04 CLR @>834B Set 0; returns floating-point 0 for RND
RNDXIT CONT continue parsing Basic

 

The following is my humble submission for the CALL LINK("RND",X) suggested in my opening paragraph:

 

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Basic RND in ALC *
* Invoked by CALL LINK("RND",X) *
* Calls RAND at least 7 times, once for each radix-100, floating- *
* point digit. If first radix-100 digit is 0, exponent is re- *
* duced and RAND is called again. If the exponent ever gets to *
* 0, the first word (16 bits) of the floating-point result is *
* set to 0. *
* Result is built in the floating point accumulator (FAC = >834A). *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* REF NUMASG for E/A module
NUMASG EQU >6040 for MM module
DEF RND
FAC EQU >834A
RND MOV R11,R9 save return
LI R0,>3F Exponent for 100^0
LI R1,FAC+1 First radix-100 digit location
LI R2,7 load radix-100 digit counter
RND01 BL @RAND call our version of GPL RAND (result in R7)
CI R7,0 0?
JNE RND02 No, go on
DEC R0 decrement exponent for next try
JEQ RND05 End with 0 if exp = 0
JMP RND01 Try again
RND02 SWPB R0 exponent to MSB
MOVB R0,@FAC move exponent to FAC
JMP RND04
RND03 BL @RAND call our version of GPL RAND (result in R7)
RND04 SWPB R7 get result to MSB
MOVB R7,*R1+ store RAND in next radix-100 digit
DEC R2 decrement counter
JEQ RNDXIT If 0, we're outta here
JMP RND03 else, get next radix-100 digit
RND05 CLR @FAC Set 0; returns floating-point 0 for RND
RNDXIT CLR R0 Setup for numeric variable
LI R1,1 Indicate first (only) variable
BLWP @NUMASG Put RND in variable whose name was passed
B *R9 return to Basic
RAND LI R6,>6FE5 Generate random number
MPY @>83C0,R6 RN = seed * 6FE5h
AI R7,>7AB9 RN = (RN & FFFFh) + 7AB9h
MOV R7,@>83C0 Load random number seed = RN
*
* At this point, 83C0h contains an unsigned, 16-bit pseudo-random number
*
LI R8,100 divisor (result will be 0-99)
CLR R6
SWPB R7 swap bytes of RN
DIV R8,R6 adjust to limit (rem in R7)
RT
END

 

The above is for the MiniMemory module. It can be used with the E/A module by uncommenting the first line with ‘NUMASG’ and commenting out the second such line. The accompanying comments should make this obvious. To use it with the E/A module, you also need to LOAD “BSCSUP”, the TI Basic support routines located on one of the E/A diskettes. The following TI Basic program works for MiniMemory and runs the CALL LINK 500 times for timing:

 

100 CALL INIT
110 CALL LOAD("DSK1.RND.OBJ")
120 FOR I=1 TO 500
130 CALL LINK("RND",B)
140 NEXT I
150 END
I timed the above. Then, for comparison, I removed line 130 to get the baseline time. Then I changed 130 to
130 B=RND

 

for the normal TI Basic way to get a random number. The results were 12.6 ms/(B=RND) and 59.1 ms/[CALL("RND",B)]. I did this on Classic99, so the 32KiB is attached. I don't know how to eliminate the 32KiB expansion in Classic99 to test without it; but, I should think it would be the same.

 

The results were practically identical for the E/A module: 12.5 ms/(B=RND) and 61.9 ms/[CALL("RND",B)], with MiniMemory slightly faster on the CALL. The TI Basic program needs to LOAD the Basic Support Utilities (BSCSUP), but is otherwise the same as the one above:

 

100 CALL INIT
110 CALL LOAD("DSK1.BSCSUP","DSK1.RND.OBJ")
120 FOR I=1 TO 500
130 CALL LINK("RND",B)
140 NEXT I
150 END

As I said, Harry was right about the slow nature of CALL. Because my RND routine is 100% ALC, except for the CALL LINK and its return, CALL LINK is really, really slow. Again, that is right in line with what Harry said. I would now expect even a call to get effectively a single, 16-bit, random number (only one call to RAND) to be slower than TI Basic's RND * 32767! :-o I may still code it; but, it hardly seems worth the effort.

 

...lee

  • Like 3
Link to comment
Share on other sites

One thing that would help in generating random numbers is that usually you want it to be an integer between a low and a high limit. If you include the limits in the CALL LINK you could have the assembly routine do that for you and eliminate those steps in BASIC. Also you can generate up to 5 random numbers for each CALL LINK. (If you pass in 2 limits and pass back one random number) This is an option in XB256 and considerably speeds up the slow XB random number generator. Since BASIC has a faster RND it might not make so much of a difference there.

  • Like 1
Link to comment
Share on other sites

I am hopeful the Color Pallet gets fixed in MESS for the EVPC.

 

Rich, just watching your video ...

 

[3:50] "kind of blue does not look right" - What I see from your video looks good to me. The mix of blue and green is cyan. The only thing is that the cyan color of the emulated 9938 looks more blueish than the cyan of the emulated TMS9928a; this is what you already notice when starting the ti99_4ev. This should indeed be verified. Both video chip emulations were written by different people.

 

I'll try the tests on my real Geneve.

  • Like 1
Link to comment
Share on other sites

Strange. I tried my copy of XHi on the emulated Geneve and on the emulated TI-99/4A with EVPC. I get different results. I'll attach my copy to this message, so maybe you can try that one and we can rule out any differences in versions.

 

[4:30] Red sky: See the screenshot below.

 

[5:30] Rays: See screenshot below.

post-35000-0-12945300-1453551996_thumb.png

xhi.dsk

post-35000-0-63343600-1453552171_thumb.png

Edited by mizapf
  • Like 1
Link to comment
Share on other sites

One thing that would help in generating random numbers is that usually you want it to be an integer between a low and a high limit. If you include the limits in the CALL LINK you could have the assembly routine do that for you and eliminate those steps in BASIC.

 

Yeah, I thought of that; but, I'm not sure the multiple calls with BLWP @NUMREF would be significantly faster than managing the result in Basic.

 

Also you can generate up to 5 random numbers for each CALL LINK.

 

I'm not sure what you mean here. For one thing, my RND and TI Basic's RND both do the same thing in making 7 or more calls to the RAND function for each radix-100 digit (0 – 99) of the Basic floating point number that is ultimately returned. The difference is that the call for my function is within the same ALC routine whereas the TI Basic call is to GPL's RAND. There can be more than 7 calls if the first digit is 0. RAND is called until that digit is non-zero, the exponent getting decremented with each additional call.

(If you pass in 2 limits and pass back one random number) This is an option in XB256 and considerably speeds up the slow XB random number generator. Since BASIC has a faster RND it might not make so much of a difference there.

 

Probably so.

 

...lee

Link to comment
Share on other sites

I said:

Also you can generate up to 5 random numbers for each CALL LINK

Lee:

I'm not sure what you mean here. For one thing, my RND and TI Basic's RND both do the same thing in making 7 or more calls to the RAND function for each radix-100 digit (0 – 99) of the Basic floating point number that is ultimately returned. The difference is that the call for my function is within the same ALC routine whereas the TI Basic call is to GPL's RAND. There can be more than 7 calls if the first digit is 0. RAND is called until that digit is non-zero, the exponent getting decremented with each additional call.

Usually (not always) a programmer wants an integer random number between a lower and higher limit. Also the compiler only works with integers. After Rich modified RXB to use the faster TI BASIC random number generator, I did something similar to XB256. Often the lower limit is zero, so for speed I made that the default. (When I posted earlier I misremembered passing a lower limit.) From the manual:

 

CALL LINK(“IRND”,limit,variable[,.........])

Returns a random number as an integer from 0 to limit-1. The results are the same as

variable=INT(limit*RND) but IRND is much faster than the slow XB random

number generator. Up to 8 random numbers can be created with one call to IRND.

 

If you wanted a random number from 4 to 8 you would CALL LINK("IRND",5,R1)::R1=R1+4

 

To XB all numbers, even integers, are actually floating point numbers, but the IRND subroutine gets the limit, converts to integer, generates an integer random number, then converts it to floating point and sends it back to XB. It repeats this until there are no more arguments on the list. Because you can pass up to 16 arguments to/from an assembly subprogram you can get up to 8 random numbers per call. (Earlier I said 5 because I misremembered passing both lower and upper limits.)

Link to comment
Share on other sites

XXB (Barry Traver) pulls it off okay, though, I remember playing with that one a lot. :) I never looked at how it does it, but the docs do note:

 

 

I took a look at how Barry got 40 columns in XB. He starts the screen at >0000 which means that the XB system area at >0370 is part of the screen. If you look with the debugger you'll see activity in that area (>0370 to >03EF) when an XB program runs. You can also sometimes see junk displayed at the bottom of the screen. To avoid this you would have to move the screen image table. The best bet would be to reserve a block of VDP ram the way missing link or XB256 do. Another way might be to put the screen table into disk buffers. But either way it is not as simple as it would be with TI BASIC and the minimemory.

  • Like 1
Link to comment
Share on other sites

Usually (not always) a programmer wants an integer random number between a lower and higher limit. Also the compiler only works with integers. After Rich modified RXB to use the faster TI BASIC random number generator, I did something similar to XB256. Often the lower limit is zero, so for speed I made that the default. (When I posted earlier I misremembered passing a lower limit.) From the manual:

 

CALL LINK(“IRND”,limit,variable[,.........])

Returns a random number as an integer from 0 to limit-1. The results are the same as

variable=INT(limit*RND) but IRND is much faster than the slow XB random

number generator. Up to 8 random numbers can be created with one call to IRND.

 

If you wanted a random number from 4 to 8 you would CALL LINK("IRND",5,R1)::R1=R1+4

 

To XB all numbers, even integers, are actually floating point numbers, but the IRND subroutine gets the limit, converts to integer, generates an integer random number, then converts it to floating point and sends it back to XB. It repeats this until there are no more arguments on the list. Because you can pass up to 16 arguments to/from an assembly subprogram you can get up to 8 random numbers per call. (Earlier I said 5 because I misremembered passing both lower and upper limits.)

 

Cool!

 

You surely know this, but TI Basic numbers are all floating point, as well.

 

Could you not effectively get any desired number of random numbers by passing a 2-dimensional array to IRND? Of course, then you would need to modify IRND to handle it that way.

 

...lee

Link to comment
Share on other sites

Usually (not always) a programmer wants an integer random number between a lower and higher limit. Also the compiler only works with integers. After Rich modified RXB to use the faster TI BASIC random number generator, I did something similar to XB256. Often the lower limit is zero, so for speed I made that the default. (When I posted earlier I misremembered passing a lower limit.) From the manual:

 

CALL LINK(“IRND”,limit,variable[,.........])

Returns a random number as an integer from 0 to limit-1. The results are the same as

variable=INT(limit*RND) but IRND is much faster than the slow XB random

number generator. Up to 8 random numbers can be created with one call to IRND.

 

If you wanted a random number from 4 to 8 you would CALL LINK("IRND",5,R1)::R1=R1+4

 

To XB all numbers, even integers, are actually floating point numbers, but the IRND subroutine gets the limit, converts to integer, generates an integer random number, then converts it to floating point and sends it back to XB. It repeats this until there are no more arguments on the list. Because you can pass up to 16 arguments to/from an assembly subprogram you can get up to 8 random numbers per call. (Earlier I said 5 because I misremembered passing both lower and upper limits.)

I showed in another thread about RND in XB and TI Basic they both use Floating Point, but XB starts out using Floating Point and ends with Floating Point.

This just creates a huge speed difference when TI Basic just smokes XB RND for good reasons.

XB actually used a Floating Point Random Number Seed while TI Basic RND uses 99 a non floating point number as a seed.

You can see this in the GPL code of XB vs TI Basic.

Link to comment
Share on other sites

Strange. I tried my copy of XHi on the emulated Geneve and on the emulated TI-99/4A with EVPC. I get different results. I'll attach my copy to this message, so maybe you can try that one and we can rule out any differences in versions.

 

[4:30] Red sky: See the screenshot below.

 

[5:30] Rays: See screenshot below.

I lost my MESS set up and have to start over.

Link to comment
Share on other sites

... TI Basic RND uses 99 a non floating point number as a seed.

 

The seed for TI Basic is most definitely not 99. The number 99 is used as the limit for a radix-100, floating-point digit (must be 0 – 99) for each call to GPL's RAND. The new seed generated by RAND is an unsigned, 16-bit number, even though RAND returns nothing more than an 8-bit, unsigned number (0 – 255), depending on the limit it was passed. Obviously, TI Basic's RND gets back 0 – 99 for each radix-100 digit. TI Basic's RND calls RAND 7+ times, so there are at least 7 new seeds generated in succession.

 

...lee

Link to comment
Share on other sites

 

The seed for TI Basic is most definitely not 99. The number 99 is used as the limit for a radix-100, floating-point digit (must be 0 – 99) for each call to GPL's RAND. The new seed generated by RAND is an unsigned, 16-bit number, even though RAND returns nothing more than an 8-bit, unsigned number (0 – 255), depending on the limit it was passed. Obviously, TI Basic's RND gets back 0 – 99 for each radix-100 digit. TI Basic's RND calls RAND 7+ times, so there are at least 7 new seeds generated in succession.

 

...lee

LOL you want to argue with me about GPL Code?

Basic RND:
4F00 : ST @>834A,>3F Exponent
4F03 : ST @>8310,>4B Loop counter
4F06 : RAND >63 till 100 >>>>>>>>>>>>>>>> HEX >63 = DECIMAL 99
4F08 : CZ @>8378 0?
4F0A : BR GROM@>4F16 No, go on
4F0C : DEC @>834A -1
4F0E : CZ @>834A 0?
4F10 : BS GROM@>4F23 End with 0
4F12 : BR GROM@>4F06 Go on
4F14 : RAND >63 till 100  >>>>>>>>>>>>>>>> HEX >63 = DECIMAL 99
4F16 : ST *>8310,@>8378 All digits
4F1A : CEQ @>8310,>51 Till >8351
4F1D : BS GROM@>4F25
4F1F : INC @>8310 Increase loop counter
4F21 : BR GROM@>4F14
4F23 : CLR @>834B Set 0
4F25 : CONT

Edited by RXB
Link to comment
Share on other sites

Rich, if you followed the code back to the GPL interpreter you would find at >027A:

GPL RAND

LI 4,>6FE5 Generate random number

MPY @>83C0,4

AI 5,>7AB9

MOV 5,@>83C0 Load random number seed

MOVB *R13,6 Fetch limit

SRL 6,8 in Lbyte

INC 6 + 1

CLR 4

SWPB 5

DIV 6,4 (my notes: after DIV R5 has remainder which is < limit)

MOVB @>83EB,@>8378 Random number on source

JMP >02AA To GPL interpreter

Edited by senior_falcon
Link to comment
Share on other sites

Rich, if you followed the code back to the GPL interpreter you would find at >027A:

GPL RAND

LI 4,>6FE5 Generate random number

MPY @>83C0,4

AI 5,>7AB9

MOV 5,@>83C0 Load random number seed

MOVB *R13,6 Fetch limit

SRL 6,8 in Lbyte

INC 6 + 1

CLR 4

SWPB 5

DIV 6,4

MOVB @>83EB,@>8378 Random number on source

JMP >02AA To GPL interpreter

I just posted the GPL code not the Assembly.

99 or >63 is the Random number seed.

 

So are you saying that RAND >63 or 99 decimal is not the seed?

Are you saying there is no seed number?

Link to comment
Share on other sites

I just posted the GPL code not the Assembly.

99 or >63 is the Random number seed.

 

So are you saying that RAND >63 or 99 decimal is not the seed?

Are you saying there is no seed number?

Of course there is a random number seed. RAND >63 generates a random number integer between 0 and 99. If you read my post, the random number seed is at >83C0 according to Heiner Martin. But if you're like me you don't believe everything you read, so you put it to the test. Get into Classic 99 and press 1 for TI BASIC. Type in this program:

10 X=RND

20 CALL KEY(0,K,S)

30 IF S<1 THEN 20

40 GOTO 10

Get into the debugger and look at the CPU ram starting at >8300. Run the program. Every time you press a key the random number seed at >8C00 changes. Change line 10 to RANDOMIZE and then run the program. Every time you press a key the LSB of the random number seed changes.

Link to comment
Share on other sites

I just posted the GPL code not the Assembly.

99 or >63 is the Random number seed.

 

So are you saying that RAND >63 or 99 decimal is not the seed?

Are you saying there is no seed number?

That's not the seed. That's the limit. Learn the difference between a limit and a seed.

Link to comment
Share on other sites

OK—I have written an IRND function that will work with both MiniMemory and Editor/Assembler as is and will return up to 100 16-bit, integral, random numbers with one call. It requires just two parameters—a variable with the number of random numbers to be returned and an array of limits for each number to be returned. The random numbers are returned in the same array in which the limits were passed. It is limited to positive, 16-bit integers. You can pass a limit of 0 if you want a random number as large as possible (32767 or 7FFFh). The limits are destroyed by the function because the random numbers are stored in the same positions in the array. IRND is called like this:

 

CALL LINK("IRND",N,A())

 

N is the number of random numbers desired and A() is a one-dimensional array of limits for each random number desired. The array must be dimensioned at least as large as the first parameter, which cannot be higher that 100 for this particular function. This limit can be raised by changing the ARRAY BSS 800 instruction in the ALC below to reserve 8 bytes for each number in the array. I will modify this routine to allow an array of random numbers as large as memory will allow, but that will not require the above large block of memory. I expect it to be a slower routine because of the need for multiple calls for transferring 8 bytes at a time from and to VRAM as opposed to the transfers of the entire array from and to VRAM with only two transfer calls; but, it will take up much less memory.

 

For those interested, here is the ALC for the function:

 

 

 

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Basic IRND in ALC *
* Invoked by CALL LINK("IRND",N,X()) *
* *
* INPUT Workspace: *
* N = number of random numbers desired *
* X() = array of limits for each desired random number *
* R0-R4 = workspace *
* R5 = reserved for radix-100 (centimal) base *
* R6 = workspace *
* R7 = current random number limit etc. *
* R8 = CPU RAM array pointer *
* R9 = working copy of N *
* R10 = pointer to referenced parameters in VRAM *
* *
* OUTPUT Workspace: *
* X() = array of range-limited random numbers *
* *
* The input array, X(), of limits for N random numbers is processed *
* through this routine's pseudo-random number generator, IRND. All *
* numbers passed from TI Basic are assumed to be positive, 16-bit *
* integers in radix-100 (centimal) floating-point format. They are *
* converted to 16-bit integers by CPFI below, which forces them *
* positive. Any passed number exceeding 32767 is set to 32767. Each *
* random number generated by IRND is converted back to floating *
* point by CPIF below and stashed in X(), replacing the respective *
* limits, before returning to TI Basic. *
* *
* IRND uses the equation from TI Forth, the last step of which is *
* to do a 5-bit circular right shift to GPL RAND's routine before *
* storing at 83C0h (SEED). Because GPL's RAND only ever returns one *
* byte, it swaps bytes before adjusting to the limit passed to it. *
* The TI Forth equation, which is the new seed is *
* *
* SEED = (6FE5h * SEED + 7AB9h) src 5 *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
DEF IRND
VDPWA EQU >8C02 VDP Write Address
VDPRD EQU >8800 VDP Read Data
VDPWD EQU >8C00 VDP Write Data
FAC EQU >834A Floating Point Accumulator
R11SAV EQU >8352 Return address storage
VRAMAD EQU >8354 save pointer to VRAM
*
SGNBIT EQU >8000
CBD50 BYTE 50
EVEN
COUNT DATA 0 will be updated when N retrieved
ARRAY BSS 800 reserve space for 100 random numbers
IRND MOV R11,@R11SAV save return
MOV @>8310,R10 get parameter list pointer
AI R10,20 point to 1st parameter's pointer
LI R5,100 radix-100 (centimal) base for CPFI and CPIF fxns
*
** Get pointer to N in VRAM
*
MOV R10,R0 VRAM address of pointer to N
LI R1,VRAMAD use VRAMAD temporarily
LI R2,2 transfer 2 bytes
LI R3,VMBR set up to read from VRAM
BL @VDP do it
*
** Now get N
*
MOV @VRAMAD,R0 VRAM address of N
LI R1,FAC put in FAC
LI R2,8 transfer 8 bytes
BL @VDP do it
*
** Convert N to an integer
*
BL @CPFI Convert it
MOV @FAC,R9 copy integer to working register
MOV R9,@COUNT copy for later
*
** Get pointer to array in VRAM
*
AI R10,8 compute VRAM array address
MOV R10,R0 VRAM address of pointer to array
LI R1,VRAMAD use VRAMAD temporarily
LI R2,2 transfer 2 bytes
LI R3,VMBR set up to read from VRAM
BL @VDP do it
*
** Now get N limit values from passed array address
*
INCT @VRAMAD increment pointer past array dim
MOV @VRAMAD,R0 VRAM address of array
LI R1,ARRAY CPU buffer for array
MOV R9,R2 R9 still has count of FP numbers to transfer
SLA R2,3 X 8 bytes/FP number
LI R3,VMBR set up to read array from VRAM
BL @VDP to ARRAY
*
** Process array
*
LI R8,ARRAY load array pointer
ARLOOP LI R7,FAC copy to FAC
MOV *R8+,*R7+ from ARRAY
MOV *R8+,*R7+ .
MOV *R8+,*R7+ .
MOV *R8,*R7 .
BL @CPFI convert to integer
MOV @FAC,R7 limit integer to R7 for RAND
BL @RAND get a new random number to SEED and R1
MOV R1,@FAC prep for conversion to floating point
BL @CPIF do it
AI R8,-6 adjust R8 to overstore limit
LI R7,FAC copy from FAC
MOV *R7+,*R8+ to ARRAY
MOV *R7+,*R8+ .
MOV *R7+,*R8+ .
MOV *R7,*R8+ . R8 now has address of next element
DEC R9
JNE ARLOOP
*
** Copy array values back to VRAM
*
MOV @VRAMAD,R0 VRAM address of array for Basic
LI R1,ARRAY CPU buffer address of results
MOV @COUNT,R2 count of FP numbers to transfer
SLA R2,3 X 8 bytes/FP number
LI R3,VMBW set up to write array from ARRAY
BL @VDP to VRAM
*
** Return to Basic
*
MOV @R11SAV,R11 restore Basic return address
RT return to Basic
*
*== Generate a pseudo-random number ===================================
*
RAND LI R0,>6FE5 Generate random number
MPY @>83C0,R0 RN = seed * 6FE5h
AI R1,>7AB9 RN = (RN & FFFFh) + 7AB9h
SRC R1,5 RN = ((RN & FFFFh) + 7AB9h) src 5
MOV R1,@>83C0 Load random number seed = RN
ABS R1 We only want to process positive numbers
CLR R0
DIV R7,R0 adjust to limit (rem in R1)
RT
*
*== VDP utilities (entry point) =======================================
*
* R3 has address of VDP routine to execute
* R0..R2 have required parameters (see routine called)
*
VDP MOV R11,R4 save return
STST R7 store status
ANDI R7,>000F only want interrupt mask
LIMI 0 disable interrupts
B *R3 branch to VDP routine
*
** Return to caller
*
VDPRET LIMI 0 assume interrupts were disabled
CI R7,0 were they?
JEQ VDPRT1 yes, we're done
LIMI 2 no, enable interrupts
VDPRT1 B *R4
*
** VDP multiple byte write
*
VMBW BL @WVDPWA Write out address
VWTMOR MOVB *R1+,@VDPWD Write a byte
DEC R2 Decrement byte count
JNE VWTMOR More to write?
JMP VDPRET Return to calling program
*
** VDP multiple byte read
*
VMBR BL @WVDPRA Write out address
VRDMOR MOVB @VDPRD,*R1+ Read a byte
DEC R2 Decrement byte count
JNE VRDMOR More to read?
JMP VDPRET Return to calling program
*
** Set up to write to VDP
*
WVDPWA ORI R0,>4000
*
** WRITE VDP ADDRESS (reading VRAM if WVDPWA not executed)
*
WVDPRA SWPB R0 LSB first
MOVB R0,@VDPWA Write low byte of address
SWPB R0 Now MSB
MOVB R0,@VDPWA Write high byte of address
RT Return to calling routine
*-------------------------------------------------------------------------
* Code derived from the Geneve MDOS L10 Floating Point Library's CFI
*
* NAME: CPFI: Convert Positive Floating point number to Integer
*
* Result: 0 <= Integer <= 32767
*
* INPUT Workspace:
* R0 = Integer result
* R1 = radix-100 digit processing
* R2 = FAC pointer
* R3 = FP fraction processing
* R4 = FP exponent for comparison
* R5 = radix-100 (centimal) base (set up by caller)
* R6 = current workspace index
* FAC = Floating-point number
*
* OUTPUT Workspace:
* FAC = Integer
*-------------------------------------------------------------------------
*
* Called by BL @CPFI
*
CPFI LI R2,FAC get pointer to number
ABS *R2 force FP number positive
JEQ CFI11 if 0, all done
CLR R0 start with zero result
CLR R3 clear fraction
CLR R4 clear out unused part of register
MOVB *R2+,R4 get exponent
STWP R6 use R6 for indexing into workspace
CI R4,>3F00 is number less than one
JLT CFI11 yes number < .01 - result = 0
JEQ CFI03 number > .01 and < 1 - result = 1
CI R4,>4100 is number less than 100,000?
JLT CFI02 it is between 1 and 100
JEQ CFI01 it is between 100 and 10,000
CI R4,>4200 is number too big to convert?
JH CFI08 yes, it is
MOVB *R2+,@1(R6) get MS-centimal-digit to LSB of R0
MPY R5,R0 radix to binary
MOV R1,R0 get result for next centimal digit
CFI01 MOVB *R2+,@7(R6) get next integral centimal digit to LSB of R3
A R3,R0 add to previous result
MPY R5,R0 radix to binary
MOV R0,R0 test for overflow
JNE CFI08 yes--cap to 32767
MOV R1,R0 get result for last digit
JLT CFI08 overflow--cap to 32767
CFI02 MOVB *R2+,@7(R6) get least integral centimal digit to LSB of R3
A R3,R0 add to result
CFI03 CB *R2,@CBD50 is rounding necessary?
JLT CFI06 no--check for overflow
*
*
INC R0 round up
CFI06 CI R0,SGNBIT did unsigned result exceed 32767?
JL CFI11 no--we're good
CFI08 LI R0,>7FFF now just finish up with 32767
*
*
CFI11 MOV R0,@FAC return number in FAC
RT
*-------------------------------------------------------------------------
* Code derived from the Geneve MDOS L10 Floating Point Library's CIF
*
* NAME: CPIF: Convert Positive Integer to Floating point number
*
* INPUT Workspace:
* R0 = Dividend|quotient in radix-100-digit-processing
* R1 = Remainder in radix-100-digit processing
* R3 = Exponent processing
* R4 = FAC pointer
* R5 = radix-100 (centimal) base (set up by caller)
* R6 = FAC pointer for clearing FAC
* FAC = Positive integer
*
* OUTPUT Workspace:
* FAC = Floating-point number
*-------------------------------------------------------------------------
*
* Called by BL @CPIF
*
CPIF LI R4,FAC get address of FAC (integer in, FP out)
MOV *R4,R0 get the integer before we clobber it
JEQ CIF03 if zero, then done
LI R6,FAC for clearing (see below)
*
CLR *R6+ and clear out FAC
CLR *R6+ .
CLR *R6+ .
CLR *R6 .
*
ABS R0 force integer positive
LI R3,>40 starting exponent
C R0,R5 1 radix-100 digit?
JL CIF02 yup
CI R0,10000 nope--2 radix-100 digits?
JL CIF01 yup
INC R3 3 radix-100 digits--inc exponent
MOV R0,R1 extract radix-100 digit
CLR R0 .
DIV R5,R0 .
SWPB R1 .
MOVB R1,@3(R4) stash digit in FAC+3
CIF01 INC R3 inc exponent
MOV R0,R1 extract radix-100 digit
CLR R0 .
DIV R5,R0 .
SWPB R1 .
MOVB R1,@2(R4) stash digit in FAC+2
CIF02 SWPB R0 R0 has leftmost radix-100 digit
MOVB R0,@1(R4) stash digit in FAC+1
SWPB R3 exponent to MSB
MOVB R3,*R4 stash exponent in FAC
CIF03 RT
END

 

 

 

Here is a ZIP file with the ALC source and the object file in FIAD format and in a 90KiB disk image: IRND.zip

 

Here is a sample TI Basic program that calls IRND to get 100 random numbers. A() is populated with 10 groups of limits from 10 to 100, i.e., A(0) – A(9) are each 10, A(10) – A(19) are each 20, ... . The program gets the random numbers and prints them out:

 

100 CALL INIT
110 CALL LOAD("DSK1.IRND.OBJ")
120 DIM A(100)
130 N = 100
140 FOR I = 0 TO N-1 STEP 10
150 FOR J = I TO I+9
160 A(J) = (I/10+1)*10
170 NEXT J
180 NEXT I
190 RANDOMIZE
200 CALL LINK("IRND",N,A())
210 FOR I = 0 TO N-1
220 PRINT A(I);
230 NEXT I
240 PRINT
250 END

For what it's worth, the CALL LINK("IRND",N,A()) in the above program takes 2 seconds compared to 4.9 seconds for the equivalent A(J) = INT(RND*(I/10+1)*10) substituted in statement 160 (E/A cartridge in Classic99), not a huge savings; but, it is faster.

 

 

 

...lee

  • Like 4
Link to comment
Share on other sites

Here is the same function as last post, but this one allows an unlimited number of random numbers to be generated. Of course, memory will limit what can be returned. As long as you can store the array in TI Basic, you can fill it with random numbers. The memory footprint of the routine that follows is 432 bytes, whereas the one from the last post was 1268 bytes, most of which was storage for up to 100 floating point numbers:

 

 

 

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Basic IRND in ALC--unlimited version *
* *
* Invoked by CALL LINK("IRND",N,X()) *
* *
* INPUT Workspace: *
* N = number of random numbers desired *
* X() = array of limits for each desired random number *
* R0-R4 = workspace *
* R5 = reserved for radix-100 (centimal) base *
* R6 = workspace *
* R7 = current random number limit etc. *
* R8 = VRAM array pointer *
* R9 = working copy of N *
* R10 = pointer to referenced parameters in VRAM *
* *
* OUTPUT Workspace: *
* X() = array of range-limited random numbers *
* *
* The input array, X(), of limits for N random numbers is processed *
* through this routine's pseudo-random number generator, IRND, one *
* number at a time. The reason this version is termed "unlimited" *
* is that it transfers one FP number from and to VRAM for each call *
* to RAND. The other version transfers the entire array all at once *
* before and after any calls to RAND. It is, thus, limited to the *
* size of the "ARRAY BSS 800" instruction in that limited version. *
* This means, of course, that this unlimited version is much more *
* compact and, as it turns out, is just as fast. *
* *
* All numbers passed from TI Basic are assumed to be positive, 16- *
* bit integers in radix-100 (centimal) floating-point format. They *
* are converted to 16-bit integers by CPFI below, which forces them *
* positive. Any passed number exceeding 32767 is set to 32767. Each *
* random number generated by IRND is converted back to floating *
* point by CPIF below and stashed in X(), replacing the respective *
* limits, before returning to TI Basic. *
* *
* IRND uses the equation from TI Forth, the last step of which is *
* to do a 5-bit circular right shift to GPL RAND's routine before *
* storing at 83C0h (SEED). Because GPL's RAND only ever returns one *
* byte, it swaps bytes before adjusting to the limit passed to it. *
* The TI Forth equation, which is the new seed is *
* *
* SEED = (6FE5h * SEED + 7AB9h) src 5 *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
DEF IRND
VDPWA EQU >8C02 VDP Write Address
VDPRD EQU >8800 VDP Read Data
VDPWD EQU >8C00 VDP Write Data
FAC EQU >834A Floating Point Accumulator
R11SAV EQU >8352 Return address storage
VRAMAD EQU >8354 save pointer to VRAM
*
SGNBIT EQU >8000
CBD50 BYTE 50
EVEN
IRND MOV R11,@R11SAV save return
MOV @>8310,R10 get parameter list pointer
AI R10,20 point to 1st parameter's pointer
LI R5,100 radix-100 (centimal) base for CPFI and CPIF fxns
*
** Get pointer to N in VRAM
*
MOV R10,R0 VRAM address of pointer to N
LI R1,VRAMAD use VRAMAD temporarily
LI R2,2 transfer 2 bytes
LI R3,VMBR set up to read from VRAM
BL @VDP do it
*
** Now get N
*
MOV @VRAMAD,R0 VRAM address of N
LI R1,FAC put in FAC
LI R2,8 transfer 8 bytes
BL @VDP do it
*
** Convert N to an integer
*
BL @CPFI Convert it
MOV @FAC,R9 copy integer to working register
*
** Get pointer to array in VRAM
*
AI R10,8 compute VRAM array address
MOV R10,R0 VRAM address of pointer to array
LI R1,VRAMAD use VRAMAD temporarily
LI R2,2 transfer 2 bytes
LI R3,VMBR set up to read from VRAM
BL @VDP do it
*
** Process array
*
INCT @VRAMAD increment VRAM pointer to 1st array element
MOV @VRAMAD,R8 load pointer to working register
ARLOOP MOV R8,R0 VRAM address of next limit value
LI R1,FAC copy to FAC
LI R2,8 FP numbers are 8 bytes
LI R3,VMBR set up to read FP number from VRAM
BL @VDP to FAC
BL @CPFI convert to integer
MOV @FAC,R7 limit integer to R7 for RAND
BL @RAND get a new random number to SEED and R1
MOV R1,@FAC prep for conversion to floating point
BL @CPIF do it
MOV R8,R0 reload VRAM address of current array element
LI R1,FAC copy from FAC
LI R2,8 FP numbers are 8 bytes
LI R3,VMBW set up to write FP number to VRAM
BL @VDP from FAC
AI R8,8 increment VRAM pointer to next array element
DEC R9 decrement count
JNE ARLOOP
*
** Return to Basic
*
MOV @R11SAV,R11 restore Basic return address
RT return to Basic
*
*== Generate a pseudo-random number ===================================
*
RAND LI R0,>6FE5 Generate random number
MPY @>83C0,R0 RN = seed * 6FE5h
AI R1,>7AB9 RN = (RN & FFFFh) + 7AB9h
SRC R1,5 RN = ((RN & FFFFh) + 7AB9h) src 5
MOV R1,@>83C0 Load random number seed = RN
ABS R1 We only want to process positive numbers
CLR R0
DIV R7,R0 adjust to limit (rem in R1)
RT
*
*== VDP utilities (entry point) =======================================
*
* R3 has address of VDP routine to execute
* R0..R2 have required parameters (see routine called)
*
VDP MOV R11,R4 save return
STST R7 store status
ANDI R7,>000F only want interrupt mask
LIMI 0 disable interrupts
B *R3 branch to VDP routine
*
** Return to caller
*
VDPRET LIMI 0 assume interrupts were disabled
CI R7,0 were they?
JEQ VDPRT1 yes, we're done
LIMI 2 no, enable interrupts
VDPRT1 B *R4
*
** VDP multiple byte write
*
VMBW BL @WVDPWA Write out address
VWTMOR MOVB *R1+,@VDPWD Write a byte
DEC R2 Decrement byte count
JNE VWTMOR More to write?
JMP VDPRET Return to calling program
*
** VDP multiple byte read
*
VMBR BL @WVDPRA Write out address
VRDMOR MOVB @VDPRD,*R1+ Read a byte
DEC R2 Decrement byte count
JNE VRDMOR More to read?
JMP VDPRET Return to calling program
*
** Set up to write to VDP
*
WVDPWA ORI R0,>4000
*
** WRITE VDP ADDRESS (reading VRAM if WVDPWA not executed)
*
WVDPRA SWPB R0 LSB first
MOVB R0,@VDPWA Write low byte of address
SWPB R0 Now MSB
MOVB R0,@VDPWA Write high byte of address
RT Return to calling routine
*-------------------------------------------------------------------------
* Code derived from the Geneve MDOS L10 Floating Point Library's CFI
*
* NAME: CPFI: Convert Positive Floating point number to Integer
*
* Result: 0 <= Integer <= 32767
*
* INPUT Workspace:
* R0 = Integer result
* R1 = radix-100 digit processing
* R2 = FAC pointer
* R3 = FP fraction processing
* R4 = FP exponent for comparison
* R5 = radix-100 (centimal) base (set up by caller)
* R6 = current workspace index
* FAC = Floating-point number
*
* OUTPUT Workspace:
* FAC = Integer
*-------------------------------------------------------------------------
*
* Called by BL @CPFI
*
CPFI LI R2,FAC get pointer to number
ABS *R2 force FP number positive
JEQ CFI11 if 0, all done
CLR R0 start with zero result
CLR R3 clear fraction
CLR R4 clear out unused part of register
MOVB *R2+,R4 get exponent
STWP R6 use R6 for indexing into workspace
CI R4,>3F00 is number less than one
JLT CFI11 yes number < .01 - result = 0
JEQ CFI03 number > .01 and < 1 - result = 1
CI R4,>4100 is number less than 100,000?
JLT CFI02 it is between 1 and 100
JEQ CFI01 it is between 100 and 10,000
CI R4,>4200 is number too big to convert?
JH CFI08 yes, it is
MOVB *R2+,@1(R6) get MS-centimal-digit to LSB of R0
MPY R5,R0 radix to binary
MOV R1,R0 get result for next centimal digit
CFI01 MOVB *R2+,@7(R6) get next integral centimal digit to LSB of R3
A R3,R0 add to previous result
MPY R5,R0 radix to binary
MOV R0,R0 test for overflow
JNE CFI08 yes--cap to 32767
MOV R1,R0 get result for last digit
JLT CFI08 overflow--cap to 32767
CFI02 MOVB *R2+,@7(R6) get least integral centimal digit to LSB of R3
A R3,R0 add to result
CFI03 CB *R2,@CBD50 is rounding necessary?
JLT CFI06 no--check for overflow
*
*
INC R0 round up
CFI06 CI R0,SGNBIT did unsigned result exceed 32767?
JL CFI11 no--we're good
CFI08 LI R0,>7FFF now just finish up with 32767
*
*
CFI11 MOV R0,@FAC return number in FAC
RT
*-------------------------------------------------------------------------
* Code derived from the Geneve MDOS L10 Floating Point Library's CIF
*
* NAME: CPIF: Convert Positive Integer to Floating point number
*
* INPUT Workspace:
* R0 = Dividend|quotient in radix-100-digit-processing
* R1 = Remainder in radix-100-digit processing
* R3 = Exponent processing
* R4 = FAC pointer
* R5 = radix-100 (centimal) base (set up by caller)
* R6 = FAC pointer for clearing FAC
* FAC = Positive integer
*
* OUTPUT Workspace:
* FAC = Floating-point number
*-------------------------------------------------------------------------
*
* Called by BL @CPIF
*
CPIF LI R4,FAC get address of FAC (integer in, FP out)
MOV *R4,R0 get the integer before we clobber it
JEQ CIF03 if zero, then done
LI R6,FAC for clearing (see below)
*
CLR *R6+ and clear out FAC
CLR *R6+ .
CLR *R6+ .
CLR *R6 .
*
ABS R0 force integer positive
LI R3,>40 starting exponent
C R0,R5 1 radix-100 digit?
JL CIF02 yup
CI R0,10000 nope--2 radix-100 digits?
JL CIF01 yup
INC R3 3 radix-100 digits--inc exponent
MOV R0,R1 extract radix-100 digit
CLR R0 .
DIV R5,R0 .
SWPB R1 .
MOVB R1,@3(R4) stash digit in FAC+3
CIF01 INC R3 inc exponent
MOV R0,R1 extract radix-100 digit
CLR R0 .
DIV R5,R0 .
SWPB R1 .
MOVB R1,@2(R4) stash digit in FAC+2
CIF02 SWPB R0 R0 has leftmost radix-100 digit
MOVB R0,@1(R4) stash digit in FAC+1
SWPB R3 exponent to MSB
MOVB R3,*R4 stash exponent in FAC
CIF03 RT
END

 

 

 

This routine is not only much smaller, it also takes the same amount of time. There are no BLWP instructions, but I still thought it would take longer.

 

Both this routine and the last one are called with IRND; but, the TMS9900 object code files are different. This one is IRNDU.obj (IRNDUO on the DSK image) and the one from the last post is IRND.obj (IRNDO on the DSK image). Both are present in the attached ZIP file:

 

IRNDU.zip

 

The only thing different from the TI Basic program in my last post is that statement 110 must load IRNDU.obj.

 

The only change I may make in the future is to allow for randomizing within the routine. I would pass a flag to indicate

  • Do not randomize
  • Randomize at the beginning
  • Randomize before every number
  • Randomize after a specified number of random numbers

I probably should check the size of the passed array with N, the count of random numbers requested—right now, that's on the user. Otherwise, I'm done with this exercise. I accomplished my goal of coding an ALC function to be called from TI Basic to pass an array without using the helper functions NUMREF, NUMASG, etc.

 

...lee

  • Like 1
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...