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