Jump to content
IGNORED

Forth Tutorials


matthew180

Recommended Posts

I was trying to get a program in Forth, but after 30 minutes of frustration I just gave up. This is what I wanted to do:

100 FOR I=1 TO 10
110 PRINT I
120 NEXT I

RUN
1
2
3
4
5
6
7
8
9
10

LIST
100 FOR I=1 TO 10
110 PRINT I
120 NEXT I

100 <down arrow>
100 FOR I=1 TO 80

RUN

Basically a simple program to show some numbers, then list and edit the program. It was not happening for me. Also, I tried a few things from some Forth tutorials on the 'net and could not get things like this to work:

."hello world"

I don't need a language reference, I need some simple programs to demonstrate the ideas of how things work in Forth. Anyone feel like posting a few? Just one concept per program please, and writing, saving, listing, and editing a program would be a nice starting point.

Link to comment
Share on other sites

Here's a "Hello, world" tutorial for TurboForth:

 

1. Create a "BLOCKS" file: "MKDSK DSK1.BLOCKS 80"

 

2. Edit a block: "2 EDIT"

 

3. Type the program:

: HELLO CR ." Hello, world." CR ;

FCTN+9 to exit the editor

 

4. Save the program: "FLUSH"

 

5. List the program: "2 LIST"

 

6. Compile the program: "2 LOAD"

 

7. Run the program: "HELLO"

 

All forth "words" (functions) are separated by spaces. So, in your exemple, a space was missing after the word [."]

 

 

Here's the "FOR I=1 TO 10 PRINT I NEXT I" program:

 

: TEST 11 1 DO I . LOOP ;

Edited by lucien2
Link to comment
Share on other sites

I have like 7 books on Forth and many years ago went through all the examples attempting to make them in TI Forth.

 

The problem was the many different flavors of Forth, like FigForth or WycoveForth and each flavor has different ways to doing the same thing, this is what really killed off Forth in the past.

 

C did not have this problem, you only had like Small C and C and C+, while Forth had like 12 or more flavors. No standards means people will hate using that language over time.

 

I still like Forth, but still have the same problem. Which flavor is best?

Link to comment
Share on other sites

Thanks for the info. A few questions:

 

1. Create a "BLOCKS" file: "MKDSK DSK1.BLOCKS 80"

What is "BLOCKS"? Is that a binary blob of data used by Forth to store all my programs? Or do I need a new name for each program?

 

2. Edit a block: "2 EDIT"

What is the "2"? Why not "1", or "6"?

 

4. Save the program: "FLUSH"

Is there a name associated with the program, if so, where is it specified?

 

5. List the program: "2 LIST"

 

6. Compile the program: "2 LOAD"

Again with the "2", what does that represent?

 

All forth "words" (functions) are separated by spaces. So, in your example, a space was missing after the word [."]

*That* is going to drive me crazy! I wonder if there is such thing as a language without quirks?

 

Here's the "FOR I=1 TO 10 PRINT I NEXT I" program:

 

: TEST 11 1 DO I . LOOP ;

How does 'I' get assigned the loop value, and how does '.' know to print 'I'?

Link to comment
Share on other sites

Forth use "blocks" to store any data, binary or text. A block is 1024 bytes.

 

I chose the block 2 because with TurboForth, the block 1 is auto-interpreted at startup (the interpreter executes "1 LOAD" at startup). I don't say auto-compiled or auto-executed, because in Forth it's the "same" process (The ":" word compiles a new word when it's interpreted).

 

Blocks doesn't have a name.

 

"I" is the loop counter, because the word "DO" takes control of the execution and has its own syntax. That's what is really original with forth. The language has no syntax, just words separated by spaces. But you can create words that change the interpreter behavior, like "DO" does. It executes everything between "DO" and "LOOP" X times and put the counter in I.

 

"I" puts the counter value on top of the stack and "." takes the value on top of the stack and prints it.

 

There are two steps, compilation and execution. When you create a new word, you can define the compilation behavior (create a new language, a new syntax) and/or the execution behavior (like all other languages do).

 

For exemple, during compilation the [."] word takes all the following characters until a ["] character is found and put them in the compiled program, and during execution, it prints that string on the screen.

 

Maybe Willsy could make this clearer if necessary. If you want a good tutorial, here's one: Starting Forth - Leo Brodie.

Edited by lucien2
Link to comment
Share on other sites

He he! Confusing eh? Forth will really screw with your brain, then you'll realise, it's actually really really easy, and there is no mystery.

 

The golden rule, as Lucien already said: There is no syntax. Only words seperated with spaces.

 

99.9% of the time, words 'communicate' via the stack. One word may leave something on the stack, to be consumed by the next word. In the case of the loop example:

 : TEST 100 0 DO I . LOOP ;

 

Firstly, 100 and 0 are placed on the stack (0 at the top). DO is then called. He is pre-programmed to pop the top two numbers off the stack. He knows that the top number (0) is the loop start point. He knows that the next number on the stack (100) is the loop termination point. He stores this data somewhere (we don't need to know where) so that he and his friends (I and LOOP) can use it later.

 

I is then called. I is not a variable. It is a word, just like everything else. I knows where DO keeps his stashed data (the 0 and the 100) and pushes the current loop value (0) to the stack.

 

. is just another word. It takes the top most value off the stack, and displays it.

 

LOOP takes the 0 stashed by DO, and adds 1 to it. It compares it to the loop termination value (100) and if less than, sets the PC back to the word just after DO.

 

That's it.

 

Even : and ; are words. ":" turns on the compiler, and ";" turns it off again.

 

Even variables are implemented as words:

 

VARIABLE FRED

 

Now type FRED at the keyboard. You get OK:1

 

Huh??????? We just *executed* a variable?? How is that even possible? Well, the word VARIABLE created a word *for us* in the dictionary, who is pre-programmed to return a pointer to his 'storage space' when executed. So the value you got on the stack after typing FRED is an address. A pointer. You can use this address with the words @ (fetch) and ! (store) to read and write data:

 

99 FRED !

 

(store 99 'in' FRED)

 

FRED @ .

 

(displays 99).

 

Head hurt yet? ;)

Link to comment
Share on other sites

Regarding blocks:

 

Code in Forth is usually distributed in source form, in a blocks file. It is simply compiled as and when it is needed. Therefore a Forth application may consist of many consequtive compiled compiled blocks of code, or many disparate (perhaps optional) blocks of code.

 

When a block is loaded, it is kind of treated as if the stuff in the block is being typed really fast by the user at the keyboard! The compiler actually doesn't know where the data has come from - block or keyboard.

 

This means you can treat blocks just like batch and/or script files. TurboForth will faithfully execute whatever it finds in the block.

 

For example, create your blocks file like this:

 

MKBLK DSK1.BLOCKS 80

 

(creates a blocks file with enough room for 80 blocks (80K))

 

S" DSK1.BLOCKS" USE

 

(the space after S" is important!)

 

Now edit block 1:

 

1 EDIT

 

In block type:

 

.( Hello world!) CR

: TEST 100 0 DO I . LOOP ;

TEST

 

Now exit the editor (FCTN 9)

Now save the block you edited to disk with FLUSH

 

Now reboot with COLD

 

What do you see?

 

Regarding blocks and FLUSH: TF has a 6 block cache. If you edit a block, it will sit in ram until such time as the cache is full, at which point it gets FLUSHed to disk for you. This is to minimise round-trips to the disk drive unless necessary.

 

If you edit a block and wish you hadn't just press QUIT (FCTN =) to abandon your edits.

 

May the Forth be with you.

Link to comment
Share on other sites

And I thought working I.T. would give you a bad case of Tourettes! Now all we need is TurboRPG-II for the TI and I think we would all be set.

 

Seriously, how about something a little less stressful, like explosive ordinance disposal? ;)

 

In all seriousness, I am still awaiting the release of the Turbo Forth module (unless I have missed it, already.)

  • Haha 1
Link to comment
Share on other sites

  • 2 weeks later...

May the Forth be with you.

 

Okay...now we need a t-shirt

 

Like this one? They're bloody expensive though - I don't set the prices, unfortunately. I ordered one for myself, so we'll see how it goes. I was going to get some done as gifts, but they're just too expensive... I looked at CafePress also, but their editor is rubbish :(

Link to comment
Share on other sites

  • 1 month later...

 

 

Hey Willsy... I've been having some issues with tf and I was hoping you could help me out with them... First of all, I've been trying to enter your example Graphics definition test... UDG. Here's my exact listing

 

: UDG

HEX 0607 0302 0703 0103 DECIMAL 4 40 DCHAR

HEX 0707 0E0D 0306 070E DECIMAL 4 41 DCHAR

HEX 00C0 E0C0 C0C0 80C0 DECIMAL 4 42 DCHAR

HEX E0E0 F0F0 C0E0 60E0 DECIMAL 4 43 DCHAR

;

: SHOW 1 GMODE UDG 40 EMIT 42 EMIT CR 41 EMIT 43 EMIT CR ;

 

As you can see... it's pretty much a carbon copy of your posted listing. Anyway, I have made a video of the attempt and posted it below... I also tried to combine the DCHARs into one using DECIMAL 16 40 DCHAR... as you described in another post. It gave me a different display, but still incorrect. **see picture below.

 

http://s830.photobucket.com/albums/zz222/Opry99er/?action=view&current=gforthprog.png&

 

 

http://s830.photobucket.com/albums/zz222/Opry99er/?action=view&current=tfissues.mp4

 

Anyway, I am hoping to get this cleared up because I'm going to try to make my train game in TF... I had started it in console BASIC, but the more I play with TF, the more I am learning to think Forth. Below I tried a short program to check the use of KEY in TF and it works swimmingly. It's about the simplest Forth program I've ever seen listed, but I was proud of it because I got it to work. Here's the listing:

 

: CHECK CR

DUP 49 = IF ." ONE COW " DROP ELSE

DUP 49 > IF ." MULTIPLE COWS " DROP

THEN THEN 8000 0 DO LOOP ;

 

: USINP PAGE ." TYPE IN A NUMBER "

CR ." TO SEE IF YOU HAVE ONE COW "

CR ." OR MULTIPLE COWS "

CR CR ." PRESS ENTER TO QUIT " ;

 

: PROG USINP KEY

DUP 13 = IF DROP PAGE QUIT ELSE

DUP 49 < IF CR ." INVALID ENTRY " 8000 0 DO LOOP ELSE

DUP 57 > IF CR ." INVALID ENTRY " 8000 0 DO LOOP DROP ELSE

CHECK

THEN THEN THEN ;

 

: TEST BEGIN PROG UNTIL ;

 

 

 

All this does is call for a keystroke within the parameters of 49-57 (the number keys) and returns a message based on which number is pressed. (one cow, multiple cows, or "invalid entry")... of course, with some graphics and a bit more logic, that little user input program might become interesting. =)

 

I want to do a train game where the user is responsible for handling switch tracks and turnoffs.... the goal is to increase complexity of the tracks and number of trains as the game progresses... If there is a collision, the game ends. Basically a high score game. =) That's what I'm hoping to accomplish as my first TF game.

 

I hope you can give me some idea why I can't get the graphics to work properly--- then we can talk about some further elements of the language... Looking forward to your book... Thanks for TF, Mark... I love it!

 

 

***Hope these pix work... crap computer at work.

Link to comment
Share on other sites

Yo!

It's nearly right, but not quite. Where did you get my code example from? Was it YouTube? I have some dim recollection of doing something like that.

 

I think the problem is that TF changed between recording that demo, and TF's release.

 

Here is the 'stack signature' for DCHAR

 

DCHAR ( address length ascii -- )

 

What this means is that DCHAR expects three items on the stack before it is called. These are (going from the top of the stack downwards)

  • ascii - the ascii code of the character to define
  • length - the number of words (not bytes) to define
  • address - the address of the character data

If we look at the code you posted:

 

HEX 0607 0302 0703 0103 DECIMAL 4 40 DCHAR

 

At runtime what you would get on the stack is 6 items (the 4 items of character data, the 4 and the 40). As you can see, DCHAR would interpret the last item of character data (0103 hex) as the address! It would then read 4 words from 0103 hex and load them into ascii 40! The other three words of character data would be left on the stack! Woops!

 

As I said, this is probably my fault, as I modified DCHAR to take an address.

So, we need to supply an address to DCHAR. The word DATA does just that:

 

: UDG
HEX DATA 4 0607 0302 0703 0103 DECIMAL 40 DCHAR
HEX DATA 4 0707 0E0D 0306 070E DECIMAL 41 DCHAR
HEX DATA 4 00C0 E0C0 C0C0 80C0 DECIMAL 42 DCHAR
HEX DATA 4 E0E0 F0F0 C0E0 60E0 DECIMAL 43 DCHAR
;
: SHOW 1 GMODE UDG 40 EMIT 42 EMIT CR 41 EMIT 43 EMIT CR ;

DATA is one of those "clever" words that does one thing at compile time (when the colon definition is being compiled to memory) and another thing at run time. It can only be used in a colon definition!

 

DATA needs to know how many items of data it is to compile, hence the number 4 after the word data (not before!), then your data items follow.

 

At run-time DATA pushes the address of the data that it compiled at compile time, and the number of data items to the stack. This information is used by DCHAR and the definition built.

 

So, this line:

 

HEX DATA 4 0607 0302 0703 0103 DECIMAL 40

 

Would result in something like the following on the stack:

 

A3B0 4 40

 

That is the address of the character data, the number of items of data (4) and the ascii code. That information is consumed by DCHAR and hey presto!

 

DATA is useful because you can use words like 'data variables' where just merely executing different words returns different data pointers. Useful for animation. I'll try to post an example later.

 

Anyway, hope that clears up the confusion! :)

 

Attached: The word glossary for TF V1.0

 

Mark

TurboForth v1-0 Words.pdf

Link to comment
Share on other sites

Thanks alot Mark... that answers ALOT... I was becoming frustrated. =) Printed the glossary. you are the freakin' man. Much appreciated... I will be playing quite a bit with this over the next couple of days and hope to have some results for you by Monday.

 

BTW, the example I showed came from your "release date" thread, I think... =) I'll be posting some stuff Monday... the next time I have access to the work computer. Holla...

Link to comment
Share on other sites

Nice one!

 

Also, I was thinking yesterday, when testing that a value is within a certain range, TF has a single word that will do this for you! Its called, er, WITHIN :)

 

WITHIN ( n low high -- true|false )

 

"Replaces n, low and high with TRUE if n >= low and < high, otherwise replaces n, low and high with false"

 

Example:

 

"Is 40 within 30 and 50?"

40 30 50 WITHIN . -1 ok:0

 

"is 40 within 40 and 50?"

40 40 50 WITHIN . -1 ok:0

 

"Is 50 within 40 and 50?"

50 40 50 WITHIN . 0 ok:0

 

The last one says false (0) because 50 is not < 50

 

Remember, you get a TRUE if your value is >= to low and < high. I'd personally prefer >= low and <= high, but the Forth-83 standard says < high, which is crap, but there you go...!

 

Of course, with Forth being Forth, you can just change the behaviour of words if you don't like them! So, if you really really want WITHIN to work on >= low and <= high, you can just re-define it!

 

: WITHIN ( n low high -- t|f) 1+ WITHIN ;

 

Here we "re-define WITHIN in terms of the old WITHIN". Crucially, we add 1 to the upper limit, so that if we do something like:

 40 30 40 WITHIN 

Then the upper limit is increased by one, so what actually gets executed is

 40 30 41 WITHIN 

and you get a TRUE!

 

That re-definition will probably confuse some people. Let's take another look at it. We'll use a bit of colour to illustrate.

 

: WITHIN ( n low high -- t|f) 1+ WITHIN ;

 

You would be forgiven for thinking that the word calls itself, recursively, forever, but it doesn't. The new WITHIN adds 1 to the top of the stack, then calls the old WITHIN, which does the work for us.

 

Simple eh? Utterly confusing when you don't know how Forth works, but once you know, you have an "Ohhhhhhhhhh!" moment! :)

Link to comment
Share on other sites

I really like the use of GCHAR and VCHAR and such... you really tailored this Forth to be recognizable to standard language TI users and I really appreciate that. I've been having a good time with this language, my friend... I want to pre-order a book... just give me a paypal address and I'll put it to it asap. =)

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...