While looking at the following web site; http://briantw.com/ti-99/4a/LIST.TXT which I found in older posts here I found this little BASIC Program. I ran it to see what it did and it made a picture of Pamela Anderson I think. (don't get excited, it's just her face)
I wondered what it would take to translate it to Forth. The following is a little tutorial on one way to manage data in Forth and create something equivalent to DATA READ and RESTORE. My hope is that the BASIC programmer and those new to Assembler or Forth will see how simple data can be inside the machine.
Anyone who has tried TI Forth or TF or the new FB Forth will quickly realize that Forth requires you to know more about the internal arrangement of memory just as you do in Assembly Language. By comparison TI BASIC has numbers and strings and you never really need to know how the computer stores the information to use them.
In the example BASIC program there is a large list of DATA statements with strings to change characters.
Forth has no DATA, READ or RESTORE words so here is how we added them just as a demonstration.
The first program change is Forth requires that you define what a new word is before you use it so we need to define our DATA at the beginning of the program.
We create a name for our data with the words CREATE DATA. All this does is add the name DATA to Forth and if you run DATA you get the address of the next available memory location on the stack. Not very useful by itself.
BASIC uses TEXT strings in the DATA statements, that must be converted to integers before they can be loaded into VDP memory to shape a character. In Forth we will put integers into memory using the ‘comma’ which compiles an integer into the next Dictionary memory cell and advances the Dictionary pointer by 2. (The Dictionary is the memory where Forth keeps all of its words.)
Forth equivalent to the DATA statements
\ DATA simply returns the base address of all these numbers HEX CREATE DATA ( -- addr) \ [ --------8 bytes--------] [---------8 bytes----------] FFFF , FFFF , FFFF , FFFF , FFFF , FFFF , FFFE , FFFF , FAED , FEF7 , BEEA , F4B5 , AA02 , 80F8 , D68A , 2401 , A9B6 , AAAD , 3695 , 564B , BFD5 , BBA9 , D1A2 , D069 , FFB5 , FF6F , FD5F , BB5F , FFDB , 7EEB , BEEB , 5DF6 , FFFF , FEFB , FDEB , FAD5 , FBEE , BBDD , 6AF6 , AD75 , D5A2 , 54A2 , A9A4 , 1249 , B64A , AA12 , 4825 , 8822 , A5AA , 5791 , 4A28 , 9425 , A0DA , 68A8 , CA9A , 6CB6 , 6D97 , 5E13 , 2A05 , 0A05 , DBED , B6FB , EDBD , F73E , FAA9 , F5BB , EAB5 , D6F3 , ADF5 , 565A , D5B5 , D66A , 204A , 8054 , 5148 , A488 , 0094 , 2196 , 2902 , 9044 , 920A , A449 , 0229 , 0293 , DAAA , B76B , AD57 , FFBF , 000A , 2004 , 81C8 , C5A2 , D72F , B517 , 4513 , 4205 , 59EA , A9D1 , 7888 , 8880 , AD35 , 5A2B , 955A , 2DB6 , 3244 , 9045 , 75A8 , 9281 , 1082 , 100A , F45A , 4AB6 , 0AA5 , 15A7 , 2A8B , 9585 , FBF2 , FB1E , A955 , 02A9 , AAAA , ADAB , 552D , AA2C , A289 , 4249 , 9241 , A8D2 , 8882 , 8A85 , 8642 , 4342 , 9AAA , AD1A , D7AC , 5A8A , BECB , 7FAF , FFBD , DEB5 , FDFE , C4EE , B4D5 , 9049 , 65A2 , AAA9 , 542A , 4A2A , AAD5 , B4D2 , AAB9 , 5CAB , 562B , AB95 , 562B , AA57 , A550 , AAD1 , A8D4 , E258 , 4345 , 52A1 , A1A0 , A1B0 , 154D , 880D , 448E , 0282 , 5AB7 , 542A , 5154 , AAD5 , 0450 , 8940 , 2280 , 2501 , 2A55 , 2D55 , 2B8A , 5005 , AD0A , 2D55 , 5AA5 , 965B , 2AD5 , 5B6D , AAB5 , EA77 , EAE8 , 6AB4 , D59A , EA5A , 1141 , 7151 , ECEE , FBEF , 0201 , 02A1 , A0C0 , 70AA , AAD5 , B65B , 160D , 0200 , 9421 , 3AC4 , 5A20 , 5A44 , 2A15 , A556 , 955D , AB15 , D554 , 6192 , 146A , 55AE , AA7B , ADB5 , EBB7 , DAAB , ED75 , BEED , 7EFE , FFED , B3FD , B7DD , F7AE , FBF8 , F0AC , FAF6 , FDB6 , DEAE , 8001 , A448 , A012 , 80A0 , DA6A , 6D2B , AD1F , 5F3A , AAAB , 5555 , AD75 , DBFE , DA68 , 2150 , 4A51 , 56EB , 4FAB , 2F97 , 6E5F , FDBF , FFB6 , DBEF , B3DB , EDBB ,
Now we have all the data in block of memory, but we need to get at it 8 bytes at a time.
First we will make word that takes a number off the stack and adds it to the address of DATA. This is actually all it takes to make a byte array.
I use my own notation here which is whenever I make a word that works like an array I give it a ‘]’ as the first character. This reminds me that it needs an index value and that it is an array.
So our new word that makes a simple byte array out of the word DATA is just:
: ]DATA ( n -- addr) \ read DATA like an array of 1 byte elements DATA + ; \ calculate address N+DATA, return result to stack
Next we need a variable to keep track of which data record to read. That’s easy.
VARIABLE P \ holds the index of the next record to read
With these two simple words we can create a READ word. In BASIC READ has to know how to READ floating point numbers, HEX number strings, and text strings. All of this adds overhead because the interpreter has to figure out what it’s reading each time. Forth thinking says make each word do one thing. If you need to do another thing, make another word. So our READ only reads our DATA array and only reads 8 byte records. We could do any other thing we wanted, but for this program this is all we need so this is all we make.
: READ ( -- addr ) \ returns the address of next 8 bytes of data
P @ ]DATA \ fetch value of P, use as index into the data array
8 P +! ; \ advance 'p' by 8, ie to the next record #
In BASIC using READ involves copying bytes from DATA to a variable. This takes lots of time.
READ in this implementation will just return the address of the data we want to use to the top of the stack. Some languages call this a 'pointer'. Forth keeps it simple and like Assembly Language calls it just what it really is, a memory address.
You should note READ has no protection. You could read past the end of the data. If you want protection in Forth you have to make it yourself.
And to be complete we need the word RESTORE. This is the easiest of all. We just reset the data pointer variable to zero.
: RESTORE ( -- ) 0 P ! ;
In Chapter 2 we will use these words to translate the example program to Forth.
Edited by TheBF, Fri Aug 4, 2017 7:34 AM.