+TheBF Posted August 2, 2017 Share Posted August 2, 2017 (edited) 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 August 4, 2017 by TheBF 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted August 2, 2017 Author Share Posted August 2, 2017 (edited) In Chapter 1 of this tutorial we created "dedicated to one DATA set" version of READ, DATA and RESTORE. Now using those words we translate the BASIC program LIST.TXT to CAMEL99 Forth. The general concepts are applicable to FB Forth or TF, but you will have to refer to the documentation for some specific equivalent words. We still need a couple of additions to make this READ statement work. We decided to make a very fast CALL CHAR. This is done by Copying bytes directly from CPU RAM to VDP RAM using an assembly language routine. READ will give us the CPU RAM address but how do we find the correct address in VDP RAM. In CAMEL99 Forth Graphics Mode, the Pattern Description table is located at address HEX 800 in VDP RAM. Let's name that address 'PDT' as a constant. 0800 CONSTANT PDT \ VDP address of "PATTERN DESCRIPTOR TABLE" Now we can create an array that gives the address of the pattern descriptor for any character. The math is simple: pattern_address = ascii# * 8 + PDT So in Forth we can write: : ]PDT ( char# -- 'pdt[n] ) 8* PDT + ; \ ]PDT is now a byte array in VDP ram Now to make a fast CALL CHAR, we use a routine that TI calls VMBW. In CAMEL Forth I named it VWRITE. This routine needs a CPU Address, a VDP address and the number of bytes to write. So with READ and ]PDT we have all we need to write: \ CALLCHAR is like CALL CHAR, but it takes a data address and ascii value \ and writes 8 bytes directly to VDP RAM (very fast) : CALLCHAR ( data_addr char# -- ) ]PDT 8 VWRITE ; The rest of the program is academic now. We broke out each part into separate routines (WORDs) which good style in Forth and allows separate testing at the console of each little piece. Here are the final parts with the BASIC lines as comments. : READ-DATA CLEAR \ 100 CALL CLEAR 104 40 DO \ 110 FOR A=40 TO 103 READ \ 120 READ A$ ( addr) I CALLCHAR \ 130 CALL CHAR(A,A$) ( Forth loop index is 'I') LOOP ; \ 140 NEXT A : SHOW-FACE 8 0 DO \ 150 FOR R=0 TO 7 8 0 DO \ 160 FOR C=0 TO 7 I 12 + J 8 + J 8* 40 + I + 1 HCHAR \ 170 CALL HCHAR(R+8,C+12,40+R*8+C) LOOP \ 180 NEXT C LOOP ; \ 190 NEXT R : COLOR-SCR 17 3 DO \ 200 FOR A=3 TO 16 I SCREEN \ 210 CALL SCREEN(A) 2000 MS \ 220 CALL SOUND(2000,44733,0) LOOP ; \ 230 NEXT A \ *CAMEL99 Forth has 'MS' which delays in milliseconds, so we don't need to use SOUND : RUN GRAPHICS \ change to 32 col graphics mode RESTORE \ we need RESTORE if we run more than once READ-DATA SHOW-FACE COLOR-SCR ; Edited August 4, 2017 by TheBF 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted August 2, 2017 Author Share Posted August 2, 2017 (edited) Here is the complete listing of LIST.TXT translated to CAMEL99 Forth. \ Exploring Forth programming from a BASIC perspective \ a ladies face. data source: http://briantw.com/ti-99/4a/LIST.TXT \ BASIC stores this data as text bytes which must be converted \ at run time. (slow) \ Here they will be store the data as integers in memory cells \ In Forth we must declare a name before we can use it so DATA is first \ 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 , \ ============================================================== \ Forth does not have READ DATA RESTORE \ But we can create words to work similar to BASIC \ The math is easy. DATA+0 will give us the address of the 1st record \ DATA+8 gives us the address of the 2nd record etc... DECIMAL : ]DATA ( n -- addr) \ read DATA like an array of 1 byte elements DATA + ; \ calculate address N+DATA, return result to stack VARIABLE P \ holds the index of the next record to read \ Instead of reading all the bytes into a string, \ we will just return the address of the data we want to use ( some complicated languages call this "a pointer' but it's just a memory address) : 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 # \ ***************************************************************** \ *NOTICE* READ has no protection. You could read past the end. \ If you want protection in Forth you make it yourself. \ ***************************************************************** : RESTORE ( -- ) P OFF ; \ just reset the data pointer variable to zero \ ============================================================== HEX 0800 CONSTANT PDT \ VDP address of "PATTERN DESCRIPTOR TABLE" : ]PDT ( char# -- 'pdt[n] ) 8* PDT + ; \ ]PDT is a byte array in VDP ram \ CALLCHAR is like CALL CHAR, but it takes a data address and ascii value \ and writes 8 bytes directly to VDP RAM (very fast) : CALLCHAR ( data_addr char# -- ) ]PDT 8 VWRITE ; DECIMAL : READ-DATA CLEAR \ 100 CALL CLEAR 104 40 DO \ 110 FOR A=40 TO 103 READ \ 120 READ A$ ( addr) I CALLCHAR \ 130 CALL CHAR(A,A$) ( Forth loop index is 'I') LOOP ; \ 140 NEXT A : SHOW-FACE 8 0 DO \ 150 FOR R=0 TO 7 8 0 DO \ 160 FOR C=0 TO 7 I 12 + J 8 + J 8* 40 + I + 1 HCHAR \ 170 CALL HCHAR(R+8,C+12,40+R*8+C) LOOP \ 180 NEXT C LOOP ; \ 190 NEXT R : COLOR-SCR 17 3 DO \ 200 FOR A=3 TO 16 I SCREEN \ 210 CALL SCREEN(A) 2000 MS \ 220 CALL SOUND(2000,44733,0) LOOP ; \ 230 NEXT A \ *CAMEL99 Forth has 'MS' which delays, so we don't need SOUND : RUN GRAPHICS \ change to 32 col graphics mode RESTORE \ we need RESTORE if we run more than once READ-DATA SHOW-FACE COLOR-SCR ; GRAPHICS Edited August 2, 2017 by TheBF Quote Link to comment Share on other sites More sharing options...
Willsy Posted August 2, 2017 Share Posted August 2, 2017 There's some prior art here that you might want to have a peek at. IIRC fbForth is also compatible with this: http://turboforth.net/lang_ref/view_word.asp?ID=4 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted August 2, 2017 Author Share Posted August 2, 2017 There's some prior art here that you might want to have a peek at. IIRC fbForth is also compatible with this: http://turboforth.net/lang_ref/view_word.asp?ID=4 Yes this is definitely a nice word Willsy. I heartily endorse it. I took the approach above to try and show those using BASIC that making data structures in raw memory is not black magic and understandable by anyone. I just took a look at your source code for 'DATA' and it's a little above introduction level. However you have cause me to think about making one for myself. :-) Thanks for weighing in. BF Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted August 2, 2017 Share Posted August 2, 2017 There's some prior art here that you might want to have a peek at. IIRC fbForth is also compatible with this: http://turboforth.net/lang_ref/view_word.asp?ID=4 Actually, DATA is not defined in fbForth 2.0, but can easily be managed with the DATA[ ... ]DATA construct: HEX : DATA ( --- addr ) DATA[ 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 ]DATA DROP ( drop cell count and just leave address) ; DECIMAL The fact that ]DATA is already defined in fbForth 2.0 does not preclude redefining it, but a newbie might do better with a different name. ...lee Quote Link to comment Share on other sites More sharing options...
+TheBF Posted August 3, 2017 Author Share Posted August 3, 2017 (edited) Actually, DATA is not defined in fbForth 2.0, but can easily be managed with the DATA[ ... ]DATA construct: HEX : DATA ( --- addr ) DATA[ 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 ]DATA DROP ( drop cell count and just leave address) ; DECIMAL The fact that ]DATA is already defined in fbForth 2.0 does not preclude redefining it, but a newbie might do better with a different name. ...lee Indeed. For those trying this using FB Forth with the DATA[ ]DATA construct, some other candidates for the picture array could be: DATA+ DATA() DATA[] ]PICTURE or any other set of ASCII text characters that makes sense to your way of thinking about it. It's Forth. "It's freedom baby! ya!" Edited August 3, 2017 by TheBF 2 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.