Jump to content

Photo

Forth: VARIABLES and the STACK (#4)


5 replies to this topic

#1 TheBF ONLINE  

TheBF

    Chopper Commander

  • 155 posts
  • Location:The Great White North

Posted Tue May 16, 2017 8:04 AM

In another post we read about a person who in interested in translating a game from BASIC to Turbo Forth

...

"But I do define and use a lot of variables. So I'll be exploring ways to eliminate the need to do so."

 

There is no law of the universe that says you cannot write Forth programs and use variables just like you would in BASIC.

Variables in FORTH, as in BASIC are GLOBAL. Any part of the program can get at them if it needs to.

 

So go ahead, write a Forth program using all the variables you used in BASIC. It will work but there will be side-effects:

1. You will use some extra RAM for each variable and it's name

2. It will be more difficult to make your program connect together like Lego blocks

 

Variables are always needed to record something the program wants to keep a permanent record of.

Like say, the Score in the game needs to be recorded, and maybe the highest score I have gotten in the session.

In fact Forth systems have a large number of variables built in to keep track of internal things.

For example here is a partial list from CAMEL99 Forth so you can see some things that need to be kept in a variable

 (these may be different in other Forth systems)

( also notice that any character except space, can be used to name a variable)

  TFLAG         \ TASK flag awake/asleep status
  JOB           \ Forth word that runs in a task
  DP            \ end of dictionary pointer
  HP            \ used in number to text conversion
  CSP           \ remembers Current Stack Position (error checking) 
  BASE          \ the current number base (decimal/hex/binary/octal)  
  >IN           \ interpreter pointer
  VADR          \ holds VDP RAM address of the cursor
  C/L           \ Chars per line (32 or 40 depending on VDP mode)
  VROW          \ current VDP row
  VCOL          \ current VDP column
  C/SCR         \ chars per screen >300 or 3C0
  VMODE         \ keeps track of the video mode we are in
  VTOP          \ top of video screen memory. defaults to 0
  VBOTM         \ "bottom" VDP row address for current MODE (HEX 300 or 3C0)

So you can see that variables have a place in Forth.

Then what's the stack for?

What if all sub-routines had a secret place where they could dump their results and other sub-routines

just knew where that secret place was and picked up what they needed from there?

 

That's where the Forth stack is used.

 

Example:

 

Forth has a simple word that prints 1 character called EMIT.

Emit knows to look on the top of the stack to get a character to print out.

No variable required.

 

In Forth we have a way to describe that with "stack comments".

 

The stack comments for EMIT look like this

 

EMIT ( char -- )     This means EMIT takes 1 char off the stack and puts nothing back.

 

GCHAR is in most TI-99 Forths as well.  The stack comments for GCHAR are:

 

GCHAR ( y x -- char)  GCHAR needs 2 inputs and gives back 1 character on the stack.

 

Here is where the Lego blocks start to happen.

 

if you wanted to read a character from the screen and re-print it at the cursor position you can feed the output of GCHAR directly into EMIT like this.

 

3 5 GCHAR EMIT

 

Using the stack lets these words plug together like lego blocks.

 

See the attached screen capture where we used HCHAR to put characters on the screen

and then read them and printed them at the cursor position.

 

 

 

Attached Files



#2 Willsy ONLINE  

Willsy

    River Patroller

  • 2,949 posts
  • Location:Uzbekistan (no, really!)

Posted Tue May 16, 2017 8:21 AM

I LIKE IT!!!!

#3 Asmusr OFFLINE  

Asmusr

    River Patroller

  • 2,267 posts
  • Location:Denmark

Posted Tue May 16, 2017 8:27 AM

So where are the variables stored, and how about arrays?



#4 Lee Stewart OFFLINE  

Lee Stewart

    River Patroller

  • 3,144 posts
  • Location:Silver Run, Maryland

Posted Tue May 16, 2017 8:41 AM

Variables are words, so they are stored in the dictionary, like all other words.  You can allot space in the dictionary immediately following the variable definition with ALLOT or by compiling values with , (comma) to build arrays that start with the variable's address.  It is all on the user to manage the size and dimensions of the array because there is no built-in array handling in the Forth kernel.  Systems have certainly been devised to build and de-reference arrays.  I believe Willsy has done so, but I'm not sure.

 

...lee



#5 TheBF ONLINE  

TheBF

    Chopper Commander

  • Topic Starter
  • 155 posts
  • Location:The Great White North

Posted Tue May 16, 2017 10:05 AM

So where are the variables stored, and how about arrays?

 

As Lee stated, the variables are in the common memory space called the dictionary.

 

<ADVANCED TOPIC>

But just like in Assembler, Forth variables are just an address.

​ie: they return an address to the stack not the contents. 

The dictionary name is the label, but it's in a linked list of other labels.

However you could use any address you cared to as a variable

 

So you could create an array manually like this:

\ purely academic example

CREATE MYDATA   100 CELLS ALLOT

\ now create a "calculator" to return the address of addr[x]
: []      ( n addr -- addr[n] )   SWAP 1 CELLS * +  ;

\ usage:

  99 7 MYDATA [] !   \ store 99 into cell 7 
  7 MYDATA [] @      \ return the contents of cell 7

</ADVANCED TOPIC>


Edited by TheBF, Tue May 16, 2017 10:18 AM.


#6 Willsy ONLINE  

Willsy

    River Patroller

  • 2,949 posts
  • Location:Uzbekistan (no, really!)

Posted Tue May 16, 2017 10:31 AM

Attached File  tf.png   21.84KB   0 downloads

 

  1. we create a variable called fred.
  2. we execute fred. It pushes its address to the stack. We then use $. (hex print) to display it on the screen in hex.
  3. we execute fred. It pushes its address to the stack. We use @ (peek/fetch) which takes an address off the stack, reads that address, and pushes the value at that address to the stack
  4. we push 99 on to the stack. We execute fred. It pushes its address to the stack. We use ! (poke/store) to poke the 99 into the address of fred.
  5. we execute fred. It pushes its address to the stack. We use @ (peek/fetch) which takes an address off the stack, reads that address, and pushes the value at that address to the stack. We then use . (print) to display the number on the top of the stack in decimal.

To increase fred by 1:

fred @ (fetch value of fred)
1+ ( add 1)
fred ! ( store it back in fred)

Or, horizontally:

fred @ 1+ fred !

(there are other ways to do that as well, using +! etc, but that's enough for now!)


Edited by Willsy, Tue May 16, 2017 10:32 AM.





0 user(s) are browsing this forum

0 members, 0 guests, 0 anonymous users