Skip to main content

It's a great nuisance 
that knowledge can only 
be acquired by hard work.

   W. Somerset Maugham.

                        STRENGTH IN FORTH

                             Part 7

              A CLOSER LOOK AT FORTH'S DIGESTION (2)

Let's  hope  Heaven dìd protect you.  Then you will be in  a  good 
mood  to understand the following explanation.  This time we  will 
turn to the Editor.  As our examples grow more and more,  it would 
be  nice to have an opportunity to write programs in such  a  way, 
that  the  testing will cost less  efforts.  Entering  definitions 
directly  from the keyboard is  tedious and time consuming.  If  a 
definition  turns out to be incorrect,  you will have to  {FORGET} 
it,  and then define at anew,  with all the typing  involved.  And 
secondly,  some  definitions are well worth saving for later  use. 
The Editor now provides both features.

THE UNDEDICATED FOLLOWER

I told you before,  that in FORTH many things follow the Standard. 
The Editor does not. There isn't any prescription of how an Editor 
should  work.  So,  there  are many different ways in  which  many 
different Editors will work.  Some general remarks however can  be 
made.
--1--:  an Editor is never part of the language.  That applies for 
Basic as well as for Pascal and FORTH. Formerly most homecomputers 
were started up in Basic, more specific in the Basic-editor. So it 
may seem  as if that were the 'normal' way of live for a computer, 
it  wasn't.  Most  interpretive computer languages  have  build-in 
Editors,  compiling languages have not.  FORTH may have a build-in 
Editor, but he might as well have not.
--2--:  the Editor is created to ultimately deliver code, that can 
be saved onto a disk (or long ago,  onto tape).  The disk-handling 
words in FORTH are part of the Standard.  In FORTH the  disk-space 
is  treated  as  virtual memory.  The  communication  between  the 
computer and the disk is fully automised and standarised.  

--3--:  a  description of a standard disk-handling should  contain 
the  following features.  The diskspace is divided into so  called 
'screens'.  A  screen  is as large as  1024  bytes  (=characters), 
consisting of 16 lines of 64 characters.  The amount of screens on 
one  disk  depends  on  the  total  of  diskspace available  on  a 
particular  machine.   The  communication  between  disk  and  the 
computer  goes  through 'buffers' in the  computer's  memory.  The 
default number of buffers is two,  but can be easily changed, each 
buffer of course ìs  consuming 1028 bytes of memory.  The  machine 
on  hand - the good-old ST - has plenty of memory to allow you  to 
expand  your number of buffers.  A buffer is a region of memory  - 
here 1028 bytes - which is set apart to temporary store data.
The  number  of  1028 bytes is caused by  4  bytes  which  contain 
information on the state of the buffer and on the screen loaded.
Your  FORTH  should  contain a word to initialise  the  disk  into 
screens.  Suppose  the word {SCREENS} initialises a disk  in  your 
system.(I will tell you,  that the FORTH I use most doesn't have a 
screen-oriented  Editor.  He didn't have any Editor at all when  I 
happened  to make his acquaintance.  He had some code  to  include 
some  external Editor I had to try and find myself. I then  had to
format  a disk,  call the Editor and I could write my words  in  a 
file-style.)
BLACK THOUGHTS

It  will  be  worthwile to simulate  a  editing-session,  using  a 
screen-oriented editor first, so we can learn some editing tricks, 
which are not clear from the editor at first sight.  Some of those 
tricks may as well count  for not screen-oriented editors.
Well,  let's say your disk is screen-ready: the word {SCREENS} put 
320  screens on it.  Of course all screens are empty.  How to  get 
such a screen visualised on your monitor ?  A common way to do so, 
is  to  type 3 {BLOCK}.  This will load screen number 3  into  the 
first  inputbuffer,  leaving the address of the first byte of  the 
data  area  of that buffer on the stack.  Till now  you  will  see 
nothing  at  all,  because  you didn't tell FORTH  to  {TYPE}  the 
contents  of  screen 3 on your terminal.  Get FORTH typing  by  64 
{TYPE},  which  will type the very first 64 characters you  stored 
into the buffer.  In fact the first line.  An empty one of course. 
Before  we  are teaching you how to spoil that virgin  space  with 
some  of  my  black thoughts,  you  should  notice  the  following 
examination.  Retype 3 {BLOCK}.  Now, what happened? Did the disk-
drive crisp as  it did before when you had entered 3 {BLOCK} ? No, 
it shouldn't !  Because block (or screen) 3 is already in  memory. 

To  display the whole of the buffer,  you just type 3  {LIST}.  Of 
course  I don't know how your screen looks now,  but it  could  be 
something like 

BLOCK #3      

1
2
3
4
5
6
7
etc. 
16

I  have to assume,  that you already know how to type some  FORTH-
code into a screen,  since I don't know, what FORTH-editor you are 
using. Suppose you managed to type the following:

BLOCK #3

1 ( #3 ----  Demo FORTH-course #7 ---- 1-3 )
2 : FIRSTTRY   ." Hello world, how are you ?" ;
3 etc.
.
.
16

Let's  inspect the first line.  It's placed between two  brackets. 
In  this way you tell FORTH to regard the text following the  left 
bracket  {  (  } as comment.  As a  matter  of  good  conventional 
practice,  the  first line of each screen is used to indicate  the 
contents  of  that  screen.  In the example above  there  is  much 
information about that screen in the first line. #3 tells you that 
it is screen # (number} 3. The next sequence describes the purpose 
of  the code in that screen.  The last two figures separated by  a 
hyphen  point out to you (and others) that #3 is the first  screen 
of a total of 3 on the same subject.  The way in which you have to 
indicate comment may differ from the one used here.

It  is important to allocate the first line for comment,  as  even 
the most genious programmer suffers from memory-drain and will not 
remember exactly what all those code was ment for. Even after some 
days  the drain-virus will cause naughty holes in your  memory  !! 
Most screen-editors supply words to display the first line of  one 
or  more screens e.g.  3 5 {INDEX} will display the first line  of 
screen 3 up to screen 5. Like this:

#3 ( #3 ---- Demo FORTH-course #7 ---- 1-3 ) 
#4 ( #4 ---- Demo FORTH-course #7 ---- 2-3 )
#5 ( #5 ---- Demo FORTH-course #7 ---- 3-3 )

The second line is the first code.  It is a definition of the word 
{FIRSTTRY},  which is the first trick you'll have to study in  any 
computerlanguage. All code is placed at the same line. That is not 
a must. I could have spread the code at line 2 and at line 3. Like

BLOCK #3

1 ( #3 ----  Demo FORTH-course #7 ---- 1-3 )
2 : FIRSTTRY 
3           ." Hello world, how are you ?" ;
4 etc.
.
.
16

ALWAYS IN YOUR MIND

Be  aware  of the fact,  that typing words into a  screen  follows 
the same rules as doing it directly from the keyboard.
That  includes  typing-errors.   Typing-errors of all kind   don't 
conflict  with  saving-procedures or with loading a block  into  a 
buffer  for editing-purposes.  But when you try to compile such  a 
screen,  FORTH will protest firmly or the action of your  compiled 
words  will  not  be correct.  Debugging a screen  (or  a  set  of 
screens)  involves  editing  and  recompiling  to  check  if  your 
debugging was successful. If your code is very complicated or your 
typing very bad,  it's easy to understand,  that many versions  of 
the  same  words  will be entered in  your  dictionary  during  an 
editing-session.  To  avoid such an accumulation of  failures  the 
following trick will do.
The second line in the demo-screen should look like:
2 FORGET DUMMY   : DUMMY ;
You'll have to watch three always-rules,  which go by this trick.
1. Always use the same word i.c. {DUMMY} 
2. Always type : DUMMY ; before you start an editing-session, i.e. 
before you restart your editing work. Otherwise FORTH will ask you 
what on earth that DUMMY is, he has to {FORGET} !!
3.  Always remove that second line,  when your code is able to run 
errorfree.
To describe the effect of this DUMMY-stuff when editing,  imagine, 
that I had elaborate the following cry: 

BLOCK #3

1 ( #3 ----  Demo FORTH-course #7 ---- 1-3 )
2 FORGET DUMMY  : DUMMY ;
3 : FIRSTTRY   ."Hello world, how are you ?" ;
4 etc.
.
.
16

To give this code the first try-out,  you have to enter :  DUMMY ; 
and then the command 3 {LOAD},  which will {FORGET} the DUMMY-word 
you  typed  directly from the keyboard,  then will compile  a  new 
DUMMY and the FIRSTTRY. You also will get a error-message. Because 
you didn't insert a space between {."} and Hello. 
Call your editor,  display screen #3 on your monitor and edit  the 
screen. Now the screen looks like:

BLOCK #3

1 ( #3 ----  Demo FORTH-course #7 ---- 1-3 )
2 FORGET DUMMY  : DUMMY ;
3 : FIRSTTRY   ." Hello world, how are you ?" ;
4 etc.
.
.
16

Jolly good,  eh ?  Now,  if we recompile this updated screen, this 
will  happen.  The  old  DUMMY-word is  forgotten  and  all  words 
compiled after DUMMY, i.c. FIRSTTRY. A new DUMMY is compiled and a 
new FIRSTTRY. The effect of compiling screen #3 two times is, that 
the words are nevertheless placed in the dictionary only once.  So 
if  the space was the only mistake you made,  you now  can  remove 
line 2. The screen looks like

1 ( #3 ----  Demo FORTH-course #7 ---- 1-3 )

3 : FIRSTTRY   ." Hello world, how are you ?" ;
4 etc.
.
.
16

Another  trick,  which will save you much annoyance,  is to put  a 
list  of  the words to use and  some comment at the  end  of  your 
application.  This  list  has  to  be displayed  as  soon  as  the 
application is compiled. The user of your application - mostly you 
- is not forced to start a long quest through the code to find out 
what to do with all that beautiful words.
Type  ST-NEWS  to  read the magazine   is  somehow  somewhat  more 
elucidating the a simple ok after all the code has been  compiled, 
won't  you  think ?  Giving you this advice   means,  that  it  is 
possible  to incorporate code in a screen that is not meant to  be 
compiled,  but to be carried out directly. Such a list consists of 
'printstatements'. An example:

BLOCK #3

1 ( #3 ----  Demo FORTH-course #7 ---- 1-3 )
2
3 : FIRSTTRY   ." Hello world, how are you ?" ;
4 etc.
.
.
15 ." Type FIRSTTRY to start the application "
16

Now it is up to you to start this 'program'.  If you intend to get 
an  application start immediately after the compilation has  taken 
place, you should follow the next example.

BLOCK #3

1 ( #3 ----  Demo FORTH-course #7 ---- 1-3 )
2
3 : FIRSTTRY   ." Hello world, how are you ?" ;
4 etc.
.
.
15 FIRSTTRY
16

As  you can see,  the word {FIRSTTRY} (line 3) is  first  compiled 
and  then  (line  15) executed.  It is as if we  entered  the code 
directly  from  our  terminal !  In a screen you  can  define  new 
words to be compiled, enter words to be executed directly and type
some comment. 
In most cases,  an application's codetext  expands over more  than 
one screen,  as we suggested earlier in relation to the example we 
are using.  I didn't use line 16 on purpose till now, because that 
line would have contained some code to automise the compilation of   
the following two screens, #4 and #5. The compilation of more than 
one screen can be automised in several ways. 
One way is the use of the word {-->} as the last word in a screen.

BLOCK #3

1 ( #3 ----  Demo FORTH-course #7 ---- 1-3 )
2
3 : FIRSTTRY   ." Hello world, how are you ?" ;
4 etc.
.
.
15 
16 -->

It is meant to say:  proceed compilation of the next block, #4. If 
that  block is not in memory,  it will be read from disk.  If  the 
word {-->} is also the last word of screen #4, then screen #5 will 
be read from disk and compiled.  So screen #3,  #4 and #5 are tied 
together at compilationtime by means of {-->}.  Another way is  to 
make a special screen: a loader.

BLOCK #2

1 ( #2 ----  Demo FORTH-course #7 ---- loader ---- 1-4 )
2
3 3 LOAD 4 LOAD 5 LOAD
4 etc.
.
.
15
16

The word {LOAD} loads a block into a buffer and has it compiled in 
one  go.  {BLOCK}  only loads a block (or screen) into  a  buffer. 
{LIST}  loads a block into a buffer and displays the  contents  at 
your screen.  All three words expects a number on the  stack,  the 
number  of  the block you want to compile - {LOAD} -,  to  edit  - 
{LIST} - or simply bear in the computer's mind, {BLOCK}.
The  word {-->} may be used everywhere in a screen.  It can  serve 
several goals.  As soon as FORTH sees this word, he will jump over 
all next code on the same screen and start the compilation of  the 
code on the next screen. 

BLOCK #3

1 ( #3 ----  Demo FORTH-course #7 ---- 1-3 )
2 -->
3 : FIRSTTRY   ." Hello world, how are you ?" ;
4 etc.
.
.
15 FIRSTTRY
16 -->

In  the  screen above FORTH simply skips all code following  {-->} 
at  the second line,  including line 15 and 16,  but he  continues 
compilation as long as there is any code to compile.  If you  want 
for some reason to terminate compilation,  you should use  {EXIT}. 

BLOCK #3

1 ( #3 ----  Demo FORTH-course #7 ---- 1-3 )

3 : FIRSTTRY   ." Hello world, how are you ?" ;
4 EXIT
.
.
15 FIRSTTRY
16 -->

The   {EXIT}-word  at  line  4  will  cause  the  termination   of 
compilation   immediately.   You  can use {EXIT} within  a  colon-
definition,  but not in a {DO..LOOP}.  When such a definition  has 
been  compiled and then executed,  {EXIT} will stop  execution  at 
that point.

SOME LATIN TO SHOW OFF

Above  we  wrote,  that the Standard requires at least  two  input 
screenbuffers.  In  the example we used three  screens,  which  we 
successively loaded into memory: three screens into two buffers !!
Well,  FORTH  has a very intelligent way to deal with screens  and 
buffers.  Let's carry on with the example. If screen #3 is  loaded 
into a screenbuffer, it will mark this buffer as used, as will  do
the  screen #4 to the other buffer when it's loaded.  Every time a 
block  is  loaded  into a buffer,  the  (user)-variable  {SCR}  is 
changed  to contain the number of the most recently listed  source 
text screen.  Don't worry about uservariables, they are not really 
the user's business. So far, so good.  But what about #5 ? The  #5
is  loaded into the buffer which is the  least  recently used one.
(Here screen #3).If that buffer is occupied by an unchanged block,
then the contents of that buffer are simply overwritten. Has there 
been  made any change in that screen,  then it is written back  to 
disk,  overwriting  the  original screen on  that  medium.  It  is 
possible  to  force a screen as updated.  The word  {UPDATE}  will 
mark  the last screen  read  into a buffer as having been changed.  

If  you  have changed  the  two screens currently in the  buffers, 
loading  two other screens will write the first two  screens  back 
to  disk.  You  can extend this procedure in case you  use  three, 
four, five or even more buffers. (Five buffers is nice).
You can also force a buffer to be cleared to contain another block
with {BUFFER}.  4 {BUFFER} will obtain the next buffer and  assign 
it to screen #4. If the previous contents of the buffer are marked 
as UPDATEd,  they are written to disk.  #4 is not loaded into  the 
buffer, although it has been cleared. See, nothing can go wrong !!
But...if  you have several updated screens in memory and you  turn 
off your computer,  none of them will be written back to disk  and 
all your work is lost.  Before turning off your love-bird, use the 
word  {SAVE_BUFFERS} or {FLUSH}.  Both will write the contents  of 
buffers that have been changed to disk,  with the distinction that 
{FLUSH} will mark all the buffers as empty. If you should feel any 
need  of clearing the buffers,  let nothing stop you and  use  the 
word  {EMPTY-BUFFERS},  which  will  do the  job  without  writing 
anything back to your mass-storage device.

As I pointed out before not all FORTH-systems use screens to  save 
code to a mass-storage device.  Some of them use a quite different 
filing-system. That kind of editor can be used to develop files of 
non-fixed  length.    Those  files  are  given  a  name,  such  as 
CIRCLE.FTH  or  SOLVER.FTH.  Apart from them having a name  and  a 
non-fixed  length,  they mostly are manipulated by means  of  key-
combinations.  But all tips given above will equally fit for  this 
kind of files, mutatis mutandi. 
                                       Ave !

SUMMARY

To  cope with the subject of the Editor wasn't easy  at  all.  The 
most  annoying handicap was the diversity of kinds of editors  and 
the  way  they  work.  So I've  emphasized  the  diskhandling  and 
discussed some tricks to use when developing an application.

|¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|
|   WORD      |    STACKNOTATION       |       DESCRIPTION       |
|¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|
|  BLOCK      | ( n  ---- addr )       |Leaves the address of the|           |                         |                         |
|             |                        |first byte of data in    |
|             |                        |block (screen) n. If not |
|             |                        |in memory, the block is  |
|             |                        |transferred from disk to |
|             |                        |the least-recently used  |
|             |                        |buffer. Updated data in  |
|             |                        |that buffer is written to|
|             |                        |disk.                    |
|             |                        |                         |
|  BUFFER     | ( n ---- addr )        |Identical to BLOCK,      |
|             |                        |except for the screen not| 
|             |                        |being transferred from   |
|             |                        |disk to buffer.          |
|   LIST      | ( n ----      )        |Lists screen n. If not in|
|             |                        |buffer, it is read from  |
|             |                        |disk.                    |
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯

|¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|
|   WORD      |    STACKNOTATION       |       DESCRIPTION       |
|¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|
|   LOAD      | ( n ----      )        |Starts compilation of    |
|             |                        |screen n. If not in      |
|             |                        |buffer, it's read from   |
|             |                        |disk.                    |
|SAVE-BUFFERS | (    ----     )        |Transfers all UPDATEd    |
|             |                        |screens  to  disk.   The |
|             |                        |contents of the buffers  |
|             |                        |are not changed.         |
|EMPTY-BUFFERS| (    ----     )        |Fills the whole buffer-  |
|             |                        |area with zeros. No data |
|             |                        |is written to disk.      |
|   SCR       | (    ---- addr )       |A uservariable contain-  |
|             |                        |ing the number of the    |
|             |                        |most recently listed text|
|             |                        |source screen.           |
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯

|¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|
|   WORD      |    STACKNOTATION       |       DESCRIPTION       |
|¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|
|  UPDATE     | (   ----      )        |Marks the current editing|
|             |                        |screen  as  having  been |
|             |                        |changed. Any UPDATEd     |
|             |                        |screen is saved onto disk|
|             |                        |before its buffer is re- |
|             |                        |used                     |
|   EXIT      | (   ----      )        |Compiled within a colon- |
|             |                        |definition it terminates |
|             |                        |execution of that defini-|
|             |                        |tion at that point. It   |
|             |                        |also terminates compila- |
|             |                        |tion of sourcetextblocks |
|   -->       | (  ----       )        |Continues compilation of |
|             |                        |the next sourcetextblock.|
 ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯

EXTRAS

In  the  previous part of this never ending story,  I  threw  your 
attention  on a bulletin board which was dedicated to  FORTH.  The 
proud owner or rather sysop, wrote me he had developed an built-in 
editor  to go with the in PD-libraries the  never  mentioned,  but 
very  powerful UNIX-FORTH.  Send a formatted disk  with  addressed 
envelope and backpostage to J.M.B. v.d. Ven, Spaarnwouderstraat 71 
zw.  2011  AC  HAARLEM THE NETHERLANDS and you will  recieve  this 
masterpiece  of FORTH-programming.  Better still,  fill YOUR  disk 
withwith some code of your own, it doesn't matter what, as long as
it is FORTH.

SOLUTIONS TO PART SIX

1. The answer to this question of course is very simple: {SP0} {@} 
   returns the address of the bottom of the stack and that address 
   is the  same as the startaddress of the Terminal Input Buffer.
   The word {TIB} equally returns that startaddress.                   

2. The alternated {SIZE}-word should look like this:
     VARIABLE TOMAT 
   : SIZE  TOMAT !  
           TOMAT @
           CASE
           DUP 25  <  OF ." Rejected    "   ENDOF
           DUP 50  <  OF ." Extra small "   ENDOF
           DUP 125 <  OF ." Small       "   ENDOF
           DUP 200 <  OF ." Medium      "   ENDOF
           DUP 250 <  OF ." Large       "   ENDOF
           DUP 249 >  OF ." Extra large "   ENDOF
           ENDCASE
           DROP
   ;

3. : <CMOVE32 
             2- 0 SWAP 
                     DO   OVER I + 2@
                          OVER I + 2!
               -2   +LOOP
   ;

4. : <MOVE>  -ROT 2DUP U<
                         IF 
                             ROT <CMOVE
                         ELSE
                             ROT CMOVE
                         THEN
   ;
5. Because I spoke a powerful protective spell ! No, it's because 
   using {EXPECT}  gives  more room for  user defined  words  and
   charactermoving.
6. 

EXERCISES

No  exercises  this time.  There isn't much to  exercise  with  an 
editor,  except  using  it for editing purposes.  So next  time  I 
haven't to give the answers,  leaving more file-space to add  very 
complicated and difficult problems.

Disclaimer
The text of the articles is identical to the originals like they appeared in old ST NEWS issues. Please take into consideration that the author(s) was (were) a lot younger and less responsible back then. So bad jokes, bad English, youthful arrogance, insults, bravura, over-crediting and tastelessness should be taken with at least a grain of salt. Any contact and/or payment information, as well as deadlines/release dates of any kind should be regarded as outdated. Due to the fact that these pages are not actually contained in an Atari executable here, references to scroll texts, featured demo screens and hidden articles may also be irrelevant.