Jump to content
IGNORED

Forth Tutorials


matthew180

Recommended Posts

Since Forth doesn't have arrays, the answer is no!

 

In Forth, when we make an array, all we're really doing is:

  • Using CREATE to create a word that returns a memory address when executed
  • Using ALLOT to reserve some space

Example:

 

CREATE MyArray 10 CHARS ALLOT

 

Reserves 10 bytes. The word MyArray, when executed, returns the address of the start of the ALLOTted block of memory. As with C et al, there are no bounds checking - Forth is *very* low-level, as you have no doubt learned by now!

 

We then access our "array" by using MyArray and adding an offset - exactly the way we would do it in machine code. So, to access the 5th byte (byte 4):

 

MyArray 4 + C@

 

Reads the byte at address MyArray+4

 

So, as you can see, it's just a block of memory, and ALLOT just changes the memory pointer. It doesn't zero anything.

 

You can use FILL to do that for you. I would use CONSTANTs to specify the array size, then you can update your arrays and any associated code by simply changing the constant:

 

100 CHARS CONSTANT ArraySize

CREATE MyArray ArraySize ALLOT \ reserve ArraySize bytes

MyArray ArraySize 0 FILL \ fill ArraySize bytes

 

Sometimes, you'll want to reserve 16-bit words, rather than bytes. That's when you use CELLS, as follows:

 

100 CELLS CONSTANT ArraySize

CREATE MyArray ArraySize ALLOT \ reserve ArraySize cells

MyArray ArraySize 0 FILL \ fill ArraySize cells

 

A cell is 2 bytes. So, all CELLS does is multiply the value on the stack by 2. ALLOT, FILL take their arguments in *bytes*. In the above ArraySize gets intialised to 200 (100 cells==200 bytes).

 

That's probably as clear as mud! It seems to tie a lot of people up in knots initially.

Link to comment
Share on other sites

Thanks Willsy. I'm actually quite familiar with arrays in Forth now because of my current graphics experimentations.

I'm still using TIF however given that TF has no bitmap support currently...

The reason for my question was that TIF requires a zero on the stack when declaring arrays, and I was wondering if it was somehow initializing them. With variable declarations, the number on top of the stack gets stored in the variable automatically.

Edited by Vorticon
Link to comment
Share on other sites

Thanks Willsy. I'm actually quite familiar with arrays in Forth now because of my current graphics experimentations.

I'm still using TIF however given that TF has no bitmap support currently...

The reason for my question was that TIF requires a zero on the stack when declaring arrays, and I was wondering if it was somehow initializing them. With variable declarations, the number on top of the stack gets stored in the variable automatically.

 

In TIF, the 0 only initializes the variable that is declared---actually, you can initialize it to whatever 16-bit value you want. That is only one cell, however. The rest of the array is only ALLOTted. 10 VARIABLE XX 30 ALLOT initializes the variable XX to 10 and reserves 30 additional bytes in the dictionary immediately following XX for a total of 32 bytes (16 cells) that can be referenced through XX and an offset. The additional 30 bytes (15 cells) beyond the first one, initialized to 10, contain whatever was in memory when they were ALLOTted.

 

The only difference between TF and TIF in this regard is that the initializing number is not used in TF, but must be used in TIF. TF initializes the variable to 0 with VARIABLE XX and TIF, as already stated, initializes to whatever number you put in front of VARIABLE as in 0 VARIABLE XX . You can use a variable name as the name of an array in TF the same way you do in TIF by following its definition with an ALLOTment of bytes to accommodate it.

 

The usual way to create an array in TF, I suppose, is to use CREATE to create the header for the array and ALLOT to reserve all the bytes or cells in the array as @Willsy explained earlier. I am pretty sure that you can do the same thing in TIF with CREATE , though I have not yet tried it. The word CELLS does not exist in TIF, but is easily created with

 

: CELLS (
n
---
n
*2 )

2 * ;

 

Anyway, as you can see, there are a couple of ways to create arrays and they do need to be initialized by the programmer if s/he cares. Well, I guess that dead horse has had a sufficient beating! :grin:

 

...lee

Edited by Lee Stewart
Link to comment
Share on other sites

Just a comment regarding VARIABLE... As you've realised, in TIF, VARIABLE takes an initial value. That was the de-facto standard for FIG Forth (TI Forth is a FIG Forth system IIRC). I think it's a nice feature. However, for some reason, the initial value was removed from the Forth 83 standard, and it was mandated that variables shall be initialised to 0. TurboForth conforms to the Forth 83 standard.

 

However, the cool thing about Forth is, if it doesn't behave the way you want, you can just change it :grin: So, if you want TurboForth to take an initial value for variables, just re-define VARIABLE:

 

: VARIABLE ( n -- )

CREATE LATEST @ >CFA >BODY ! ;

 

99 VARIABLE FRED

FRED @ .

99 ok:0

 

:thumbsup:

 

Now, the question is, can you see how it works? :grin: :twisted:

Link to comment
Share on other sites

well here is the help

 

: VARIABLE ( n -- )

CREATE LATEST @ >CFA >BODY ! ;

 

CREATE -

Reads forward in the terminal input buffer and creates a word in the dictionary whose run-time behaviour is to return the body address of the newly created word

 

LATEST -

Pushes the address of the latest dictionary entry variable.

 

@ -

Pushes the value read from address to the stack.

 

>CFA -

Converts dictionary address da to code field address cfa.

 

>BODY -

Given a CFA on the stack, >BODY returns the address of the body of the word. The “body” contains the “payload” of the word. Only applies to words created with <a href="http://www.turboforth.net/language_reference.html#create">CREATE (i.e. VARIABLE, CONSTANT, VALUE). It is not meaningful to apply >BODY to words that are not children of CREATE. >BODY may also be applied to words that utilise DOES> to carry out their work.

 

! -

Writes value to the cell at address.

Link to comment
Share on other sites

Since TIF does not have an I', how can one access the outermost loop counter in a 3 nested loop structure? I suppose I could always pull I and J off of the return stack onto the data stack, read the 3rd value off of the return stack (I'), then push back the I and J onto the return stack, but is there a better and less cumbersome way?

Link to comment
Share on other sites

Since TIF does not have an I', how can one access the outermost loop counter in a 3 nested loop structure? I suppose I could always pull I and J off of the return stack onto the data stack, read the 3rd value off of the return stack (I'), then push back the I and J onto the return stack, but is there a better and less cumbersome way?

 

Actually, I' is the 2nd value on the return stack, at least, that's the way it is for TI Forth. I haven't looked for how @Willsy keeps track of the loop index and limit for TurboForth. I' is the loop limit for the innermost loop. If you have 3 loops and want to reference the outermost loop index from the innermost loop, TIF would have it as the 5th value down on the return stack! If TF does loops the same way, we could write a TF assembler word for the 3rd loop index by directly reading the 5th value down on the return stack---perhaps, with a check that the return stack actually has at least 5 values!

 

...lee

Link to comment
Share on other sites

Actually, I' is the 2nd value on the return stack, at least, that's the way it is for TI Forth. I haven't looked for how @Willsy keeps track of the loop index and limit for TurboForth. I' is the loop limit for the innermost loop. If you have 3 loops and want to reference the outermost loop index from the innermost loop, TIF would have it as the 5th value down on the return stack! If TF does loops the same way, we could write a TF assembler word for the 3rd loop index by directly reading the 5th value down on the return stack---perhaps, with a check that the return stack actually has at least 5 values!

 

...lee

 

Yes, you're right, it is the 5th value on the stack. I don't recall seeing I' mentioned in the TIF manual. I might be able to come up with a workaround to avoid having 3 nested loops, but it won't be as neat. Alternatively, I could create a word like this:

 

: INDEX3 R> R> R> R> R> J' ! >R >R >R >R >R ;

 

where J' is defined as a simple variable. I'm not sure what kind of performance hit using this scheme repeatedly will result in, but it's worth experimenting with. It's becoming clear to me that I will have to eventually learn to use the TIF assembler at some point...

Edited by Vorticon
Link to comment
Share on other sites

Yes, you're right, it is the 5th value on the stack. I don't recall seeing I' mentioned in the TIF manual. I might be able to come up with a workaround to avoid having 3 nested loops, but it won't be as neat. Alternatively, I could create a word like this:

 

: INDEX3 R> R> R> R> R> J' ! >R >R >R >R >R ;

 

where J' is defined as a simple variable. I'm not sure what kind of performance hit using this scheme repeatedly will result in, but it's worth experimenting with. It's becoming clear to me that I will have to eventually learn to use the TIF assembler at some point...

 

A few things:

  1. You used up one of the values taken from the return stack with R> J' ! , so your code will cause a stack underflow and corrupt the return stack.
  2. J' as a variable will likely not be up to date when you check it. It is better to define it as a word that gets the current value every time it is executed.
  3. To keep the loop metaphor consistent, you should use a different letter for the third loop, something like K . J' implies the 2nd loop's limit, not the 3rd loop's index.
  4. You will want to leave a value on the stack when you execute INDEX3 or K if you define it as suggested in (2).

Perhaps (The following code does not work and will also corrupt the return stack! This is explained in later posts.)

 

: INDEX3
R> R> R> R>
R
>R >R >R >R
; or

:
K
R> R> R> R>
R
>R >R >R >R
;

Also, I' is not defined in TI Forth, it is mentioned (and defined) in "Appendix C" of the TI Forth Instruction Manual because the first edition of Brodie's Starting Forth explains it on pages 110 and 123.

 

...lee

Edited by Lee Stewart
Link to comment
Share on other sites

Ah yes, I missed that one :) The stack monster strikes again! I'm currently tackling 3D matrix transformations in floating point math, and I shudder at the thought of how many stack oversights I have made so far. I still have 2-3 functions to write then I'll be able to test everything.

I was not planning on using J' without an INDEX3 first, so it would have remained up to date. But of course, your suggestion of defining it as a word itself makes a lot of sense and now that you mentioned it so obvious! I think I'm still hampered by my old programming habits with classic languages, and Forth has so many ways to skin a cat it's not even funny.

The good news is that I'm starting to get fairly comfortable with it, too late unfortunately to write any decent application by Faire time...

Link to comment
Share on other sites

Ah yes, I missed that one :) The stack monster strikes again! I'm currently tackling 3D matrix transformations in floating point math, and I shudder at the thought of how many stack oversights I have made so far. I still have 2-3 functions to write then I'll be able to test everything.

I was not planning on using J' without an INDEX3 first, so it would have remained up to date. But of course, your suggestion of defining it as a word itself makes a lot of sense and now that you mentioned it so obvious! I think I'm still hampered by my old programming habits with classic languages, and Forth has so many ways to skin a cat it's not even funny.

The good news is that I'm starting to get fairly comfortable with it, too late unfortunately to write any decent application by Faire time...

 

3D? You should try entering a contest with this 4k Forth

http://neoscientists.org/~plex/win4k/index.html

Link to comment
Share on other sites

Well, hang on guys... If you know which CPU register points to the return stack, and you know where TIF's workspace is, then you can peek the return stack pointer directly, apply an offset, do a fetch and voila, there is your value.

 

Alternatively, TIF may have a word which returns the *address* of the top of the return stack? In TF it's called RP@. Again, apply an offset, do a fetch, and there's your value...

Link to comment
Share on other sites

Well, hang on guys... If you know which CPU register points to the return stack, and you know where TIF's workspace is, then you can peek the return stack pointer directly, apply an offset, do a fetch and voila, there is your value.

 

Alternatively, TIF may have a word which returns the *address* of the top of the return stack? In TF it's called RP@. Again, apply an offset, do a fetch, and there's your value...

 

When is TF's manual coming again? :grin:

Link to comment
Share on other sites

Well, hang on guys... If you know which CPU register points to the return stack, and you know where TIF's workspace is, then you can peek the return stack pointer directly, apply an offset, do a fetch and voila, there is your value.

 

Alternatively, TIF may have a word which returns the *address* of the top of the return stack? In TF it's called RP@. Again, apply an offset, do a fetch, and there's your value...

 

TIF does not have a pointer to the top of the return stack, just the bottom ( R0 ). I wrote one in assembler awhile back. I'll have to dig it up for y'all. It was actually very simple, as you said.

 

...lee

Link to comment
Share on other sites

In TurboForth, here is how you do K:

 

Assembly:

asm: K ( -- k)
sp dect,
14 r5 () *sp mov,
12 r5 () *sp a,
;asm

 

Or, in Forth:

: K ( -- k )
 RP@ 14 + DUP @ SWAP 2+ @ + ;

 

RP@ returns the address of the top of the return stack pointer.

 

Here's a test:

 

: test
10 0 do
 1 0 do
	 1 0 do
		 k .
	 loop
 loop
loop ;

 

In TIF it will be simpler.

Link to comment
Share on other sites

TIF does not have a pointer to the top of the return stack, just the bottom ( R0 ). I wrote one in assembler awhile back. I'll have to dig it up for y'all. It was actually very simple, as you said.

 

...lee

 

It must be tracked in a register? If it is, then you can get it just by reading the register direct (see my TF example above). A rather nice feature of our processor architecture!

Link to comment
Share on other sites

In TurboForth, here is how you do K:

...

In TIF it will be simpler.

 

Yeah. Much simpler in TIF! RP is the TIF ALC alias for R14, the return stack pointer. TIF workspace also starts at 8300h:

 

TI Forth assembler---

 

CODE K ( -- k)

SP DECT,

8 @(RP) *SP MOV,

NEXT,

 

High level TI Forth ( :_( This code does not work!! )---

 

HEX

: K ( -- k)

831C @ 8 + @

;

I haven't tried it yet. I will later---gotta go fix a gutter before the hurricane hits! :-o

 

...lee

Edited by Lee Stewart
Link to comment
Share on other sites

The above TI Forth assembler coded K and the compiled machine coded K work just fine. The words have identical dictionary entries.

 

I am not sure why the high-level TI Forth code above, which grabs the 5th number down the return stack, does not work. The only idea I have is that the return stack must be involved in the high-level definition of K so that the position on the return stack of the loop variables is either indeterminate or not safe to guess. The only place in the ALC source I can find that the return stack is used in a high-level definition is the code for : and ; . At the moment, I'm stymied!

 

...lee

Link to comment
Share on other sites

OK, it looks like this code will work:

 

HEX

: K 831C @ A + @ ;

DECIMAL

 

It goes one notch lower on the return stack, which must mean that executing the word pushes a number onto the return stack (probably : ) at the beginning, necessitating going one deeper to get the right number! This took way too much time and space here---sorry. :ponder:

 

...lee

Edited by Lee Stewart
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...