Willsy Posted May 10, 2017 Share Posted May 10, 2017 Forth: A Brief Primer A Forth primer for anyone that is interested in dipping their toes into the Forth language… Words (Forth parlance for routines/commands/subroutines) take their arguments off the stack, do something with them, and if necessary, push results back to the stack. In this way, Forth words are ‘chained’ together, using the stack to communicate with each other. It really is as simple as that. TOM DICK HARRY The above looks strange if you are used to BASIC, but the above is a valid Forth program (assuming those words exist in the Forth system that you are using). When Forth code executes it starts executing at the left, and proceeds to the right, so TOM executes first, then DICK, then HARRY then the “program” ends. Now, it may be that TOM places a number on the stack when he executes. DICK may take that number and do something with it, leaving a result for HARRY. Let’s try an example: : TOM ( n – n*2 ) 2 * ; : DICK ( n – n*4 ) 4 * ; : HARRY ( n – ) 9 + . ; This probably looks terrifying. Baffling. But please don’t be afraid. Often, ones objections to learning Forth is based on the assumption that is it difficult and/or complicated, but in actual fact, it is the complete opposite. It’s so simple, that when you eventually “get it” you might even feel somewhat stupid, a bit like when you learn how a certain magic trick works when you’re a kid, and when you realise it, you think “Is that IT? Is that ALL it is? Are you KIDDING me?!” Let’s break that little “program” above down and see how it works. First, we see a colon. This symbol is used to say to the Forth system “Hey, I’m going to define a new word, so listen up”. Following the colon, we have the name of the new word: TOM. Note, there must be a space between the colon and the name. Next, the open bracket. This is actually the start of a comment (REM in BASIC). Inside the comment, we are using a simple convention/notation to describe stack requirements of the word, usually called the “stack signature”. Note: THIS IS NOT CODE. It’s a comment, and as such it’s purely optional, but it helps readers to understand what the word does to the stack without having to dissect the code inside the word. See the article Forth: A Sojourn Into Stack Comments for more information. Inside TOM we see the following code: 2 * Looks a little strange, but, following this from left to right, it’s quite easy to break down what happens. REMEMBER, the stack comment tells us that a number should already be on the stack when this word is called. Let’s say 9 as an example. The next thing that happens is that TOM itself pushes the number 2 onto the stack, so the stack looks like this: 9 2 <--top of stack Then, the * word executes, which simply means multiply. Multiply takes two values from the stack, multiplies them, and pushes the result back to the stack. So, 9*2 is 18. So, if we passed 9 in on the stack, 18 would “come out” of TOM onto the stack. The next thing we see is a semi-colon ; which says to the Forth system “Hey, I’ve done with my definition of the word TOM, thanks very much.” At this point, the Forth systems “knows” a new word, TOM, and you can test it immediately: 44 TOM . The system will display the value 88 (44*2). What just happened? We asked the Forth system to put 44 on the stack, then asked it to execute TOM, which it did. TOM took the value on the stack, multiplied it by two, and put the result back on the stack. Then the word . (dot) took the value off the stack (in other words, it consumed it), and displayed it for us. So, TOM is written and tested. That wasn’t so hard, was it? Let’s have a look at DICK: : DICK ( n – n*4 ) 4 * ; We can see that this word takes a value from the stack, multiplies it by 4, and leaves the result on the stack. And finally, HARRY: : HARRY ( n – ) 9 + . ; Note the stack comment. There is nothing on the output side. This word takes the top value on the stack, adds 9 to it, and uses . (dot ) to display the result, placing nothing on the stack. Now, this is simply a collection of “words”. It’s not really a “program” as such. It’s more like three subroutines that exist independently of each other. That’s how Forth programs are developed: You split your program up until logical blocks of functionality, each having its own word (a block of functionality may have many words – it’s up to you how you “factor” your program”). These words can be written independently of each other, and tested by supplying data from the stack. When they are tested to satisfaction, you call it done, and you move on to the next logical part of the program. We could do with some glue; another word, to bind TOM DICK and HARRRY together into a program: : TDH ( n -- ) TOM DICK HARRY ; So, here’s a word called TDH (Tom, Dick, Harry!) that calls TOM, then DICK, then HARRY, in that order. The stack comment tells us that we need to pass a number in on the stack before calling TDH. How do we do that? Simple: 99 TDH When we type the above and press enter, we get the result 801: ((99x2)x4)+9 Let’s look briefly at TI BASIC version of the above program for comparison purposes: 10 N=99 20 GOSUB 100 30 GOSUB 200 40 GOSUB 300 50 PRINT N 60 END 100 N=N*2 110 RETURN 200 N=N*4 210 RETURN 300 N=N+9 310 RETURN Lines 10 to 50 are the equivalent to the word TDH Lines 100 to 110 are TOM Lines 200 to 210 are DICK Lines 300 to 310 are HARRY Now, let us look at the Forth version again: : TOM ( n – n*2 ) 2 * ; : DICK ( n – n*4 ) 4 * ; : HARRY ( n – ) 9 + . ; : TDH ( n -- ) TOM DICK HARRY ; A lot shorter, and uses no variables – the data is carried in and out of the words using the stack. Each word can be tested independently of the other words, and bound together at the end into a program that does some work. If you've read this far, then hopefully you are thinking “Well, it’s kinda weird, but I get it”. If that is the case, congratulations, you have just learned Forth. I’m not being flippant. Forth is all about words, and the stack. And when you’ve got that concept down, the rest is just a matter of experience. Forth on the TI-99/4A Hopefully you’ll try the above out on your favourite TI-99/4A Forth. There’s the excellent fbForth written by Lee Stewart, which is a much more advanced version of TI’s original TI FORTH, and has the advantage of running from a cartridge. There’s also my own Forth cartridge, TurboForth (and its companion website, http://.turboforth.net) and there is Brian Fox’s new Forth system, the name of which escapes me at the moment (chime in, Brian!). Feel free to post comments/questions. It’s all about learning, sharing, and having fun. 6 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted May 10, 2017 Share Posted May 10, 2017 My as yet work in progress Forth is called CAMEL99. Why? It is derived from a simple ANS/ISO Forth system called CAMEL Forth The author of CAMEL Forth, a fellow Canadian, Brad Rodriguez Phd. created the system in response to the 1994 ANS Forth spec. The specification was designed by a committee... "A camel is a horse designed by a committee" I gather he was less than enthusiastic about the new language spec. :-) Quote Link to comment Share on other sites More sharing options...
Retrospect Posted May 10, 2017 Share Posted May 10, 2017 Excellent Willsy. How would one set up a variable based on a random number? We could define the word "DICE" to display a random number between 1 and 6 .. but how would we store that random number in a variable? Quote Link to comment Share on other sites More sharing options...
+TheBF Posted May 10, 2017 Share Posted May 10, 2017 (edited) Excellent Willsy. How would one set up a variable based on a random number? We could define the word "DICE" to display a random number between 1 and 6 .. but how would we store that random number in a variable? I can take this one if you like. This is where we have a fundamental difference in how you think when writing in Forth vs BASIC. Each language gives us a "structure" to work in and these 2 languages are quite different So we need to have the random number words defined in our system. I will assume we have them. In BASIC you have RND. So in Forth we would call our word RND as well. In BASIC you might write 100 MYRAND=RND In Forth you could do the very same like this: VARIABLE MYRAND \ variables must be created before use RND MYRAND ! \ RND puts a number on the stack \ MYRAND puts its own memory address on the stack \ '!' (pronounced 'STORE') stores the number in the memory address So in Forth it's more like using POKE() to put a number in variable. In other words Forth is lower level than BASIC, a little like Assembler with some lipstick. To get the contents of a variable we use the 'fetch' operator which is just a '@' character. MYRAND @ . \ fetch the contents of MYRAND to the stack and print it with '.' 457 ok _ Note: Typically RND takes an input from the stack to control how big the result number will be. I left it out to simplify the explanation. Edited May 10, 2017 by TheBF Quote Link to comment Share on other sites More sharing options...
Sinphaltimus Posted May 10, 2017 Share Posted May 10, 2017 (edited) Great question Retrospect. If I understand correctly, you wouldn't. You'd store the number in the stack. If I'm correct, then I guess you'd need a way to efficiently retrieve that number from the stack. Which would be my next question, how? Perhaps assign that result to a word? Edited May 10, 2017 by Sinphaltimus 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted May 10, 2017 Share Posted May 10, 2017 You are correct Sinphaltimus. You might not want or need to keep a random number in a variable. You normally just get it and use it. Which is the same with BASIC's RND function. It just returns a value. Do whatever you want with it. So Forth RND typically wants a number on the stack to set the range from 0 to n So here is DICE defined: : DICE ( -- n ) 6 RND ; When you type DICE at the console your will see 'ok" What the heck? Well DICE just plopped a random number onto the stack. That's ALL it can do. If you want that number it's there to use. To see it use the '.' word. (called dot) To store it in a variable see my previous post. Make sense? Quote Link to comment Share on other sites More sharing options...
+TheBF Posted May 10, 2017 Share Posted May 10, 2017 Actually it's best to load a Forth onto your machine and play around with the stack. You will crash the machine because it is your right to do so when you are brave enough to work directly with the hardware. Get "Starting Forth" https://www.forth.com/starting-forth/0-starting-forth/ or read Willy's tutorial pages. Quote Link to comment Share on other sites More sharing options...
+TheBF Posted May 10, 2017 Share Posted May 10, 2017 Here is some material from a manual I am writing for people who know TI-BASIC and want to play with Forth. Look at the program listing below: 10 CALL CLEAR 20 PRINT “Hello world!” 30 GOTO 20 Now look at it in CAMEL99 Forth with some "helper" words loaded : 10: CLEAR ; : 20: C" HELLO WORLD" PRINT ; : RUN 10: BEGIN 20: AGAIN ; To a TI-BASIC programmer it looks weird. This program is to help you understand how BASIC is really working and what it takes for a compiled programming language to imitate BASIC. We have loaded our TI-BASIC word set into CAMEL99 so we have some familiar word names, but they seem to be backwards. Why? That has to do with how Forth uses something called the Stack which we will explain in the next lesson. Also notice we don’t have to CALL those sub-programs. That’s because all Forth words are sub-programs so they are all called by Forth by default. We have used the colon/semicolon structure to create something that looks like line numbers. PLEASE NOTE: They are NOT line numbers, they are now WORDs in Forth but they let you see how a line number in BASIC performs the same function as a WORD does in Forth. Line numbers are just an identifier, that the computer can use to find code when it needs to. BASIC’s line numbers are labels to let the computer find pieces of code Forth WORDs are labels to let the computer find pieces of code Let’s review what our new Forth words (line numbers do) 10: is obvious. It calls CLEAR, which fills the screen with spaces and puts the cursor on the bottom line. 20: Uses C” to put a literal characters in the definition that end at the other quote. Literal means the characters are “compiled” into place in memory just as they appear. And PRINT can take a string like that and PRINT it to the screen. 30: This is where things get different. We defined a word called RUN. RUN is performing the function of the BASIC interpreter, which is to “RUN” the code in each line number in order one at a time. Forth does not have a RUN function so we did it manually. Structured ProgrammingForth is known as a structured programming language so it does not have GOTO. This may be a very weird thing for people who have only used BASIC. Structured languages do not let you jump anywhere. They provide you with ways to jump but it is well… structured. To create an infinite loop (goes forever) we use the BEGIN/AGAIN structure. AGAIN is a GOTO that can only jump backwards to BEGIN. I hope it is clear in our RUN word that 20: is going to go on forever because it is between BEGIN and AGAIN. Forth VersionNow please do not think I want you to write Forth code that looks like this BASIC with line numbers. I simply wanted you to see something more familiar. Now that you know what line numbers really do in BASIC look at how it could look in Forth if we used more descriptive names instead of line numbers: : CLS CLEAR ; : HI! C" HELLO WORLD" PRINT ; : RUN CLS BEGIN HI! AGAIN ; In fact we really don’t need the first line because CLS is just calling the word CLEAR. So we can use CLEAR by itself and the program would become: : HI! C" HELLO WORLD" PRINT ; : RUN CLEAR BEGIN HI! AGAIN ; And if we were really in a hurry we could remove the word HI! And put what it does right in our RUN word so it would look like this: : RUN CLEAR BEGIN C" HELLO WORLD" PRINT AGAIN ; Did that make your head spin a little. It can when you are used to BASIC. Compared to FORTH, BASIC is like a straight-jacket, forcing you do things in very specific ways with few exceptions. Forth gives you much more freedom, which means you have to think a little more about what you want but the program can do almost anything. I will post a simple version of CAMEL99 with Graphics words and training wheels to assist in the transition shortly. Quote Link to comment Share on other sites More sharing options...
Willsy Posted May 10, 2017 Author Share Posted May 10, 2017 I can take this one if you like. Perfect! That's exactly what I was hoping to see! Quote Link to comment Share on other sites More sharing options...
Willsy Posted May 10, 2017 Author Share Posted May 10, 2017 Great question Retrospect. If I understand correctly, you wouldn't. You'd store the number in the stack. If I'm correct, then I guess you'd need a way to efficiently retrieve that number from the stack. Which would be my next question, how? Perhaps assign that result to a word? Yes. Generally, you'd try to avoid storing stuff in variables, but you can use them. Of course, we can't store all the variables of a program on the stack, your program would spend most of its time "juggling the stack" (stackrobatics) to get the value it needs to the top of the stack where it can work on it. Not good. Variables in Forth are very easy to understand. Variables have a name, just like BASIC, but, as Brian mentioned, they are declared in advance using the VARIABLE keyword: TurboForth: VARIABLE X fbForth 0 VARIABLE X Note that fbForth requires an initial value for the variable, whereas TurboForth initialises new variables to 0. There are lots of subtle differences between the two systems as they built on different Forth language standards. Now, as Brian said, variables push their address to the stack, not the value stored in the variable. Here's a demonstration (TurboForth) VARIABLE X X $. TurboForth reponds with A2CE (on my system). A2CE is the address (in hex) of the variable x (the word $. prints whatever is on the stack as a hex value). You could also print it as an unsigned decimal number: x u. and see that it's at address 41678 decimal. To write a value into a variable, you use the word ! ("store" or "poke", whichever you prefer). ! needs two values on the stack: The value to store; The address to store it in (as the top most stack item). So, to store 100 in X we could simply write: 100 X ! So, 100 goes on the stack, then X executes (it sounds weird to say that a variable "executes" but it really does - executing it makes it push it address to the stack), then the word ! (store) takes the address, and the value, and stores them for you. We can prove that by reading it back with @ ("fetch", or "peek"): x @ . And you'll see 100 displayed. Ta dah! Not so hard, eh? So, to store a dice roll in a variable, called ROLL, we could have something very simple like this: VARIABLE ROLL : ROLL-DICE ( -- ) 6 RND ROLL ! ; And test it with the following: ROLL-DICE ROLL @ . Can you see what that is doing? Note: RND, just like RND in BASIC always chooses a random number between 0 and the value you give it minus 1. So if you give 6 to RND you get a random number between 0 and 5 (which is of course 6 possible values). To make it between 1 and 6, simply add to the value that RND gives us and then store it: : ROLL-DICE ( -- ) 6 RND 1 + ROLL ! ; So, here, we execute RND with 6 on the stack (so we get a number between 0 and 5 on the stack) and then we add 1 to it, then we store it in ROLL. 1 Quote Link to comment Share on other sites More sharing options...
Willsy Posted May 10, 2017 Author Share Posted May 10, 2017 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted May 10, 2017 Share Posted May 10, 2017 (edited) More on control structures. Remember that little program we wrote in the CAMEL99 Trainer? : RUN CLEAR BEGIN C" HELLO WORLD" PRINT AGAIN ; Well it loops forever because of BEGIN AGAIN. BASIC builds the BREAK control right into the running interpreter but Forth does not to keep loops fast. So how do we stop it? We want it to loop UNTIL something happens right? Forth has the word UNTIL, that will loop like AGAIN until it sees a non-zero value on the stack. In fact the definition for AGAIN is equivalent to "0 UNTIL" meaning that zero will make it loop forever. Here is how we use UNTIL : TEST C" I am waiting here! ..." PRINT BEGIN KEY? UNTIL ; Paste this into the CAMEL99 Trainer and type TEST <enter> You will see the string print and then nothing, until you press a key and then Forth will reply with ok. What's happening? The word KEY? calls the TI-99 KSCAN routine in the console ROMs. If nothing is pressed it puts a zero on the stack. If something is pressed it puts HEX FFFF on the stack (all 16 bits set is Forth's "true flag") UNTIL keeps jumping back to BEGIN until... it sees that true flag on the stack. Then it exits the loop and returns to the Forth interpreter. As you can see UNTIL does not care where that true flag came from. It could be the result of a calculation, some logical operation or comparison. Anything that gives us a non-zero result will cause the loop to stop. Edited May 10, 2017 by TheBF Quote Link to comment Share on other sites More sharing options...
+TheBF Posted May 10, 2017 Share Posted May 10, 2017 (edited) For a full featured Forth system with all the bells and whistles you have to use Turbo Forth or FBForth. But if you want to do a little exploring with something the feels kind of familiar (but a little bit reverse order) download the CAMEL99 trainer here. https://github.com/bfox9900/CAMEL99 Just click on CAMEL99 and then click the download button. It wakes up in 32 col graphics mode and you can try Forth with training wheels You can't save your work, and it can't load a file (yet) so use notepad or your favourite editor and Classic99 and paste your code into it or type it at the keyboard. Here are some of the training wheels that have been put it to make playing with Forth a little easier for theTI-Basic programmer. NEW starts a new session. All code added to the system is removed. CLEAR clears the screen and puts the cursor on the bottom line where it belongs! (no need to CALL it. It's smart enough to call itself) STRING: lets you make string variables and use them similar to BASIC 50 STRING: A$ makes a string that can hold 50 bytes. PRINT well it only works for strings, but it just feels correct so go ahead. Try this: 50 STRING: A$ A$ =" Hello world!"A$ PRINTHello world! ok (mind the space after =" It is a Forth word ) And you have these string functions, which like BASIC can be used together.Example: A$ 0 5 SEG$ CHAR : LEN ( $ -- n ): CPOS ( $ char -- position) : LEFT$ ( $ # -- top$): RIGHT$ ( $ # -- top$) : SEG$ ( $ start# char# -- top$): STR$ ( n -- top$): VAL$ ( adr$ - # ): CHR$ ( ascii# -- top$ ) Concatenate 2 strings USAGE: A$ B$ & PRINT : & ( $1 $2 -- top$) ( changed from ADD$. it's RPN. be careful) : COMPARE$ ( $1 $2 -- flag) : =$ ( $1 $1 -- flag) : >$ ( $1 $2 -- flag) : <$ ( $1 $2 -- flag) And something that BASIC does not have : SKIP$ ( $ char -- $ ) ( removes leading chars)----------------------------GRAPHICS stuff like TI-BASIC.Screen coordinates are base 0. 0..23 Rows, 0..32 columns*NEW*Give it an ascii value and it returns the character set no.no need to remember the color set# Yay! SET# ( ascii -- set#) ( usage: CHAR A SET# 2 8 COLOR) *NEW*Change contiguous character sets at once COLOR ( character-set fg-color bg-color -- ) Uses same color numbers as Basic. COLORS ( set1 set2 fg bg -- )SCREEN ( color -- ) GRAPHICS ( -- ) Changes to familiar CYAN screen \ CHAR sub-program is renamed to CHARDEF\ USAGE: HEX" 7C7C 7C7C 7C7C 7C00" 30 CHARDEF : CHARDEF ( FFFF FFFF FFFF FFFF char# --) *NEW*CHARDEF@ ( char# -- FFFF FFFF FFFF FFFF) fetches a pattern from VDP RAM and puts the numbers on the stack GCHAR ( row col -- char) ( NOTE: for HCHAR & VCHAR you must give the cnt. Forth requires 4 parameters always): HCHAR ( col row char cnt -- ): VCHAR ( x y char cnt -- ) Edited May 12, 2017 by TheBF 1 Quote Link to comment Share on other sites More sharing options...
Retrospect Posted May 11, 2017 Share Posted May 11, 2017 (edited) Right. So, for example, would a "Dopewars" game be possible with Forth? My way of doing mine was roughly this in Basic ! D$ is Items on the market / inventory ! P$ is the places (streets of new york for example) ! MP is the market price ! MA is the market amounts (quantities of each item) ! IP is the inventory prices paid ! IA is the inventory amounts carried ! HI represents a high price for items on the market ! LO represents the lower prices of items on the market 1 ! DOPEWARS TI 2 RANDOMIZE 10 DIM D$(6),P$(6),MP(6),MA(6),IP(6),IA(6),LO(6),HI(6) 20 FOR LOOP=1 TO 6 :: READ X$ :: READ Y$ :: READ X :: READ Y 30 D$(LOOP)=X$ :: P$(LOOP)=Y$ :: HI(LOOP)=X :: LO(LOOP)=Y :: NEXT LOOP 40 DATA COCAINE,BRONX,10000,19000 50 DATA HEROIN,GHETTO,50000,9000 60 DATA SHROOMS,"CENTRAL PARK",10000,4000 70 DATA HASHISH,MANHATTEN,5000,2250 80 DATA ACID,BROOKLYN,1500,500 90 DATA LUDES,"NEW JERSEY",100,10 100 CASH=2500 :: DEBT=5000 :: DAY=0 :: FOR LOOP=1 TO 6 :: IA(LOOP)=0 :: IP(LOOP)=0 :: NEXT LOOP 110 ! NEW DAY 120 DAY=DAY+1 130 DI=INT(DEBT/16) :: DEBT=DEBT+DI 140 ! GET MARKET AMOUNTS & PRICES FOR NEW DAY 150 FOR LOOP=1 TO 6 160 MA(LOOP)=INT(100*RND)+1 :: MP(LOOP)=INT(HI(LOOP)*RND)+LO(LOOP) 170 XX=INT(6*RND)+1 :: IF XX=LOOP THEN 171 ELSE 180 171 MA(LOOP)=0 :: MP(LOOP)=0 ! THIS IS SIMULATING THERE BEING NONE OF THAT PARTICULAR ITEM THAT DAY 180 NEXT LOOP 200 ! SHOW THE MAIN SCREEN 210 CALL CLEAR 220 PRINT "ITEM";TAB(12);"QTY";TAB(18);"COST";TAB(26);"NUM" :: PRINT 230 FOR LOOP=1 TO 6 240 PRINT D$(LOOP);TAB(11);MA(LOOP);TAB(17);MP(LOOP);TAB(26);LOOP 250 NEXT LOOP ......and so on, and so forth ..... Edited May 11, 2017 by Retrospect Quote Link to comment Share on other sites More sharing options...
+TheBF Posted May 11, 2017 Share Posted May 11, 2017 I gotta run, but will try to give you a Forth version, with BASIC code as the comments. The technical answer is that Forth is a Turing complete language so it can in theory code anything. The real answer is Forth is close to Assembler so you end up building up from low level pieces, some of the stuff you take for granted in BASIC. But... that can be fun and once you do it, you have it forever if you need it again. So those string arrays will need to be created, but I think we can do something simple. And I should add some of the things that seem natural to do in BASIC, you just don't do that way in Forth. B 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted May 12, 2017 Share Posted May 12, 2017 (edited) So I have become slightly crazy since I started comparing BASIC to FORTH. I posted a literal translation of deNile here: http://atariage.com/forums/topic/265231-in-denile/page-2 Now for anyone who is interested I took the same Forth code, which is very bad Forth style and re-organized in the way the Forth programs are written. You can see this "factoring" of the program into small parts in Lee's code. The stack stuff might make your head spin, so this shows Forth style but still using some Variables and strings like the original did. All we did was re-organize how to think about it. That's important for programming in any language if you move from BASIC. I think the thing to know is Chuck Moore believes every Forth WORD (sub-routine) should be so simple that there was no question what it does. And the name should clearly let you know what it's going to do. So with those to things in mind here is deNile using more "WORDS" which is how Forth programs are made. *Look at how many lines were removed by creating a WORD to clip values and then using that clipping word to make automatic functions to control the water characters. And hopefully we get more clarity when we are done because we have created a little language to solve the problem. ( Let me know if this is helpful or not I can shut-up anytime, but with a venue to teach about Forth I can be chatterbox) \ DENILE.BAS translate using Forth style factoring \ dialect: CAMEL99 Forth V 0.9 TRAINER \ with extensions to help TI-BASIC PROGRAMMERS \ un-rolled this loop HEX" 0102 0709 1F24 7F92" 65 CHARDEF \ 20 FOR L=65 TO 70 ) HEX" 8040 E090 F824 FE49" 66 CHARDEF \ 30 READ Q$ ) HEX" FF92 FF24 FF92 FF49" 67 CHARDEF \ 40 CALL CHAR(L,Q$) HEX" AA55 4489 2002 4801" 68 CHARDEF \ 50 NEXT L ) HEX" 0002 1735 7CFC 44AA" 69 CHARDEF \ Camel HEX" 0008 081C 2A08 1414" 70 CHARDEF \ little man \ water waves HEX" 0083 C7AE FBEF BDF7" 104 CHARDEF \ 70 CALL CHAR(104,"0083C7AEFBEFBDF7") HEX" 0007 8F5D F7DF 7BEF" 105 CHARDEF \ 80 CALL CHAR(105,"00078F5DF7DF7BEF") HEX" 000E 1FBA EFBF F6DF" 106 CHARDEF \ 90 CALL CHAR(106,"000E1FBAEFBFF6DF") HEX" 001C 3E75 DF7F EDBF" 107 CHARDEF \ 100 CALL CHAR(107,"001C3E75DF7FEDBF") HEX" 0038 7CEA BFFE DB7F" 108 CHARDEF \ 110 CALL CHAR(108,"00387CEABFFEDB7F") HEX" 0070 F8D5 7FFD B7FE" 109 CHARDEF \ 120 CALL CHAR(109,"0070F8D57FFDB7FE") HEX" 00E0 F1AB FEFB 6FFD" 110 CHARDEF \ 130 CALL CHAR(110,"00E0F1ABFEFB6FFD") HEX" 00C1 E357 FDF7 DEFB" 111 CHARDEF \ 140 CALL CHAR(111,"00C1E357FDF7DEFB") \ ALL variables must be defined before they are used VARIABLE T VARIABLE Y 32 STRING: A$ \ STRING: is not standard Forth. We made it to for you. 32 STRING: B$ \ *BTW Strings can have any name 32 STRING: C$ : TABS ( n -- ) 0 ?DO SPACE LOOP ; \ we don't have a TAB word so make one : PUSH-UPS ( n -- ) 0 ?DO CR LOOP ; \ do the same for new lines \ build the program "in our own words" : SAND ( -- ) 0 23 68 32 HCHAR ; \ 250 CALL HCHAR(24,1,68,32) : CAMEL ( -- ) 0 22 69 1 HCHAR ; \ 260 CALL HCHAR(23,1,69) : MAN ( -- ) 1 22 70 1 HCHAR ; \ 270 CALL HCHAR(23,2,70) : PEAK ( -- ) CR 15 TABS ." AB" ; \ 180 PRINT TAB(X+1);"AB" : BRICKS ( -- ) C$ " CC" & C$ $! \ 190 C$=C$&"CC" ( " needs a space, '&' is RPN " A" C$ & " B" & B$ $! ; \ 200 B$="A"&C$&"B" : WALLS ( -- ) 0 13 ?DO BRICKS CR I 1+ TABS B$ PRINT \ 210 PRINT TAB(X);B$ \ 220 C=C+1 \ 230 X=X-1 -1 +LOOP \ 240 IF C=13 THEN 250 ELSE 190 ; : PYRAMID ( -- ) A$ ="" B$ ="" C$ ="" \ reset the strings \ 160 X=13 \ 170 C=1 PEAK WALLS SAND CAMEL MAN 3 PUSH-UPS \ 280 PRINT \ 290 PRINT \ 295 PRINT SAND \ 296 CALL HCHAR(24,1,68,32) ; : WAVECLIP ( n -- n') DUP 111 > IF DROP 104 THEN ; \ clip n to valid wave char : NEXT-T ( -- n) T @ 1+ WAVECLIP DUP T ! ; \ inc T by 1 & clip : NEXT-Y ( -- n) Y @ 2+ WAVECLIP DUP Y ! ; \ inc Y by 2 & clip : INIT-WAVES ( -- ) \ 300 T=104 104 DUP T ! 2+ Y ! ; \ 310 Y=106 : RIVER \ flow the river loop INIT-WAVES BEGIN \ 320 T=T+1 \ 330 IF T>111 THEN 340 ELSE 350 \ 340 T=104 \ 350 Y=Y+2 \ 360 IF Y>111 THEN 370 ELSE 380 \ 370 Y=104 0 21 NEXT-T 32 HCHAR \ 380 CALL HCHAR(22,1,T,32) 0 22 NEXT-Y 32 HCHAR \ 390 CALL HCHAR(23,1,Y,32) 100 MS KEY? UNTIL \ 400 GOTO 320 ; : RUN \ Forth doesn't have RUN so we make one CLEAR \ 10 CALL CLEAR 13 6 5 COLOR \ 150 CALL COLOR[10,6,5] PYRAMID \ 500 GOSUB PYRAMID RIVER \ 600 GOSUB RIVERFLOW ; Edited May 12, 2017 by TheBF 2 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted May 12, 2017 Share Posted May 12, 2017 OK I promise this is the last one for Primer #2. Now we have removed all the BASIC comments to make space on the page. With that space we have shown you some alternative ways to format Forth code to make it read nicely. Forth source code is free form so you can express yourself in whatever way works for you. We also introduce: COLORS ( set# set# FG,BG -- ) SET# ( char -- set$) VREAD ( vdp-addr cpu-addr cnt -- ) (also known as VMBR) VWRITE ( cpu-addr vdp-addr cnt -- ) ( a.k.a VMBW ) With those words we create a way to save the all the patterns and restore them when the program ends. And we include a little TI BASIC "HONK" so it's official. \ DENILE #3 MORE factoring and Forth Style \ dialect: CAMEL99 Forth V 0.9 TRAINER \ with extensions to help TI-BASIC PROGRAMMERS \ give names to the pattern data \ Where BASIC uses strings, we will use HEX integers \ Each word drops 4 numbers onto the stack for CHARDEF \ to pickup HEX : RSLOPE ( -- n1 n2 n3 n4) 0102 0709 1F24 7F92 ; : LSLOPE ( -- n1 n2 n3 n4) 8040 E090 F824 FE49 ; : STONE ( -- n1 n2 n3 n4) FF92 FF24 FF92 FF49 ; : SAND ( -- n1 n2 n3 n4) AA55 4489 2002 4801 ; : CAMEL ( -- n1 n2 n3 n4) 0002 1735 7CFC 44AA ; : LITTLE-MAN ( -- n1 n2 n3 n4) 0008 081C 2A08 1414 ; : WAVE1 ( -- n1 n2 n3 n4) 0083 C7AE FBEF BDF7 ; : WAVE2 ( -- n1 n2 n3 n4) 0007 8F5D F7DF 7BEF ; : WAVE3 ( -- n1 n2 n3 n4) 000E 1FBA EFBF F6DF ; : WAVE4 ( -- n1 n2 n3 n4) 001C 3E75 DF7F EDBF ; : WAVE5 ( -- n1 n2 n3 n4) 0038 7CEA BFFE DB7F ; : WAVE6 ( -- n1 n2 n3 n4) 0070 F8D5 7FFD B7FE ; : WAVE7 ( -- n1 n2 n3 n4) 00E0 F1AB FEFB 6FFD ; : WAVE8 ( -- n1 n2 n3 n4) 00C1 E357 FDF7 DEFB ; \ ==================================================== \ *NEW* save & restore character set \ we will use some low expansion RAM at address HEX 2000 HEX 2000 CONSTANT CHARBUFF \ it's just that easy \ PDT is the pattern descriptor table in VDP RAM \ 800 is how many bytes it contains (2K bytes) \ VREAD is VMBR in TI speak \ VWRITE is VMBW in TI speak : SAVE-CHARSET ( -- ) ( from) PDT ( to) CHARBUFF 800 ( bytes) VREAD ; \ *NEW* "COLORS" restores mulitple charsets wow! : RESTORE-CHARSET ( -- ) ( from) CHARBUFF ( to) PDT 800 ( bytes) VWRITE 0 16 2 1 COLORS ; DECIMAL \ ==================================================== \ assign patterns to characters \ this is a big defintion but it is like a table of data \ so it is reads easily but it is CODE. \ *NEW* SET# calculates the set# for us : CHANGE-CHARS ( -- ) SAVE-CHARSET \ pattern char set# colors \ -------- ---- ---- ---------- RSLOPE 65 CHARDEF 65 SET# 11 1 COLOR LSLOPE 66 CHARDEF STONE 67 CHARDEF SAND 80 CHARDEF 80 SET# 2 12 COLOR CAMEL 88 CHARDEF 88 SET# 2 1 COLOR LITTLE-MAN 89 CHARDEF WAVE1 104 CHARDEF 104 SET# 5 6 COLOR WAVE2 105 CHARDEF WAVE3 106 CHARDEF WAVE4 107 CHARDEF WAVE5 108 CHARDEF WAVE6 109 CHARDEF WAVE7 110 CHARDEF WAVE8 111 CHARDEF \ -------------------------------------------- ; \ ==================================================== \ wave control variables VARIABLE T VARIABLE Y \ pyramid building strings 32 STRING: A$ 32 STRING: B$ 32 STRING: C$ : TABS ( n -- ) 0 ?DO SPACE LOOP ; : PUSH-UPS ( n -- ) 0 ?DO CR LOOP ; \ DOT is the Forth word to print a number. \ These words begin with '.' to tell us they print something \ A Forth style you can use if you want to : .SAND ( -- ) 0 23 80 32 HCHAR ; : .CAMEL ( -- ) 0 22 88 1 HCHAR ; : .MAN ( -- ) 1 22 89 1 HCHAR ; : .PEAK ( -- ) CR 15 TABS ." AB" ; \ this part is still very BASIC style \ We don't need to use strings for this : .BRICKS ( -- ) C$ " CC" & C$ $! \ 190 C$=C$&"CC" " A" C$ & " B" & B$ $! ; \ 200 B$="A"&C$&"B" : .WALLS ( -- ) A$ ="" B$ ="" C$ ="" 0 13 ?DO .BRICKS CR I 1+ TABS B$ PRINT -1 +LOOP ; \ ==================================================== \ Horizontal format code reads more like human language. \ Some people code Forth this way : .PYRAMID ( -- ) .PEAK .WALLS .SAND .CAMEL .MAN 3 PUSH-UPS .SAND ; : WAVECLIP ( n -- n') DUP 111 > IF DROP 104 THEN ; \ clip n to valid wave char : NEXT-T ( -- n) T @ 1+ WAVECLIP DUP T ! ; \ inc T by 1 & clip : NEXT-Y ( -- n) Y @ 2+ WAVECLIP DUP Y ! ; \ inc Y by 2 & clip : INIT-WAVES ( -- ) 104 DUP T ! 2+ Y ! ; : .WAVES ( -- ) 0 21 NEXT-T 32 HCHAR 0 22 NEXT-Y 32 HCHAR ; : .RIVER ( -- ) INIT-WAVES BEGIN .WAVES 200 MS KEY? UNTIL ; : RUN ( -- ) 15 SCREEN CHANGE-CHARS CLEAR .PYRAMID .RIVER HONK RESTORE-CHARSET ; 1 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted May 12, 2017 Share Posted May 12, 2017 These Forth tutorials are great, @Willsy! Just when you decided to start #2, I was in surgery for a complete right knee replacement. Today is the first time I have felt up to doing anything much online. I am sorry I was not here to participate from the start. I still may be a little slow to get back in the saddle. Again, wonderful effort. ...lee Quote Link to comment Share on other sites More sharing options...
+TheBF Posted May 13, 2017 Share Posted May 13, 2017 These Forth tutorials are great, @Willsy! Just when you decided to start #2, I was in surgery for a complete right knee replacement. Today is the first time I have felt up to doing anything much online. I am sorry I was not here to participate from the start. I still may be a little slow to get back in the saddle. Again, wonderful effort. ...lee I wondered why you were not here to correct all my mistakes. :-) So happy that you are back. Get back to 110% soon Lee. You are in my thoughts. B 1 Quote Link to comment Share on other sites More sharing options...
Willsy Posted May 13, 2017 Author Share Posted May 13, 2017 Ah! Great to "see" you back here, Lee. I had wondered why you hadn't chimed in. Hope you're back up to full-speed soon! #3 coming soon (I might write it up tonight). If you or Bruce want to add an article just go right ahead. Use "Forth: some title (#number)" as the format for the title, that way we can easily search for them later. 2 Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted May 13, 2017 Share Posted May 13, 2017 Thanks, Guys! It will not be long. I will have to think about starting one of these tutorials---I don't know. You fellows' teaching style is so much better than mine! I cannot seem to avoid too much detail, for one thing. Maybe I will limit my participation for a while to augmentation(!?). ...lee Quote Link to comment Share on other sites More sharing options...
Sinphaltimus Posted May 14, 2017 Share Posted May 14, 2017 Attempting this - : TOM ( n – n*2 ) 2 * ;: DICK ( n – n*4 ) 4 * ;: HARRY ( n – ) 9 + . ; 44 TOM. and I get this: Quote Link to comment Share on other sites More sharing options...
Sinphaltimus Posted May 14, 2017 Share Posted May 14, 2017 (edited) ROLL-DICE.png I got this one working - I missed the "test with" line at first. What am i doing wrong in the first post I put above? Edited May 14, 2017 by Sinphaltimus Quote Link to comment Share on other sites More sharing options...
+Lee Stewart Posted May 14, 2017 Share Posted May 14, 2017 You typed a word that does not exist in the dictionary: TOM. What you intended was: TOM . ...lee 1 Quote Link to comment Share on other sites More sharing options...
+TheBF Posted May 14, 2017 Share Posted May 14, 2017 Attempting this - : TOM ( n – n*2 ) 2 * ; : DICK ( n – n*4 ) 4 * ; : HARRY ( n – ) 9 + . ; 44 TOM. and I get this: Untitled.png Master Yoda says: Beware of the Forth must you be. For soon backing talkwards will you be! 1 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.