Forth Hints & Tips: Kaleidoscope

Discussion about ZX80 / ZX81 Software
User avatar
kmurta
Posts: 305
Joined: Tue Sep 01, 2009 5:04 am
Location: Belo Horizonte - BR
Contact:

Forth Hints & Tips: Kaleidoscope

Post by kmurta »

In the old days, some magazines had a tips section where interesting little programs were presented for the reader to type on their computer. Under this influence and aiming to demystify Forth a little, I open this topic with small fragments of code in Forth for the reader to type on their ZX81, rescuing a little of the spirit of that time.

I invite all forth enthusiasts to also contribute in order to enrich the topic.

Programs can target Toddy Forth-79, FIF Forth and also Husband Forth (H4TH).

I just ask that you maintain the following standard in your posts:

- When replying with your contribution edit the subject in this format:
Forth hints & Tips: title
- In the body of the message, specify the Forth variant for which it is intended:
Forth variant: TF79 / FIF / H4TH

So let's go to the first entry, a program originally in BASIC published in the magazine Sinclair Programs nr. 5 and converted to TF79:

Kaleidoscope.png


Forth variant: TF79

Two versions are presented: the first passes the arguments via values/variables; the second only uses the stack to pass the arguments.

Code: Select all

\ KALEIDOSCOPE

VALUE H   VALUE V  
VALUE RH  VALUE RV

: KALEIDOSCOPE
  BEGIN  PAGE
   250 RND 200 + 1 DO
    24 RND TO V  32 RND TO H
    23 24 V - + TO RV
    31 32 H - + TO RH
     H  V  1 PLOT
    RH  V  1 PLOT
    RH RV  1 PLOT
     H RV  1 PLOT
   LOOP  KEY ASCII C = NOT
  UNTIL ;

Code: Select all

\ KALEIDOSCOPE

: KALEIDOSCOPE
  BEGIN  PAGE
   250 RND 200 +  1 DO
    32 RND  24 RND
    OVER 32 SWAP - 31 +
    OVER 24 SWAP - 23 +
    2OVER     1 PLOT
    2DUP      1 PLOT
    -ROT SWAP 1 PLOT	
              1 PLOT 
   LOOP  KEY ASCII C = NOT
  UNTIL ;

After the picture is finished, use the "C" key to continue and any other key to end the program.
1 x ZX81, 2 x TK85 , 1 TK82C, 1 TK95, 1 x Alphacom 32 printer, 1 x ZXpand
ZeXtender board, Joy81 - Programmable Joystick Controller, Turbo Sound 81
http://zx81.eu5.org
https://toddysoftware.itch.io/
Moggy
Posts: 3267
Joined: Wed Jun 18, 2008 2:00 pm

Re: Forth Hints & Tips: Kaleidoscope

Post by Moggy »

A nice routine, Kelly short and sweet. :D

It should be mentioned for those unfamiliar that it is to long to type straight into the console window, it produces an error after typing 4 lines and has to be done in the editor then compiled as per the manual.

I would like to offer some contribution, Kelly, but unlike most people I have no interest in games or their creation and use Forth more for mathematical problems and some scientific work, I'm afraid anything I offer would likely put people off Forth for ever or just bore them to death due to lack of any graphical content. :oops:
User avatar
kmurta
Posts: 305
Joined: Tue Sep 01, 2009 5:04 am
Location: Belo Horizonte - BR
Contact:

Re: Forth Hints & Tips: Kaleidoscope

Post by kmurta »

Moggy wrote: Tue Mar 05, 2024 4:59 pm It should be mentioned for those unfamiliar that it is to long to type straight into the console window, it produces an error after typing 4 lines and has to be done in the editor then compiled as per the manual.
This is mentioned in section 1.5.1 of the manual. The error will only occur if the last word of the 4th line is broken. See in the video how I enter the KALEIDOSCOPE definition directly through the terminal:

kscope.gif

And it is not even necessary to enter all the words like this, you can enter one line at a time, typing NEWLINE at the end of each line:

: KALEIDOSCOPE <NEWLINE>
BEGIN PAGE <NEWLINE>
250 RND 200 + 1 DO <NEWLINE>
32 RND 24 RND <NEWLINE>
.
.


and so on.

The explanation is that : creates a new entry in the dictionary with the name that follows it and then switches Forth to compiled mode, where everything that follows will be compiled in memory until ; which returns Forth to interpreted mode. But of course it is much safer and more practical to use the Editor and I recommend that everyone do so.

And as for your contribution, don't worry because it doesn't need to be a game or generate graphic content, I believe that mathematical and scientific applications can be very interesting. The goal is to present short programs that can be quickly typed and tested so that people can become more familiar with Forth. ;)
1 x ZX81, 2 x TK85 , 1 TK82C, 1 TK95, 1 x Alphacom 32 printer, 1 x ZXpand
ZeXtender board, Joy81 - Programmable Joystick Controller, Turbo Sound 81
http://zx81.eu5.org
https://toddysoftware.itch.io/
Moggy
Posts: 3267
Joined: Wed Jun 18, 2008 2:00 pm

Re: Forth Hints & Tips: Kaleidoscope

Post by Moggy »

Ahh understood Kelly. :D

In which case I shall put something together and post it.
Moggy
Posts: 3267
Joined: Wed Jun 18, 2008 2:00 pm

Conditional test using /MOD.

Post by Moggy »

The following is a very simple exercise to test whether a succession of numbers are divisible by 3, 4 and 5.

Not very useful in its self but uses I think a novel way to achieve this by using the remainder or modulus of a set of computations, added together, to decide if the number in question being divided passes the criterion required for divisibility by all three divisors.

The routine is very compact but uses variables, summation, division with remainder culminating in a conditional test for zero after divisibility.
All of this is runs within a traditional DO...LOOP the start and limit of which can be altered by the user before compilation as can the three divisors used in the test.


The number/dividend to be tested is provided by the index of the loop itself. For example in BASIC we have the FOR NEXT loop, in Forth we have the DO..LOOP. In BASIC we set the start and the limit thus, FOR X = 1 TO 1000 ( some work) NEXT X.

Whereas in Forth the method is... 1001 1 DO (some work) LOOP. Each pass through the loop increases the loop index ( I ) by 1.

(Note the reversed start and limit of the loop as opposed to BASIC as well as the limit being one more than required which is a feature of how Forth handles the loop)

Forth variant: TF79

The program.

First we have to declare and name the three variables used. In this case I have called them THREE FOUR and FIVE. You can name them something else if you like. We do this so....


VARIABLE THREE (NEWLINE/ENTER)

VARIABLE FOUR (NEWLINE/ENTER)

VARIABLE FIVE (NEWLINE/ENTER)





The values they contain are unimportant for now as the program itself will attribute values to them.

We now can create the word we will use for the routine to be compiled.

: MULT345 PAGE 1001 1 DO
The colon denotes the start of the word to be compiled, PAGE is the Forth word akin to BASIC's CLS followed by the limit and start of the loop then the word DO to start the loop.

What follows are three identical commands albeit with three different values.

I 3 /MOD SWAP THREE !
The index/loop counter is divided by the number 3 using the word /MOD. this places on the stack the result of the division (Quotient) and the remainder (modulus) This is followed by the word SWAP. This word takes the top two stack values and reverses their position, the reason for this is because we require the modulus to be on top of the stack. TF79 places the Quotient on top of the stack and the modulus as the second item, some other Forths do the reverse of this.

Next we have.

THREE @ FOUR @ FIVE @ + +

The Forth operator @ is used to fetch a value and place it onto the stack. in this case it is preceded by the name of the variable THREE, this simply means fetch the value of the variable called THREE and place that value on the stack.
This is repeated for the variables FOUR and FIVE. we now have the three values of these variables on the stack.
Following this we have two addition operators which simply adds the two topmost stack items together then the result of that to the third.

To recap, earlier on in the routine these three variables were loaded with the modulus resulting from the division by 3,4 and 5.
If the number pointed to by the index was divisible by say for example 3 then that would mean the modulus or remainder of that calculation would be zero and this is what would be stored in the variable used for that divisor. If the number to be divided is dividable by 3,4 and five with no remainder then all three variables would contain zero and their summation likewise.

It is this summation that is conditionally tested by what follows..
0= IF CR I . ." DIV, BY 3-4-5" THEN

If the result of this summation is equal to zero then the 3 number divisibility test has passed and the screen display reflects this by printing the number that passes the test followed by a short sentence confirming it before moving on through the loop.

Note how Forth performs this in reverse. Using BASIC we would say if X =0 THEN PRINT X; " IS DIV, BY 3-4 AND 5" Forth as we can see does the reverse of this and is something to remember when getting to grips with the language.

0= IF CR I . ." DIV, BY 3-4-5" THEN

IF means exactly what it says, in this case IF the result of the summation equals zero, CR is the carriage return Forth word so as to move to the next line to be printed. The I . is what we use in Forth to print the current value of the loop counter/index The word THEN is used as in BASIC albeit at the end of what is to be performed should the condition in the IF statement be met. Should the condition not be met, nothing is printed and the routine loops around to the next value to be tested.


DROP DROP DROP LOOP ;


The routine culminates with loop end and a semi colon which indicates the end of the compilation however some explanation of the DROP word should be given and why I used the word 3 times.


Drop as it may be implied simply drops/discards the top stack item. When doing any computations in Forth any numbers used are placed on the stack and although once computed, the top stack item is generally the result we are seeking, if one is not careful the stack starts to grow in size downwards unless one starts to DROP items from it or print the result of a computation which also will reduce the stack. This stack downward growth will eventually impinge on the system area causing a whiteout.

Whilst using a small sample of numbers to be tested this may not manifest itself but I found that when testing numbers in the tens of thousands, unless three DROPs were used the system would wipe out around the 2000 mark.

To use type MULT345 (NEWLINE)
If you have got this far then thanks for reading and I hope the example wasn't too dry for you. :D



Code: Select all

\ MULT345 


VARIABLE THREE
VARIABLE FOUR
VARIABLE FIVE

: MULT345 PAGE 1001 1 DO 

I 3 /MOD SWAP THREE !

I 4 /MOD SWAP FOUR !

I 5 /MOD SWAP FIVE !

THREE @ FOUR @ FIVE @ + +

0= IF CR I . ." DIV, BY 3-4-5"  THEN 

DROP DROP DROP LOOP ;
     
User avatar
kmurta
Posts: 305
Joined: Tue Sep 01, 2009 5:04 am
Location: Belo Horizonte - BR
Contact:

Re: Conditional test using /MOD.

Post by kmurta »

Congratulations, Moggy! Your teaching is excellent and I believe that even those who still don't understand Forth have managed to understand your explanation! Very good!

Just a tip: since the quotient of the division is discarded, instead of /MOD, you could use the word MOD which leaves only the remainder of the division on the stack. Consequently, the use of SWAP and DROP would become unnecessary.

But for educational purposes, your example is perfect!
1 x ZX81, 2 x TK85 , 1 TK82C, 1 TK95, 1 x Alphacom 32 printer, 1 x ZXpand
ZeXtender board, Joy81 - Programmable Joystick Controller, Turbo Sound 81
http://zx81.eu5.org
https://toddysoftware.itch.io/
Moggy
Posts: 3267
Joined: Wed Jun 18, 2008 2:00 pm

Re: Conditional test using /MOD.

Post by Moggy »

kmurta wrote: Sat Mar 09, 2024 8:49 pm Congratulations, Moggy! Your teaching is excellent and I believe that even those who still don't understand Forth have managed to understand your explanation! Very good!

Just a tip: since the quotient of the division is discarded, instead of /MOD, you could use the word MOD which leaves only the remainder of the division on the stack. Consequently, the use of SWAP and DROP would become unnecessary.

But for educational purposes, your example is perfect!
Thanks for the kind words Kelly.

It never entered my head that MOD was all that was required, it just shows I have much to learn from the master! :lol:
Moggy
Posts: 3267
Joined: Wed Jun 18, 2008 2:00 pm

Decimals in Forth. Part one.

Post by Moggy »

Toddy Forth79.
Part one.

Forth is, are most of you maybe aware, is generally regarded by many as an integer only language, lacking decimals it can seem at first to be off putting to those learning Forth but require a decimal point or two to raise its head.

There are however a couple of nice ways to circumvent this problem.

Most Forths ( Toddy is no exception but alas H4th is the exception ) contain surprisingly enough a system variable that keeps a track of, for the sake of this explanation, an imaginary decimal point. More of which later, there is a second method of which some explanation is needed I think.

In Forth, using TF79 as the example, numbers can be expressed in simple terms in one of two ways. They can be either what is called a "SINGLE" number or a "DOUBLE" number.

Single numbers are in this case 16 bit and lie in the range -32768 to 32767 (signed.)

Double numbers are 32 bit with a range of -2147483648 to 2147483647, again signed.

There are other ways of expressing values but we shall stick with signed numbers for now.

Single numbers are written as one would expect EG 1 2 3 4 5....

Double numbers however have a point/period amongst the figure like so 1. 2. 3. 4. and so on.

This "dot" tells Forth that whatever the value please treat it as 32 bit and the usual manner of writing/displaying would be for example..100.
Simply put this is one hundred double number format. However just imagine if you slid that point two places to the left EG 1.00
You could be forgiven for thinking that this now represents one point zero zero. Alas Forth still regards the figure as one hundred but to our human eyes it looks like a proper decimal number and as mentioned at the start there are two ways we can take advantage of how this looks to us to achieve decimal arithmetic.

At this point I should state that TF79 does contain a floating point package if one wants ease of use however this preamble of mine is to show how things were achieved before such packages were available.

Should there be enough interest I will complete this in a second part, I find it quite taxing at my age, typing, trying to find ways of passing on what I have learned re Forth, checking my errors etc so if the interest is not there for discussing it on a public forum or perhaps people would rather learn things in private then I will bow out.


That said, the two ways of achieving decimal arithmetic are as follows..

(1) Number formatting.

2) The use of the DPL system variable.

Sadly I can find no way of using either method with H4th so this TF79 only.

Just as a teaser I will leave with a tiny tiny snippet of code to show what goes on inside the DPL variable with a way to use this effectively, later as well as an explanation of number formatting.

Type what you see in the code code box, then enter/newline, and look at the number printed on the screen. Can you work out how DPL is working?
Try entering other numbers with different amounts of places after the point to confirm that DPL indeed does store this "imaginary" amount of places after the point but remember that something that looks like 12.123456789 to our human eyes is actually regarded as an eleven digit integer by Forth, a point to remember during computations if one does not wish to exceed Forth's number ranges.

1.23 DPL @ .
1.23 is the decimal under examination followed by the expression DPL @. @ simply tells Forth to fetch the value found at say an address or in this case the DPL system variable, the point simply tells the system to print that value.

Code: Select all

\ System variable DPL.


1.23 DPL  @   .
Last edited by Moggy on Mon Mar 11, 2024 3:33 pm, edited 1 time in total.
User avatar
kmurta
Posts: 305
Joined: Tue Sep 01, 2009 5:04 am
Location: Belo Horizonte - BR
Contact:

Re: Forth Hints & Tips: Kaleidoscope

Post by kmurta »

Delighted with these Forth lore pills, it's a great addition to the manual! Please continue.
1 x ZX81, 2 x TK85 , 1 TK82C, 1 TK95, 1 x Alphacom 32 printer, 1 x ZXpand
ZeXtender board, Joy81 - Programmable Joystick Controller, Turbo Sound 81
http://zx81.eu5.org
https://toddysoftware.itch.io/
Moggy
Posts: 3267
Joined: Wed Jun 18, 2008 2:00 pm

Decimals in Forth. Part two.

Post by Moggy »

Part two.

Continuing on with our exploration of decimals in Forth we shall explore number formatting and the use of the system variable DPL.

At this point I would like to recommend a most excellent Forth publication which is not only based on Forth-79 but whose superb, pertinent examples and exercise contained therein are compliant with Toddy Forth-79. One of the main grumblings concerning Forth is the lack of standardisation between dialects, even those supposedly of the same dialect can contain words not compliant with each other, and there is nothing more infuriating than typing in a long listing only to bump into a word or operator not included in your particular Forth tongue! ?DO anyone?

Just imagine if there were three different versions of Z80 assembly language, all doing the same thing, but unable to communicate with each other!


The book, entitled "The Complete Forth" by Alan Winfield is available in PDF format and can be found here...

https://ia803101.us.archive.org/6/items ... _forth.pdf

I was intending to write about the use of the DPL variable but given my penchant for, no doubt noticeable, verbosity I feel it's best to let Professor Winfield do the talking.

The relevant chapter in his book can be found at 8.4 pages 80 through to 85.

These pages also contain a section on number formatting and so should suffice as a lesson however I feel I should earn my metaphorical salt and write something of my own as promised, which although identical in concept to Winfield's text goes slightly further in specifying how to have more than two places after the point, with something about the magnitude of numbers to follow later.

Number formatting.

Remember how in part one I introduced the concept of placing the point/period used in double numbers in such a way as to look like a proper decimal number to our human eyes?

Well thankfully TF79 contains such a method of making this a reality, consider the statement below for example...

: 2PAP <# # # 46 HOLD #S #> TYPE SPACE ;


This is a simple formatting routine to have two numbers after the point, useful for say currency work. Lets break it down.

: 2PAP

This is a colon definition not a direct console command and the only way number formatting in this manner is allowed so we start with the colon.
Next we give the definition a name, in this example 2PAP. A term I use to mean "TWO PLACES AFTER THE POINT." It thus becomes obvious that for say three numbers after the point one could call it "3PAP" and so on.

< # # #

The definition opens with a less than symbol then a hash. This tells Forth that number formatting has now begun. what follows next are two more hash symbols, this tells forth that we are working with two decimals after the point.

46 HOLD

The word HOLD tells Forth that it should display the symbol given by the ASCII value that precedes it, in this case 46 representing a full stop/point or period. You can of course use other values depending on what symbol you want as the delimiter, some prefer the colon for example.

#S #>

The final part of the formatting process consists of a hash followed immediately by the letter S without space, a space, then a hash then greater than symbol again no space in between them. The hash followed by the less than symbol opened up the formatting process so it must therefore be inferred that the hash greater than combination closes the same process, however the hash S combination requires some explanation of how forth works out and prints the decimal numbers concerned

#S

It is good time to remember Forth's reverse way of working, number formatting is no exception.

The definition for two places after the point we are working on has the two hash symbols representing the decimal places required for places AFTER the point at the beginning of the definition . This is because in Forth the routine works out the two places after the point first in any computation using it.
It then uses the HOLD command to place the delimiter, so what about the remaining numbers BEFORE the point? There may one, two or ten places before the point so how do we work that out?

The answer quite simply lies with the hash S operator which simply tells Forth, now that you have worked out and printed the two places after the point, the delimiter be it point or colon, what ever figures are left in the computation just place them BEFORE the point.

A most elegant way of working out what lies before the point without user intervention makes the hash S operator quite a powerful one.


TYPE SPACE ;

This simply prints out everything in the correct order and is explained more fully in Winfield's book as are all the above points.
The semi colon closes the definition. I have just tried to present things in a more simplistic fashion than the book, hopefully to encourage people to take up Forth and gain some understanding before delving deeper.


IMPORTANT TO NOTE.

When entering figures using this method it should be remembered that however many places there are after the point in the definition then any figures entered/used should also have the same amount of places after the point other wise spurious/incorrect results will apply even if it is a single number such as say 1 or 2.

EG for two places after the point enter figures as so... 1.00 or 2.00 not 1. 2.

The above is for TF79 0nly as H4th, surprisingly for a Forth that is supposedly based on FIG Forth doesn't contain the DPL variable nor the means of number formatting described, both of which are reputedly FIG Forth in origin.


Usage.

Example.

1.23 1.23 D+ 2PAP (enter/newline)


Next ( Interest not withstanding )

The magnitude of numbers.

Code: Select all

\ 2PAP

: 2PAP  <#   #  #   46 HOLD  #S  #>  TYPE SPACE ;



Post Reply