Bishop Owen - Exploring Forth
Bishop Owen - Exploring Forth
I ft
lift
Exploring FORTH
Exploring FORTH
Owen Bishop
in collaboration with
Audrey Bishop
GRANADA
London Toronto Sydney Newark
Granada Technical Books
Granada Publishing Ltd
8 Grafton Street, London W1X 3LA
This is a book for those who are starting to learn FORTH. It does
not expect any previous knowledge of the language. The
explanations are detailed so that you can follow exactly what is
going on at every stage.
The book will not teach you all there is to know about FORTH, as
there is not enough room in a book of this size to deal with every
aspect of this fascinating language. But it is hoped that, by the time
you have finished this book, you will feel confident to continue the
exploration of FORTH on your own, or with the help of one of the
more advanced books.
This is not a book for armchair reading. You will get far more out
of it and progress more rapidly if you have your computer switched
on, with FORTH in operation, as you study the book. Key in all the
short examples that are given throughout the book and watch things
happen on the screen. Try varying the examples; type in something
slightly different. See if the effect is what you expect it to be. If it is
not, try to work out why. This is the practical way of exploring
FORTH.
Chapter One
Which FORTH!
(a)
Fig. 7.7. The course of action when running (a) a program in BASIC; (b) an
application in FORTH.
Which FORTH? 3
(b)
performing its own actions. There is no ‘listing’ that you can read
from start to finish, to see what the program does. To follow the
action you must thread your way from word to word. But, since the
definition of each word is short and clearly related to the definitions
of other words, this is an easy matter. When working with FORTH,
we use or apply the existing words to create new words. For this
reason, it is best to speak of an application rather than a ‘program’.
4 Exploring FORTH
To summarise
>10 FOR J - 1. TO 30 0 00
>20 NEXT
notice that the number for the end of the loop comes before the
number for the beginning. This ‘back-to-front’ habit of FORTH is
something we shall see a lot more of. Why it works this way is
explained later. It may seem strange at first, but you soon get used to
it, just as one soon gets used to driving a car on the opposite side of
the road when visiting a foreign country. Then it feels odd when you
come back home!
The loop which does the counting begins with the word DO and
ends with the word LOOP. In this definition there is nothing
between DO and LOOP, just as there was nothing in the BASIC
loop given earlier. All the micro is being asked to do is to loop back
to DO thirty thousand times.
Now to execute the word TEST. Key in the word TEST by itself
on the line below. Get your stop-watch ready, then press RETURN.
The ‘OK’ message appears as soon as the computer has counted to
30000. On the BBC Microcomputer, the test took only two seconds.
For this particular operation, FORTH is about eight times faster
than BASIC. Comparisons for other operations give different
results, depending on what has to be done by the micro, but it seems
that the claim that FORTH is up to ten times faster than BASIC is to
be believed.
The speed of FORTH makes it ideal for computer games. It also
has the advantage that the computer can perform a long series of
calculations in a reasonably short time. There are applications in
this book which take advantage of the speed of FORTH in both of
these ways.
High-level languages are of two main types: interpreted and
compiled. BASIC is an interpreted language. When a program in
BASIC is run, the computer goes through it, line by line, working
out what the various statements mean. The interpreter program in
the ROM of the micro interprets each BASIC statement, calling on
machine code routines to perform the necessary actions. The
program is interpreted every time it is run. Moreover, if there are
lines in a loop which are repeated, say 100 times, then those lines
have to be interpreted 100 times each. Interpreting inevitably
requires time, which means that BASIC programs run relatively
slowly. But interpreters have an advantage. When you are working
with an interpreted language, it is easy to stop the program, make
small changes in it and then run it again. Programming is relatively
quick and easy, and you can instantly see the results of any changes
you make.
A compiled language, such as Pascal, has entirely different
8 Exploring FORTH
To summarise
• FORTH is fast.
• FORTH combines the advantages of an interpreted language
with those of a compiled language.
Chapter Three
Stacking It Up
When we look at the stack, we see only the top card. We will refer
to this as top-of-stack. This particular card is only top-of-stack for
as long as the stack remains unaltered. We could add another card to
the stack, placing it on top of the stack. Now the original top-of-
stack card is covered and the newly added card becomes the top-of-
stack. Or we could remove the top-of-stack card and throw it away.
Then the newly exposed card which was below it becomes the new
top-of-stack.
Note that when we add a card to the stack we always place it on
12 Exploring FORTH
top. We never try to insert it further down in the stack. Also, when
we remove a card from the stack, we always remove the top card,
never a card from further down the stack. These two rules are an
essential part of the way the stack works.
Suppose we begin with an empty clip-board. This can be referred
to as an ‘empty stack’. It would help at this stage if you were to have
an empty clip-board beside the computer. You also need about ten
cards (or sheets of paper), and a pencil. If you do not have a clip¬
board it does not matter; just keep the windows and doors shut so
there is no wind to blow the stack away! When you are ready with
this equipment, turn on the computer and call up FORTH.
First write the figure 4 on one of the cards (we will refer to them as
cards, even if you are really using scraps of paper). Place it in the
clip-board. This card is top-of-stack. The value stored at top-of-
stack is 4 (Fig. 3.2).
When you first run FORTH, its stack is empty, just like the clip¬
board was. To place 4 at top-of-stack, all you have to do is type:
and press RETURN. You will see the ‘OK’ prompt on the next
screen line, indicating that your instructions have been obeyed and
the computer is waiting to be told what to do next.
How do we know that 4 has really been placed at top-of-stack?
FORTH has a word which tells the computer to take the top number
off the stack and display it. This word is the shortest word possible:
No, it’s not a dirty mark on the page, it is a full-stop. When we refer
to it. we call it ‘dot’. Key in ‘dot’, then press RETURN. The sequence
so far is shown in Fig. 3.3. As you can see, the computer has
displayed 4. the number stored at the top-of-stack.
Stacking It Up 13
4
OK
‘1 OK
Fig. 3.3.
Can you get the computer to display it again? Try ‘dot’ followed
by RETURN. What happens next depends on the version of
FORTH you are using. On the BBC Microcomputer, for example,
you get:
0 . ? MSG * 1
The words MSG# 1 refer you to error message no. 1. If you look this
up in the manual, you will find that it means ‘Stack empty1.
On the Jupiter Ace you will get:
-18572ERROR 2
The first number may be different, but the error message will always
be number 2, which means ‘Stack empty1.
It seems that the 4 is no longer on the stack. It is the same as if you
had taken the card from the clip-board, and pinned it up on the wall
for everyone to see (Fig. 3.4). Do this now - take the card from the
board and put it where everyone can see it (no need to pin it to the
wall!).
the stack. There is no slipping the 666 card under the 55 card! We can
do the same thing with the computer’s stack (see Fig. 3.6). Press
4
OK
55
OK
666
OK
Fig. 3.6.
55 OK
♦
4 OK
Fig. 3. 7.
displayed in the reverse order to that in which they were put on the
stack. That this should happen is obvious if you take the cards from
the top of your clip-board stack. First comes the 666. Removing this
Fig. 3.8 The three values displayed, leaving the stack empty.
Stacking It Up 15
4 55 666 « * t
Recall the earlier remarks about spaces; leave one space between
each number and each ‘dot’. Then press RETURN. The result is:
666 55 4 OK
If all that FORTH could do is stack numbers and then unstack them
in reverse order, it would be a useless language. Let us give it more to
do with the numbers. But first, try this with your clip-board stack.
Begin with an empty stack. Place the 4 card on the stack, then the 55
card. Now comes the adding. Take both cards off the stack ready for
adding. Place them and a blank card as shown in Fig. 3.9. Add 4 and
55 to get the answer 59 and write this on the blank card. Put the 59
card on the stack. Having done the addition, and having placed the
answer on the stack, you have no further interest in the 4 and 55
cards. So throw them away. This is just what the micro does.
The same operation on the computer is done in three steps:
WAS SECOND-ON-STACK
WAS TOP-OF-STACK
Fig. 3.9. Adding, with two values taken from the stack.
computer will not show you what the answer is unless you tell it to.
Use ‘dot’ to find out the answer:
♦
59 OK
This leaves the stack empty - or should do. Use ‘dot’ again to find
out if the 4 or the 55 are still lurking on the stack. They are not there.
They have been used as requested and are now forgotten.
There is one thing you may not have noticed about what you just
did. It all happened so naturally that it did not seem strange. To
make the micro add 4 and 55, you gave it the two numbers first and
then told it to add them. This makes sense. Until the two numbers
are on the stack, how can the computer be expected to deal with
them?
Yet some people who are beginning FORTH find this a strange
way of going about things. This is because we usually write down an
addition as:
4 + 55 = 59
The + comes between the two numbers which are to be added. But
this is only a convenient way of writing down the numbers as an
equation. You do the actual addition in your head. When you were
first learning to add, you probably wrote it down (or had it written
Stacking It Up 17
This is just the way it is done in FORTH, using the stack. Now try
this. Empty your clip-board stack. Put the 666 card on the stack, then
put the 59 card on top of it. 59 is top-of-stack and 666 is second-on-
stack. Now we are ready for a subtraction. Take the 59 off the stack,
then take off the 666, placing it on the table above the 59 card. Put a
blank card below the 59, as in Fig. 3.10. Do the subtraction, writing
WAS SECOND-ON-STACK
666
WAS TOP-OF-STACK
r
i/
Fig. 3.10. Subtracting, with two values taken from the stack.
the answer (607) on the blank card. Place this card on the stack. The
59 and 666 cards are not needed any more, so throw them in the
waste-paper basket. The subtraction is done; now to let other people
see the answer. Take the 607 card from the stack and place it on a
shelf.
Here is the same thing done in FORTH:
666 59 - .
607 OK
This does three things:
We have included a fourth item, the ‘dot’, in the line, so we get the
answer straight away, and nothing is left on the stack. As with the
addition, we give the computer the two numbers first, and then tell it
what to do with them. As before, we cannot expect it to do anything
until it has the two numbers to work on. Numbers first, action
second.
There is one way in which addition is different from subtraction.
If we tell the computer to add, it does not matter in which order the
two numbers have been placed on the stack. If we tell it to subtract, it
always takes the top-of-stack from second-on-stack. To check on
this, try:
59 666 - .
-607 OK
Taking a larger number from a smaller one gives the expected
negative result.
In other words, numbers taken from the stack are always taken from
the top; new numbers put on the stack are always put on the top.
The second rule is about giving the micro the figure or figures to
work on, and then telling it what to do with them. We can call this
rule:
Readers who are soccer fans may prefer to call this the FA rule.
The TP rule explains why the DO...LOOP line given at the
beginning of Chapter Two seemed to have the figures the wrong way
round. Now it is clear that they are the right way round. The loop is
to run from 1 to 30000. When the computer wants to know how
many LOOPs it is to DO, it will go to the stack and look at top-of-
stack. Here it will find 1, for this was the figure placed there last. It is
Stacking It Up 19
the first to come off (LIFO is a result of the TP rule). Having found
out the value which is to begin the loop, it wants to know the value
which is to finish it. The micro looks at the stack again. It has already
taken away the 1, so now the top-of-stack is 30000. It removes this,
leaving the stack empty, and performs the loops. This line also
illustrates the FFAA rule. The figures for the end and the beginning
of the loop are put on the stack, then the micro is told to DO a
LOOP. More about DO...LOOP in Chapter Nine.
C! ?TAB (+LOOP) M*
or symbols alone:
; + ♦(>#/ —>
(The word can begin with numerals if you wish, which is something
not allowed in BASIC.) Spaces are not allowed in words, which is
obvious, since FORTH relies on spaces to separate one word from
the word next to it. If you want a ‘two-word’ word, use a hyphen as in
the example MAKE-TWO at the end of Chapter Two.
Although it was stated above that only an ‘all-numeral’ group of
characters is read as a number, there is the exception that a group of
numerals with a minus sign in front is taken as a negative number. In
some FORTHs you are allowed to key in numbers with decimal
points - what are called floating point numbers. The FORTH—79
20 Exploring FORTH
standard does not provide for such numbers. All its calculations are
done in whole numbers, or integers. Using floating-point numbers
makes calculations slower and there is not usually any real need to
work with such numbers. We shall consider this topic again in
Chapter Eleven.
The words we have used in Chapters Two and Three are:
* + •- DO LOOP : t
The action of ‘dot’, ‘plus’, ‘minus’, DO and LOOP have already been
explained, but we have not mentioned ‘colon’ and ‘semicolon’. The
word ‘colon’ tells the computer that you are defining a new FORTH
word. We used it at the beginning of the line in which we defined the
words TEST and MAKE-TWO (Chapter Two). The word
‘semicolon’ is used to tell the computer when the definition of the
word is complete.
(32 ...)
This example shows that ‘dot’ begins with a number (e.g. 32) on the
stack and finishes with nothing.
Stacking It Up 21
Quite often we want to show what a word does without using any
numbers in particular. Then we use ‘n’, or ‘nl\ ‘n2’, ‘n3’, etc., if there
is more than one number. The action of the word ‘dot’ can be shown
by:
(n ...)
4 *S 55 ♦S 666 *S + ,S - .S . *S
4
4 55
4 55 666
4 721
-717 -717
EMPTY OK
Fig. 3.11.
To summarise
Explore more
(1) Key in lines to make the computer do the following additions and
subtractions and display the result:
^ , 23 + 56 146 + 72 146 - 72
4 + 43 + 5 1 + 0 + 67 1000 + 100 + 10
45 + 21—6 55 - 3 - 2 80 - 2 + 22
(2) Define a word ADD to take three numbers from the stack, add
them together and display the result.
4
I
Chapter Four
What Is The Stack?
4 BINARY
1.0 0 OK
5 BINARY
101 OK
32 BINARY
100000 OK
345 BINARY
£ 010110 01 OK
Fig. 4.1.
® © ® © ®
STACK
CO
POINTER
POINTS TO
THIS ADDRESS
1 2
WHEN THE
STACK IS
EMPTY
82
Fig. 4.2. The stack, how numbers are stored in it and how the stack pointer
points to the top-of-stack.
What Is The Stack? 25
Figure 4.2 shows that the top-of-stack has the lowest address. This
may seem an upside-down approach, but some people may find this
quite natural. However, it is one which is convenient for the
operating system. As we add numbers to the stack, or take numbers
from it, the computer alters a variable called the stack pointer. This
variable is stored at another part of memory and ‘points at’ the top-
of-stack. In other words, the value of the stack pointer is the address
of the memory location which is currently the top-of-stack.
There is a word which lets us find out the value of the stack
pointer. This word is called ‘S-P-fetch’ (see Fig. 4.3). In Fig. 4.3 we
SPB ♦ Jr . -)
88 OK
'T> "
1 2 3 SPB . <
82 OK
€ 2- ■
4 5 SPB .
78 OK
♦ . ♦ . SPB ♦
5 4 3 2 8.6 OK
Fig. 4.3. t
began with an empty stack. The stack pointer equalled 88. The word
‘S-P-fetch’ puts the value of the stack pointer at top-of-stack. The 1 11
value it puts is the value of the stack pointer before ‘S-P-fetch’ acted.
If we follow ‘S-P-fetch’ with ‘dot’ to display this value, this removes
the value from top-of-stack, leaving the stack as it was before.
The next step in Fig. 4.3 was to add three more numbers to the
stack, then find the value of the stack pointer. This becomes 82. We
I K4tl
put three numbers on the stack, but the pointer moved six locations
further down in memory. It seems that it takes two locations (two ■f
bytes) to store each number. This is the stage illustrated in Fig. 4.2.
To check on this we put two more numbers (4 and 5) on the stack.
The stack pointer changed to 78, showing that four more bytes were
used. Finally, we removedfournumbersfromthestackbyusing‘dot’.
These numbers were displayed and, after them, came the latest value
*
of the stack pointer, 86. Removing four numbers from the stack t/ t> »>
iSi/O-
26 Exploring FORTH
Table 4.1
0000000000000000
0000000000000001
0000000000000010
0000000000000011
0000000000000100
0111111111111100 32764
0111111111111101 32765
0111111111111110 32766
32767
0111111111111111
1000000000000000 -32768
1000000000000001 -32767
1000000000000010 -32766
1000000000000011 -32765
1111111111111100
1111111111111101
1111111111111110
1111111111111111
0000000000000000
It is easy to understand the top half of this table. The 16th digit (the
one on the extreme left) is 0, so all the numbers are positive. They
run from 0 up to the largest possible with 15 bits, 32767. Adding 1 to
What Is The Stack? 27
1111111111001000
1000000000000000
This confirms what we have seen in the table above.
It is usually more convenient to have the computer dealing with
signed numbers, so that we have negative numbers when we want
them. But sometimes it is better for the computer to treat the
numbers as unsigned, with values ranging between 0 and 65535, and
with no negative numbers allowed. If this is what is required, there
are FORTH words to make the computer treat the numbers as
unsigned.
28 Exploring FORTH
To summarise
• Use the stack pointer to investigate the way the stack is used.
• Write a decimal number as a signed binary number.
Explore more
0+0 = 0 0-0 = 0
0+1=1 0-1=1, borrow 1
1 + 0=1 1-0=1
1 + 1 = 0, carry 1 1-1 = 0
Here are the decimal numbers to work with:
4+5 4-5
255 + 1 128-1
256 + 256 10 + 21
10-2 - 32768+1
When you have worked out the answers in binary on paper, use the
computer to check your results.
Chapter Five
Numbers In Store
50 RATE / .
4 OK
30 Exploring FORTH
This line introduces a FORTH word called ‘divide’. This uses the
same symbol as is used in BASIC. The action of the line above is to
put the value 50 on the stack, followed by the value of RATE (12).
Then ‘divide’ divides second-on-stack (50) by top-of-stack (12),
giving the answer, 4, which is displayed by ‘dot’. You may object that
50/12 gives 4.16666667. This is true, but remember that FORTH
deals only in integers. It ignores the figures after the decimal point.
You may think it most inconvenient of FORTH to drop the pence,
but it is no more inconvenient than BASIC, which insists on giving
you all the decimal places, as in the 4.16666667 quoted above. With
either language we need a little more programming to get exactly
what we want.
If you want to use FORTH in a currency conversion application,
you will need to work out ways of making it handle the pence as well
as the pounds. One way to do this is explained later in the chapter.
For the moment, we will ignore the odd pence and look further at the
uses of CONSTANT. Let us suppose that the travel guide told you
that a hotel room costs 120 francs a night. This too is a constant, so
we can define:
7 ROOM * RATE / ♦
7 0 OK
Let us look at the stages of this calculation. First we put 7 on the
stack, followed by ROOM (120). The next word is ‘times’ (*). As
might be expected, the action of this word is to take the top two
numbers from the stack, multiply them together, and place their
product (the result) on the stack. At this stage, top-of-stack is 840.
Then we put RATE (12) on top-of-stack. The next word, ‘divide’,
takes the top two numbers off the stack, divides them (840/12) and
leaves the answer (70) on the top-of-stack. This is displayed by ‘dot’.
If you are not clear about what happened, Fig. 5.1 illustrates the
same sequence, but with ‘dot-S’ included after each word, to make
the computer print out the contents of the stack at every stage.
Whenever you have difficulty in following how a line of FORTH
works, make use of ‘dot-S’ as in Fig. 5.1. We shall not use
it again for this purpose in this book, but will assume that you will
Numbers in Store 31
7 * S ROOM ,8 * .8 RATE .8 /
/
7 120
8 TO
8 TO 12
70 70 OK
Fig. 5.1.
FAHR 9 5 */ 32 +
We put the Celsius temperature on the stack, then use FAHR. It
multiplies the temperature by 9, divides it by 5 and adds 32. This
gives the temperature in Fahrenheit:
, 21 FAHR
69 OK
32 Exploring FORTH
Now to return to our currency conversions. It is all very well for the
computer to ignore the pence and tell us only about the pounds, but
pence are important. If the average cost of a meal is 57 francs the cost
in pounds is £4.75. But the computer works out the cost as £4. Every
meal costs 75p more than the computer tells us, and we might have
no money left for meals during the last few days of the holiday!
There is a word in the line below that helps us look after the pence:
57 RATE /MOD
4 9 OK
The action of ‘divide-mod’ is the same as that of ‘divide’ in that it
performs a division but, before it puts the result (the dividend) on the
stack, it puts the remainder there. The example above shows that 57
divided by 12 (=RATE) gives 4, remainder 9. The 9 indicates that
there are 9 francs remaining, which must now be converted into
pence. The way to do this is to multiply the number of francs
remaining by 100 and divide by RATE. This is another use for
‘times-divide’. The example above shows that the number of pounds
was on top-of-stack. We display this so as to get it out of the way
while the pence are calculated.
^ 96
■ (lv
34 Exploring FORTH
X FORMAT ♦* £" 1 ,R .« -» , OR i
You can see that this is similar to the last section of the previous line,
except that we have put T . R’ in place of the first ‘dot’. The 1 goes on
the stack to tell the computer that ‘dot-R’ must make its field only
one character wide. This 1 is removed from the stack when ‘dot-R’
acts. There is one further improvement. The definition ends with the
word ‘CR’ which is short for carriage return. It is the equivalent of
pressing the RETURN key at the end of the line. The effect of this is
to make the computer go on to the next line of the screen before it
prints its customary ‘OK’. This makes it easier to see the result of the
calculation. Now we are ready to combine all our previously defined
words into a single word, EXCHANGE:
57 EXCHANGE
£4-75
OK
By keying in the number of francs and using the single word
EXCHANGE we are able to call upon all the words we have defined
and several that were defined already. .
> RATE
POUNDS calls /MOD
SWAP
Some of the words originally there, such as SWAP, may call other
words, and these in turn may call upon machine code routines to
perform the actions we required. All of this can be put into operation
by the single word EXCHANGE. This example illustrates the
threaded nature of FORTH.
Numbers in Store 35
Variables
VARIABLE RATE
Unless you have switched off the computer or restarted FORTH
since the last session, you will see ‘MSG #4’ telling you that you are
using the same name as you used for the constant. Take no notice of
this.
Note that in this definition, we do not have to give a value to
RATE. To find out what value it already has, try this:
RATE .
15759 OK
This is an odd value to have! What we see here is not the value of
RATE, but the address in memory at which the value of RATE is
stored. This is one way in which VARIABLE differs from
CONSTANT. Whereas CONSTANT puts the value of the constant
on the top-of-stack, VARIABLE puts the address at which the value
is stored.
Numbers in Store 37
rate: @ .
12 RATE !
OK ^
We first place on the stack the value which is to be given to the
variable. Then the word RATE places the address of RATE on the
stack. ‘Store’ takes the address from top-of-stack, and the value
from second-on-stack, and stores the value at the address. Use the
line given earlier to check that the value of RATE has now changed
to 12.
If we want to be able to use a varying exchange rate in our
calculations, it is more convenient to use VARIABLE than
CONSTANT. It is possible to change the value of a constant, but
this is less easy to do. In any case, the point about a constant is that it
does not change!
When POUNDS and PENCE were defined above, RATE was a
constant. If you use POUNDS and PENCE now, they will still use
the old definition of RATE as a constant. To get these words to
make use of your new definition, you will have to redefine them:
57 13 DAILY
£'t~38
OK
57 11 DAILY
£5-18
OK
Fig. 5.3.
In this chapter we have seen how CONST ANT is used for storing
numbers which are to remain unchanged during the running of an
application, while VARIABLE is used for storing numbers that are
to change. In the course of this discussion we have looked at many
other useful words in FORTH. If you are going straight on to the
next chapter, with perhaps only a short break for a cup of tea, leave
the computer switched on.
To summarise
Explore more
2^'O' j xf
Chapter Six
See How They Run
(1) No two makes of micro handle their graphics in exactly the same
way.
(2) No two makes of micro have exactly the same version of
FORTH.
1
1
42 Exploring FORTH
8 ARRAY CHARS
The figure 8 indicates how many numbers are to be stored. CHARS
is the name we are giving to the array. It is short for ‘characters’,
since it will be used for holding details of graphics characters. When
it is defined, the values in CHARS are the values already present on
the block of memory that happened to be used. Try reading one of
the values (see Fig. 6.1). The locations in the array are numbered
3 CHARS (.» .
14 OK
Fig. 6.1.
y
from 0 to 7, so this tells us what is in the fourth location (Fig. 6.2).
Let us see exactly what happened in the line above. We put the
number of the location on the stack. CHARS puts the address of the
first memory location of the array on the stack (just as using
VARIABLE puts on the stack the address at which the single value
is stored).
Now the DOES> part of the definition comes into action. Here is
the state of the stack (top-of-stack on right), stage by stage. We are
using the example above and assume the address of the start of the
memory block is 15504
STORES X POSITION
OF CHARACTER 1
15510 3 6 STEPS ALONG IN RAM
REQUIRED FOR 3 STEPS
STORES Y POSITION ALONG THE LOCATION
OF CHARACTER 1 OF CHARS
15512 4
STORES X POSITION
OF CHARACTER 2
15514 5
STORES Y POSITION
OF CHARACTER 2
15516 6
STORES X POSITION
OF CHARACTER 3
15518 7
STORES Y POSITION
OF CHARACTER 3
Fig. 6.2. The region of RAM which is allotted to the array CHARS. Although
each location in CHARS requires two bytes of RAM, each location holds only
one value.
3 CHARS ♦
15504 OK
Try the above for several locations, you will see that successive
locations begin 2 bytes further along in memory. This is because
44 Exploring FORTH
FORTH needs two bytes to store each number, just as it does when
storing numbers on the stack. Storing a value in an array is done like
this:
23 4 CHARS !
OK
The first value is the amount to be stored, the second is the location
in which it is to be stored. CHARS leaves the address of the location
on top-of-stack, as above, and the value to be stored is second-on-
stack. ‘Store’ then uses these two numbers to store the value in the
array.
After that flight into the higher realms of FORTH, we return to
the subject of graphics displays.
User-defined graphics
Most micros allow the user to define their own graphics characters.
The way this is done depends on the micro itself, though the principle
is generally the same for all. To define a character such as a mouse,
for example, we begin by sketching the design on an 8-by-8 grid (Fig.
6.3). Each row of the grid is coded, by considering the shaded
CODES
48 RO
72 R1
144 R2
130 R3
68 R4
230 R5
253 R6
255 R7
The next few paragraphs may send you off to the manual which
came with your computer, or the manual belonging to your version
of FORTH to find out exactly what to do on your computer. The
two examples below are intended to show you the way to set about
the task. First of all, we must specify exactly what is to be done. The
aim is to define a word, CHARDEF, which defines a graphics
character and which has the following stack action: ,,, „
(R7\R6\R5\R4\R3\R2\R1\R0\N ...)
The row of symbols above means that CHARDEF expects to find
the row codes, in decimal, in bottom-to-top order, with the code
number of the character (N) on top-of-stack. We shall be using
several characters, and these are to be numbered from 0 upward.
The absence of anything after the ‘’ indicates that CHARDEF is
to leave nothing on the stack when it has finished.
Here is a word in Acornsoft FORTH which acts as described
above: r __ _ r
■ J/
t CHARDEF 23>VDU 224 +90 DO >UDU LOOP
n /v r t
8 oo i c f
46 Exploring FORTH
Displaying a character
Once again, most versions of FORTH will have some special way of
printing a character at any given location on the screen. Below we
use the Acornsoft words to define a word PLACEIT. You will need
to adapt any words given in your FORTH to perform the following
stack action:
(N\,X\,Y...)
‘TAB(X,Y);CHR$(N)’
Note that this does not work when the computer is in Mode 7. Mode
4 is a good one for use during this chapter. PLACEIT can be defined
rather more simply on thq Jupiter Ace, as shown in Appendix A.
PLACEIT is to be used for putting a graphics character anywhere
on the screen. See it in action:
V
r 20 15 PLACEIT y
things. You are now ready to go ahead making up other words. But,
from now on, whatever FORTH or micro you have, the applications
in the rest of this chapter should work. The only adjustments you
might need to make are to allow for the number of rows and columns
on your screen.
If we are intending to place a lot of characters on the screen at one
time, we want to know where each one is. Here is a word to do this:
(N\X\Y...)
You will notice two new words in this definition, ‘one-plus’ and
PICK. ‘One-plus’ increments top-of-stack by 1; it has the same
action as the two words T +’. The action of PICK is illustrated in
Fig. 6.4 along with the action of several other words which shift
values around on the stack. In STORPLACE we use ‘4 PICK’ to
find the value which is fourth-on-stack and place it at top-of-stack.
STORPLACE has a rather long definition; this is what happens,
stage-by-stage. As before, we show the stack with top-of-stack to
the right: jrjt 5'
We begin with N X Y
DUP N X Y Y
4 PICK NX Y Y
2 * N X Y Y
1+ N X Y Y
CHARS! N X Y
OVER N X Y X
4 PICK N X Y X N
2* N X Y X 2N
CHARS ! N X Y
The stack is unaltered and is ready for the final word, PLACEIT.
Note that before using CHARS we have to double the value of N,
since there are two values (X and Y) associated with each character
(Fig. 6.2). Before the first CHAR we have to add one to twice N so
II
48 Exploring FORTH
A 1 B | C | D | E |
DROP
, 1
A | B | C | D | '
? DUP
A 1 B | C | D | E | OPERATES
DUP DUPLICATE
ONLY IF
? DUP TOPIS
A | B 1 C | D | E 1 E |
NON-ZERO
OVER A 1 B | C | D | E |
A | B 1 C 1 D | E 1 D 1
SWAP A 1 B i C | D | E |
A | B | C 1 E | D |
ROT ROTATE A | B | C | D | E |
A ! B | D | E | C |
ROLL A 1 B | C | D | E | DIAGRAM
SHOWS
B 1 C | D | E | Aj 5 ROLL
PICK A | B | C | D | E | DIAGRAM
SHOWS
A | B | C | D | E A | 5 PICK
0 8 18 STORPLACE
Looking at the stack operations set out above, some readers may
despair of ever working out word definitions for themselves. Here
are a few tips on how to set about this task.
Writing definitions
FORTH
S 1 TES
N X Y
N X Y Y 2N+1
We do not know how many steps the puzzle is going to take. When
deciding what must be on the bottom line we remembered that
CHARS removes both Y and 2N+1 from the stack, so we must
retain the value of N, ready for storing X. The best course seems to
keep it in its original place. We also need to have a copy of Y on the
stack, ready for use by PLACEIT later on. These points decide what
must be on the stack just before CHARS. Having established the
beginning and end, we try working downward from the beginning
and upward from the end - hoping to meet in the middle!
There is a clear way back from the end:
N X Y
2*1 + N X Y Y N
N X Y Y 2N+1
50 Exploring FORTH
4 PICK N X Y Y N
2:1+ N X Y Y 2N+1
Now the rest is easy. To link the first line to the remainder all we need
is DUP:
N X Y
DUP N X Y Y
4 PICK N X Y Y N
2*1+ N X Y Y 2N+1
The link between beginning and end has been established. A similar
line of reasoning can then be used to prepare the stack for the second
occurrence of CHARS.
Moving graphics
we must supply it with a number which is 224 less than 32, which
gives -192. Later, PLACEIT adds 224 to this, obtaining 32, and so
printing a space. As an exercise in working out stack operations try
setting out on paper what happens during the execution of BLANKIT.
If you are using a version of PLACEIT which adds some other
amount to the character code (or perhaps uses it unchanged)
substitute a suitable value for —192 in BLANKIT.
Try using STORPLACE, to display a character, then use
BLANKIT to remove it:
0 12 6 STORPLACE BLANKIT
Finally, we need a word to find out where the character was before it
was blanked, and then print it a specified number of places away.
The word we use is MO VEIT:
DifX and difY are the amounts by which X and Y are to be changed.
Here is another chance for you to see how SWAP and PICK are
used. The first part of MOVEIT calculates the new positions by
adding difX to X (got from CHARS) and difY to Y (also from
CHARS). Then STORPLACE stores these new values in CHARS
and prints the character in its new position. Now for a preliminary
run: /
./ y f
\ RUN 0 1 IS STORPLACE 39 1
DO 0 BLANKIT 0 1 0 MOVEIT
LOOP f
The values in this word are set to make the mouse run from left to
right across the 15th row of the screen. Whether it’s the 15th up or
the 15th down depends on how the display of your computer
operates. The initial ‘01 15’ places character 0 (the mouse) at column
1, row 15. Youcanalter these numbers to use a different character or
to start it in a different place. The ‘39 1’ are the end and beginning of
the loop to move the mouse across the screen. If you have a different
screen width, or want the mouse to run to a different position, you
can alter the 39. Within the loop, BLANKIT blanks out the mouse
ready for it to be displayed in its new position by MOVEIT. The
52 Exploring FORTH
J RUNSLOW 0 1 15 STORPLACE 39 1
DO 0 BLANKIT 010 MOVEIT 1000 0
DO
LOOP
LOOP 0 BLANKIT
OK
Fig. 6.5.
finishes with BLANKIT, to remove the last image of the mouse from
the screen. There are endless variations on RUNSLOW. Try a few of
them. Start the mouse from different parts of the screen and let it
finish at different parts. Let it run up or down the screen, instead of
across. Or let it take a diagonal path, instead.
You now have all the essential words needed for producing moving
graphics. With these, the screen can be filled with hordes of aliens
from Outer Space, while you fire laser beams across the screen at
them from a spacecraft. The fact that it was necessary to use a delay
to slow the mouse down indicates that FORTH is capable of dealing
with a screenful of objects at the speed required for action games.
This is not the time to start designing such games, for there is more to
find out about how FORTH takes decisions (for example, has the
laser hit the alien?). This is explained in Chapter Eight. In the
meantime, it is worth while saving the essential words on a FORTH
screen, as has been done in Fig. 6.6.
See How They Run 53
SCR * .16 10 H
0 ( GRAPHICS WORDS )
1 J ARRAY CREATE 2 * ALLOT D0ES> SWAP 2 * + J
N n <r liT O K 03 O' a rt w CO T 111
8 ARRAY CHARS
: CHARDEP 23 >VDU 224 + 9 0 DO >VDU LOOP t
J PLACEIT 31 >VDU SWAP >VDU >VDU
224 + >VDU !
t STORPL.ACE DUP 4 PICK 2*1 +
CHARS ! ODER 4 PICK 2 *
CHARS ! PLACEIT }
i BLANKIT -192 SWAP DUP 2 * CHARS 8
SWAP 2*1+ CHARS § PLACEIT i
l MOVEIT 3 PICK 2*1+ CHARS 0 +
SWAP 3 PICK 2 * CHARS 6 + SWAP
STORPLACE i
Fig. 6.6.
More mice?
When you are tired of mouse-running, why not invite all three blind
mice on to the screen? This example shows a way of moving several
characters at once. The application is designed to show the three
blind mice chasing after the farmer’s wife. First of all, define three
graphics characters (0 to 2) as mice. Use the same design (Fig. 6.3)
for them all, or work out a special design for each. Figure 6.7 shows
the farmer’s wife, who is defined as character 3.
15
28
58
121
60
228
134
The wife is to move across the screen from left to right, followed
by the three mice, one behind the other. There must be gaps between
the mice. If a mouse is moved into the place just vacated by the
54 Exploring FORTH
I 3*1 15
J CHASE 20 0 DO SHTR
LOOP t
How about making up some words and new characters to show the
farmer’s wife turning round, carving knife in hand, and chasing the
mice back across the screen?
Some machines spoil this kind of graphics display by printing the
prop^1 k'
See /Vow 77?ey Sen 55
cursor after every character. It is useful to have a word to turn off the
cursor. This word is for the BBC Microcomputer: cM*
t OFF 0 0 0 0 0 0 1 23 8 0 DO >YDU
LOOP t ■"
The easiest way of turning it back on again is to change Mode.
The chase scene is the simplest way of animating objects. It works
reasonably well for mice, as the scale on which they are displayed is
too small for us to expect to see their legs actually moving. The
running wife lacks realism, because we ought to be able to see her
legs move as she runs. She appears to skate across the screen. The
next section shows how to improve the animation.
Leapfrog
Character X Y
0 -3 15
1 -2 14
2 -1 14
3 0 15
The action of the frog is shown in Fig. 6.10. LEAP displays the
sitting frog, after having ‘moved’ it 3 squares to the right from its
‘starting position’ off screen. After a delay, BLANKIT clears the
display of the sitting frog. Immediately, MOVEIT is used twice to
display the leaping frog characters (1 and 2). Then comes a delay.
LEAP then uses BLANKIT twice to clear away the leaping frog, and
uses MOVEIT to display the landing frog. After a delay, this is
blanked out.
The word HOPS sets the frog leaping across the screen:
READY sets up CHARS for the start. Then comes a loop to repeat
LEAP 12 times. After this the sitting frog is displayed once more in
the final landing position. The CR at the end of the word is to make
the computer display the ‘OK’ message on the next line instead of to
the right of the frog.
Happy landings!
To summarise
Explore more
(1) Create an array to hold 100 values. Write a word using DO ...
LOOP to fill the array with the values 1 to 100, in order. Write a
58 Exploring FORTH
(2) Follow up the many suggestions made in the text for devising
animated graphics displays.
KEY .
5'I OK
In the example shown above, the ‘6’ KEY was pressed. The ASCII
code for 6 is 54 and this was placed on the stack. The ASCII codes
for other characters are listed in Appendix B. KEY is one of the most
useful words for making the computer wait for input from the user.
After the key has been pressed, the number at top-of-stack tells the
computer which key it was. The computer can act accordingly, as
will be explained in Chapter Eight.
In some applications it does not matter which key is pressed. We
simply want to make the computer wait until we are ready for it to
continue. The following line demonstrates how to do this:
?KEY
OK
The number put on the stack before ‘query-KEY’ is used tells the
micro how many hundredths of a seconds to wait. In the example
above, the ‘OK’ appears as soon as you press a key. If you do not
press a key, it appears after five seconds.
Another related word available in some versions of FORTH is
INKEY. This makes the computer test the keyboard to find out if a
KEY is being pressed, but it does not wait for a key-press. It has the
same action as ‘query-KEY’ would have if there was zero on top-of-
stack. If you have INKEY in your version of FORTH, but do not
have KEY or ‘query-KEY’, Appendix A shows how to define them
by using INKEY.
String inputs
NAME?
TYPE YOUR NAME♦ GUY FAWKES
EMPTY GUY ?
First of all, the message is displayed. The computer then waits until
something is typed in. In this example the name ‘GUY FAWKES’
was typed, followed by RETURN. The response in the third row
shows the stack to be empty. It also shows that the micro started to
look at what had been put into its input buffer and did not
understand it! It searched its list of words and could not find ‘GUY’
among them.
Obviously, QUERY leaves nothing on the stack. The ‘GUY
FAWKES’ string seems to have disappeared into some inner region
of the computer. We need a word which will take the string from the
input buffer, and store it in some accessible place. The word used to
do this is WORD. WORD takes a string from the input buffer and
transfers it to some other part of memory. Exactly where it places it
depends on the version of FORTH. In Acornsoft FORTH it places
it in the word buffer. You can find the starting address of this stretch
of memory by using the word ‘word-buffer’ (WBFR) to place the
address of the first byte of the buffer on the stack:
WBFR ,
1088 OK
The figure ‘1088’ tells us where the word buffer begins. The word
WORD is capable of taking the whole input buffer and transferring
it to the word buffer. On most occasions we do not want the whole
62 Exploring FORTH
OK
t NAME? TYPE YOUR NAME: • QUERY
32 WORD .S ?
NAME?
TYPE YOUR NAMEt KING CANUTE
1088 CANUTE ?
Now WORD reads the input string only as far as the first space. The
word ‘KING’ is transferred to the word buffer, but ‘CANUTE’ is left
behind in the input buffer to puzzle the micro! Using a space as a
delimiter is a convenient way of ensuring that only one word is taken
in at a time. It is also possible to use a letter such as an ‘A’ as
delimiter:
NAME?
TYPE YOUR NAME: KING CANUTE
1088 NUTE ?
The string is transferred as far as the first ‘A’, leaving only ‘NUTE’ to
mystify the micro.
The result of using NAME showed that WORD leaves the address
of the start of the word buffer on the stack. WORD has transferred
the string to the word buffer, and has told us where to find it.
The usual way to fetch a stored number and put it on the stack is to
use the word ‘fetch’ (@). But it has been explained that FORTH uses
two bytes to store each number. ‘Fetch’ takes the address of the first
of these two bytes, then gets the values stored in the first byte and the
Interactive FORTH 63
next one, and uses them to calculate the stored number. In the case
of the word buffer, we might expect that each character is stored in a
single byte. To fetch a single byte and put it on the stack we use the
word ‘C-fetch’ (C@). This is one of a range of FORTH words used
for single-byte operations, all of which have a C as part of the word.
Here we use ‘C-fetch’ to find out what is stored in the first byte of the
word buffer:
1088 CB .
2 OK
If your FORTH includes WBFR, you can use the line ‘WBFR C@ .’
instead.
The result of this operation is the value 2. This is the number of
characters that WORD has put into the buffer. This 2 has nothing to
do with ‘KING CANUTE’. Since using NAME? we have typed in
the direct command given in the line above. The word most recently
placed in the word buffer is C@, which is 2 characters long. This
reminds us that words such as QUERY and WORD are being used
by FORTH all the time to receive input from the keyboard. If we
type in direct commands to find out how the buffers are operated,
the commands themselves interfere with what we are trying to find
out. The way round this is to put the test routines into words, such as
NAME?. These are then executed without any further input from
the keyboard except that which we want to test. This is why we are
forced to define and redefine NAME? so many times in this chapter.
Here is yet another version of NAME?. This one uses ‘C-fetch’ to
fetch the first byte from the word buffer immediately after WORD
has dealt with the input string:
NAME?
TYPE YOUR NAME: MERLIN
6 OK
These tests confirm that the number found in the first byte of the
word buffer is the number of characters in the string, including
spaces if any.
64 Exploring FORTH
1
Presumably the next bytes in the buffer contain the ASCII codes
OllA-'
for the characters in the string. There is a word COUNT which takes
us one step further. Given the start address of the word buffer, left at
top-of-stack by WORD, it finds out the number of characters and
hr % the start address of the string, leaving these at second-on-stack and
top-of-stack respectively. Here we see it in action:
NAME?
TYPE YOUR NAME: CASSANDRA
67 65 83 83 65 78 68 82 65 OK
Interactive FORTH 65
A table of ASCII codes (see Appendix B) shows that these values are
the codes for the letters of words typed in.
To sum up, there are three words important for string inputs:
Creating strings
New words
Switch off the computer, then reload FORTH. Or, if your version
provides a method of doing so (such as typing COLD), perform a
cold start. This clears the stack and, more important for this
exploration, clears away all the word definitions which you have
made. You begin with only those words provided by the writers of
your version of FORTH. Now type VLIST. You have probably
done this many times already for it is probably the first thing you
ever did. Writers of FORTH manuals for beginners are fond of
putting this first in the manual. It’s the easiest way for a beginner to
get something impressive on the screen! Just to be different, we have
left VLIST until Chapter Seven, the first chapter in which we need to
use it.
What you see on the screen is a list of all the words your FORTH
contains. You will now able to spot ‘colon’, QUERY, +, WORD,
‘dot-quote’ and all the other words you have used, apart from those
you have defined yourself. In the usual back-to-front method of
FORTH, the list is in reverse order. The first word the writers
defined is listed last, followed by the inevitable ‘OK’. The word most
recently defined is at the top of the list. The words are stored in
memory in the order shown by VLIST. Early words are low down in
memory, the more recent the word the higher in memory is its
address. These stored words constitute the dictionary that FORTH
uses when it operates. Any new words that you define are stored in
memory just above the existing dictionary. Their names are added to
the end (the top) of the list. The word HERE tells us the address of
the first byte available for receiving the definition of a new word:
HERE .
15461 OK
Interactive FORTH 67
t DOUBLE DUP + * J
Given a number on the stack, DOUBLE displays double its value.
What effect has this definition had on the value of HERE?
HERE .
15'180 OK
What do all these numbers mean? Let us take them a few at a time:
134 subtract 128 from this one: the result is 6 which is the number of
letters in the name of the word (DOUBLE).
68, 79, 85, 66, and 76 are the ASCII codes for the letters D, O, U, B,
and L.
197 subtract 128 from this one too, giving 69, the ASCII code for E,
the last letter of the name.
that these are values being stored as double-bytes. To find out what
these values are we use 2SHOW:
l 2SHOW DO I (» . 2 +LOOP *
15294 This is the address of the next word in the dictionary. This
value is called the Link Field Address.
8371 This is the first address of the machine code which has been
compiled to execute DOUBLE. This value is called the Code Field
Address.
7985 to 7627 These are values needed by the machine code routine.
This collection of values is called the Parameter Field. The address
of the first of these (15472) is called the Parameter Field Address.
12174 OK
' EXIT .
7629 OK
Fig. 7.2.
Interactive FORTH 69
‘ticked’ is EXIT. This was not part of our definition, but is included
automatically to terminate each word defined by ‘colon’. The figures
obtained by ‘tick’ each differ by 2 from the corresponding values
displayed by 2SHOW. ‘Tick’ is giving the parameter field address of
each word, while 2SHOW is giving the code field addresses which
appear in the parameter field of DOUBLE.
To sum up, a word definition consists of the following parts:
The Body: holding the Parameter Field, in which are the addresses
or other values needed by the machine code routine.
word was compiled. At run time, the parameter field of each word
contains the addresses of the machine codes routines or the code
field addresses of each word it calls upon. FORTH jumps directly
from one address to the next as it executes each word, performing
first this one piece of machine code, then the next. This is the secret
of the way FORTH combines high speed with great flexibility.
With VLIST as your chart and with SHOW and 2SHOW as your
instruments, you are ready to explore the deepest regions of the
FORTH jungle. Starting from a word such as DOUBLE, we have
been led to the words used in its definition. Next you could explore
the definitions of these other words. Gradually you could trace your
way up the tributaries, the words in defining these words, until you
come to the primitives, which use machine code directly. You are led
to the starting addresses of these routines, and, if you understand
machine code, can get right to the sources of FORTH. In following
these threaded pathways you are travelling the same routes that
FORTH uses when it executes an application. Few may wish to
venture so far, but the way is open for those of an exploring nature.
Before we return to define words to define string variables, we will
look at the definition of the word used for numeric variables,
VARIABLE. First to get our bearings:
HERE ♦
15529 OK
HERE ♦
15543 OK
It extends from 15529 to 15542, so we use SHOW to take a closer
look:
15543 15529 SHOW
135 87 69 65 84 72 69 210 143 60 12 33 0 0 OK
Taking 128 from 135 gives 7, the number of letters in the name
WEATHER. The table of ASCII codes confirms that the numbers87
to 210 are the name in code (subtract 128 from 210 to get 82, the code
for R). The last six numbers are the double bytes of the link field, the
code field and the parameter field, respectively, one double byte for
each. We use 2SHOW to read them, starting from address 15537
Interactive FORTH 71
(count your way along the row of single bytes from 15529 to find the
starting address):
The link field address is 15503. The code field address is 8460. Could
the zero be the stored value of WEATHER? We have said that
VARIABLE initialises each variable to zero when it is defined, so
this seems likely. To check that this is so, we set WEATHER to a
particular value:
123 WEATHER !
OK
And there it is, stored in the last two bytes of the parameter field.
Counting along the byte from 15529 tells us that the addresses at
which the variable is stored are 15541 and 15543. When we first
discussed the action of VARIABLE it was stated that it puts on the
stack the address at which the value is stored. To confirm this, try:
WEATHER .
15541 OK
Back to strings
The next task is to define the defining word STRVAR which will
define string variables. This is really a simple matter, for all that the
string variable needs to have is a head containing its name and the
usual link field and code field addresses, plus a body consisting of
enough bytes to hold the string to be stored there. Here is its
definition:
16 STRVAR NAME*
The line above defines a string variable called NAMES, which can
hold a string consisting of up to 15 characters. At this point you
could check that STRVAR has worked. Use the technique described
in the previous section. First find the value of HERE. Then use
SHOW to display 26 bytes up to the one before HERE. The first
should hold 5 (the number of characters in NAMES). Next come
5 bytes holding the ASCII codes for NAMES, then there are
4 bytes for the link field and code field addresses. Then there are 16
bytes for the parameter field. These may hold any values at present,
for the definition of the word does not provide for clearing the
parameter field.
Next we need a word for storing a word in NAMES, or in any
other string. A good name for this word is STR!, the T reminding us
of the word used for storing numerical values. We intend to use
STR! after WORD, and the name of the string variable, as in
‘WORD NAMES STR!’. WORD puts the start of the word buffer
(WBFR) on the stack. The string variable puts its PFA (parameter
field address) on the stack. STR! will operate on these two values.
Here is its definition:
‘C-fetch’ fetches the first value from the word buffer, which, as
explained earlier, is the number of characters in the string. This is
incremented by 1 so that the string variable will receive not only the
string but the number of characters as well.
Interactive FORTH 73
from\to\count
‘From’ is the starting address of the block of memory the bytes are to
be transferred from. In this case this address is to be the start of the
word buffer (WBFR). ‘To’ is the starting address of the block of
memory the bytes are to be transferred to. In this case this is to be the
beginning of the parameter field (PFA) of the string variable.
‘Count’ is the number of bytes to be transferred. The list above
shows that the stack has everything ready for CMOVE to operate
on. The bytes are transferred from the word buffer to the parameter
field.
We also need a word for taking the string which is stored in a
string variable and displaying it on the screen. One of the simplest
possible of such words is .STR:
NAME?
TYPE YOUR NAME! TARAS BULBA
OK
74 Exploring FORTH
NAME-* * STR
TARAS BULBA
OK
Usually the format would be more elaborate. In a game for example,
we could display the name of the person whose turn it is to play:
NEXT$ .STR
TARAS BULBA
OK
while saving the words we have devised in this chapter. Figure 7.3
shows a complete listing.
SCR * 17 11 H
0 ( STRING VARIABLES )
1 J SHOW DO I C@ . LOOP i
2 i 2SH0W DO I & . 2 +LOOP }
3 t SIRVAR CREATE ALLOT *
4 : STR! OVER C@ 1+ CMOVE J
5 J ♦STR COUNT TYPE CR f
6 i NAME? . " TYPE YOUR NAMEl *
7 QUERY 13 WORD
8 NAME$ STR! f
9
10
11
12
13
14
15
Fig. 7.3.
There is only one further point to be made. The words have no error¬
checking facilities built in to them. For example, if NAMES is
defined so as to allow up to 15 characters in the string and if the user
types in a name longer than this, only the first 15 characters will be
displayed. Maybe this does not matter in a given application. Too
long a name may spoil the display, so persons with over-long names
will have to put up with a truncated version. We shall return to the
subject of error checking in Chapter Eight.
To summarise
• The ‘key’ words, KEY, ?KEY and INKEY which detect key¬
presses (...ASCII code).
76 Exploring FORTH
Explore more
(1) Define a new version of the defining word for string variables,
STRVAR, which fills each string variable with spaces when each is
defined.
Interactive FORTH 77
(2) Define a word HALF which displays half the value of any
number placed on the stack. Then follow up the definition as far as
you can, threading your way through memory, using the techniques
described in this chapter.
easy to investigate (See Fig. 8.1). Equals leaves 1, only when the
numbers are equal. In YN?, Key should leave either 89 or
78 on the stack, depending upon whether the Y or the N key
was pressed. YN? places 89 on the stack and then uses ‘equals’ to find
4 4
1 OH
5 4 =
0 OK
Fig. 8.1.
out if the number left by KEY equals 89 or not. In other words, was
the Y key pressed? If it was pressed, the top-of-stack will be 1, if the
N (or any other) key was pressed it will be zero.
Next in the definition of YN? comes IF. Like most other FORTH
words, it expects to find a value on the stack. In particular, it expects
to find a 0 or a 1. It interprets a 1 to mean ‘true’. That is to say: “It is
true that the user pressed the Y key”. It interprets a 0 to mean ‘false’,
or ‘not true’ - the user did not press the Y. IF is a decision-taking
word.
Depending on the results of the test, true or false, the computer is
sent in one of two directions. If the result is true, the words following
IF, are executed, as far as THEN. After this the computer continues
executing the words, if any, which follow THEN. If the result is not
true, the words between IF and THEN are not executed. The
computer jumps straight to THEN and continues from there.
In the operation of the word YN?, after the Y key has been
pressed, the computer displays YES, then jumps to the carriage
return after THEN and executes that:
YN?
YES
OK
7 TRUE?
TRUE
OK
0 TRUE?
OK
Fig. 8.2.
YN?
YES THANK YOU!
OK
YN?
NO THANK YOU!
OK
Fig. 8.3.
Taking Decisions 81
Fig. 8.4.
Fig. 8.5. Nested IF ... ELSE ... THEN routines in YN?. The inner routine is
nested in the ELSE branch of the outer routine (compare with Fig. 8.13).
word. If the key was N it jumps to the first THEN and passes straight
on to the second THEN before leaving the word.
Now to return to what happens if the key is neither Y or N. In this
event, YN? calls itself. It does this as often as is necessary, until the
user has responded acceptably. When a routine calls itself, we refer
to it as a recursive routine.
In FORTHs such as the Acornsoft FORTH used on the BBC
Microcomputer, there are checks in the system to prevent words
from being compiled if they contain words that are not already
compiled. However, to allow for those who wish to define recursive
Taking Decisions 83
One of the dangers of the string handling words defined in the last
chapter is that a user may key in a name which is longer than the
string variable is intended to hold. We allowed up to 15 characters,
which is enough for most people, but there are those with long
names who like to type them out in full. READ? accepts up to 80
characters, possibly more in some FORTHs, and STR! will quite
happily put these into the parameter field of NAMES (or any other
string variable), even though the parameter field is not long enough
to hold them. All seems to go according to plan - you can even use
.STR to display the long string - but there is trouble in store. When
you try to use the word which was defined immediately after
NAMES, it seems as if the computer has forgotten all about it. If you
VLIST to find out what has happened to it, you may find part of
your long string appearing in the list instead of the name of the word
you thought was there. Other odd things may appear too! What has
happened is that STR! has written the whole of the long string into
memory and the part that would not fit into the parameter field of
NAMES has been stored in the space previously taken up by the
word defined after NAMES. Its name field now contains part of
84 Exploring FORTH
your long string and, if the string is long enough, its link field and
other fields will have been over-written too.
We need a way of checking that the string is not too long to fit into
NAMES. What we do when we find that the user has typed in a
string too long is a matter of preference. Maybe the computer should
emit a loud beep and a warning message should appear. This can be
easily arranged but, to keep the discussion simpler, we will just trim
off the excess characters and store the shortened string in NAMES.
This approach often causes less confusion to the user who should, in
any case, have been warned either in the manual or on the screen that
only a limited number of characters may be typed in.
The word to trim the string to size is TRIM:
The word greater than (>) is another testing word, similar to ‘equals’.
It puts a flag on the stack, which indicates whether second-on-stack
Taking Decisions 85
8 STRVAR NAME*
OK
NAME?
TYPE YOUR NAME: FRANCIS
OK
NAME* .STR
FRANCIS
OK
NAME?
TYPE YOUR NAME: ROGER BACON
OK
NAME* .STR
ROGER B
OK
Fig. 8.7.
86 Exploring FORTH
Rounding
VARIABLE RATE
OK
12 RATE !
OK
J POUNDS RATE 6 /MOD SWAP »
OK
{ PENCE 100 * RATE 0 /MOD SWAP
OK
Fig. 8.8.
add 1 to the number of PENCE. Rather than try to base the decision
on half of RATE, which would be awkward if RATE was an odd
number, such as 13, we double everything up before making the test.
This means that the remainder is to be ignored if twice the remainder
is less than 12, the full value of RATE. This is how we test the
remainder:
At this stage we could use IF.. .ELSE.. .THEN to take the action
required, but there is a quicker way to get the same result. To round
up we add 1 to the pence value; to round down we add nothing. We
need to add 0 or 1, and the flag is either 0 or 1. But the flag is the
‘wrong way round’; it is 1 when we want to add 0, and it is 0 when we
want to add 1! Another FORTH word comes to the rescue. This is
NOT which, being a negative, turns true into false and false into
true. In other words, it changes the flag to its opposite. After NOT
the top-of-stack is:
58 EXCHANGE
£4-83
OK
59 exchange:
£4-92
OK
UFO
bottom left corner. Move it across the screen by pressing one of the
keys:
Z left
X right
; up
/ down
The keys used can be changed to suit your micro or your own
preferences as will be explained later. Your aim is to wait for the
UFO to pass by and then ‘jump’ on it by pressing the correct key at
exactly the right moment. If you score a jump, you hear a beep and
your score is incremented by 1. However, if by chance you happen to
be in the way of the UFO and it runs on to you, you hear a longer
beep and your score is decremented by 1. The game runs for several
minutes, at the end of which your score is displayed. Naturally, the
aim is to get as big a score as possible within the allotted time.
The words are described screen by screen:
PART 1 (Screen 18) (Fig. 8.9)
SCR # 18 12 H
0 ( UFO GAME - PART 1 )
1 VARIABLE SCORE
2 VARIABLE MOVEX VARIABLE MOVEY -
3 VARIABLE YOUX VARIABLE YOLJY
4 1 ARRAY CREATE 2 * ALLOT DOES> SWAP 2 # + $
5 4 ARRAY CHARS
6 t CTIARDEF 23 >VDU 224 + 90 DO >VDIJ LOOP J
7 129 102 24 2A 255 219 126 60
8 0 CHARDEE
9 153 126 90 255 255 90 126 153
10 1 CHARDEE
11 t PL ACE IT 3.1 >VDU SWAP >VD1J >VDU
12 2 2ft ± > V D U t
13 : STORPLACE Dl.JP 4 PICK 2 * 1 +
14 CHARS ! OVER 4 PICK 2 *
15 CHARS ! PLACEIT i -■•->
Fig. 8.9. Screen 18.
This begins with definitions of the variables that will be needed in the
game:
0 X-position of UFO
1 Y-position of UFO
2 X-position of YOU
3 Y-position of YOU
This is exactly the same scheme as in the array CHARS of Chapter
Six. This array is called CHARS, too. The difference is that this
CHARS needs only four locations. Before we can have an array, we
define the word to define the array. This is the same word, ARRAY,
as used in Chapter Six. Line 4 of Screen 18 (Fig. 8.9) has the
definition of ARRAY. This is used on line 5 to define CHARS.
Next we will define the special characters of the UFO and YOU
(Fig. 8.10). You can, of course, substitute designs of your own for
these. Lines 6 to 10 are taken up with the definition of CHARDEF
and using it to define the two characters. If you had to use your own
version of CHARDEF in Chapter Six, to suit your micro or your
FORTH, use it again here. Another such word is PLACEIT (lines
11-12). Key in your own definition, if it is different from the one
shown. From this point on, except for the initialising word GO, we
shall be using only words that are likely to be found in all versions of
FORTH. The only changes needed will be those which allow for the
number of rows and columns on your screen and the direction in
which the rows are numbered.
Screen 18 ends with the definition of STORPLACE, exactly as in
Chapter Six.
PART 2 (Screen 19) (Fig. 8.11)
This begins with two more words from Chapter Six, BLANKIT and
MOVEIT. Next come four words to alter the direction of motion of
the UFO when it comes to the edges of the screen. In these we assume
that the rows of the screen are numbered 0 to 31 from top to bottom,
and the columns are numbered 0 to 39 from left to right. Actually we
do not use column 39. Placing a character at the bottom of this
causes a line-feed which upsets the display. LEFT changes the
direction of motion when the UFO hits the left margin of the screen.
‘0 CHARS @’ copies the X-position of UFO fromCHARS and puts
it at top-of-stack. ‘0=’ tests to see if it is equal to zero, this being the
left-most column. If your columns are numbered from 1, substitute4!
=’ for ‘0=’. This is followed by an IF.. .THEN routine. If the value is
zero (UFO is at left of screen) the value 1 is stored in MOVEX. The
effect of this will be to make UFO move toward the right when
MOVEIT is next used.
RIGHT operates in a similar way, giving MOVEX the value —1
(move left) if UFO is in column 38. If your screen has a different
92 Exploring FORTH
Some of these may not apply to your micro but, if they do, the
FORTH handbook should explain how to perform equivalent
operations. The Mode statement referred to above automatically
clears the screen. Include this action in GO if you have not used
MODE. Some FORTHs have a word CLS. An alternative is ‘12
EMIT’ which works on most machines which recognise standard
ASCII codes.
,'lA~ 7°P
SCR 14 H lA'lo
0 < UFO GAME PART 3 ) /<0^\
1 t PLAY 0 0 CHARS ! 0 1 CHARS !
'?
x.. <£$(Z: CHARS ! 22 3 CHARS !
3 1 MOV EX ! 1 MOVE'Y ! 0 SCORE !
4 : MUFO PORDER 0 MOVEX 6 MOVEY g MOVEIT t
5 i DELAY 100 0 DO LOOP J
6 : HALT 0 YOUX 1 0 YOUY ! i
7 J KEY/ 47 - IF 3 CHARS 8 31 =
8 IF ELSE 1 YOUY
9 THEN
10 ELSE THEN i
11 : KEY > DUP 59 = IF 3 CHARS 6 0 =
12 IF ELSE -1 YOUY
13 THEN DROP
14 ELSE KEY/ THEN ?
15
MYOU uses ?KEY to wait a short while for a key-press. The ASCII
code code of the key is placed on the stack.
KEYZ tests to see if the key pressed is Z (code 90). If not, it calls
KEYX. If the key is Z and YOU is not at the extreme left of the
screen, YOUX is given the value -1, to move YOU to the right. The
action continues without further examination of the key code. We
will look into the action of KEYZ in more detail later.
KEYX tests to see if the key pressed is X (code 88). If not, it calls
KEY;. If the key is X and YOU is not at the right of the screen,
YOUX is given the value 1, to move YOU to the right. The action
then continues.
KEY; tests to see if the key pressed is; (semicolon). If not, it calls
KEY/. If the key is ; and YOU is not at the top of screen, YOUY is
given the value -1 to make YOU move up. The action then continues.
KEY/ tests to see if the key pressed is /. If not, the action continues,
for all possible key presses have now been looked for. If the key is /
and YOU is not at the bottom of screen, YOUY is given the value 1
to make YOU move down. The action then continues.
Fig. 8.13. Nested IF ... ELSE ... THEN routines in KEYZ. The inner routine
is nested in the IF branch of the outer routine (compare with Fig. 8.5).
the action goes, it finishes at the outer THEN, and from there the
computer passes to the next word in MYOU.
The structure of KEYX and KEY; are the same as that of KEYZ.
KEY/ is a little simpler because, being the last word to be called, it
does not need to duplicate the code, and there is no value to be
DROPped at the end. Also, there is nothing in the outer ELSE
section.
These words have illustrated several aspects of decision taking in
FORTH.
PART 4 (Screen 21) (Fig. 8.14)
Having defined the KEY words, in reverse order so that each one can
96 Exploring FORTH
SCR # 21 15 H
0 ( UFO GAME -- PART T )
t 1 : KEYX DUP 88 - IF 2 CHARS (» 38 =
IF ELSE 1 YOIJX !
3 THEN DROP
q ELSE KEYf THEN l
: KEYZ DUP 90 = IF 2 CHARS § 0=
6 IF ELSE -1 YOUX !
7 THEN DROP
8 ELSE KEYX THEN }
9 : MYOU 10 ?KEY KEYZ. 1 BLANK IT
10 1 YOUX @ Y (!) U Y (» MO VEIT HALT 5
11 : SAME 0 CHARS (;? 2 CHARS 6 =
12 1 CHARS (? 3 CHARS @ ~
13 0= IF DROP 0 ELSE 1 = THEN *
ITt i HITUFO SAME IF SCORE (» 1 +
15 SCORE ! 7 EMIT THEN f .>
call the earlier ones, we come to MYOU. This uses ?KEY to detect
the key-press, the figure 10 allowing waiting period of 0.1 second. If
your FORTH has INKEY, which does not wait, use this instead of
TO ?KEY\ The essential point is that the word used should not wait
indefinitely for a key-press. This is followed by KEYZ, which calls
all the other KEY words, if necessary. YOUX and YOUY will now
contain values to produce the motion required. T BLANKIT’ clears
the previous image of YOU from the screen. Then the values
required by MOVEIT are put on the stack:
MOVEIT displays YOU in its new position and stores this position
in CHARS. HALT restores MOVEX and MOVEY to zero, so that
YOU does not move until another key-press is detected.
One vital element of game programs is coincidence detection. We
often want to know if two objects are in collision - if a bullet has hit
its target, for instance. Here we want t.o know if UFO and YOU are
at the same position on the screen. Coincidence is detected by
SAME, which is another example of decision-taking. It is also a
word that can be applied in many other games programs. The UFO
and YOU are in collision if both have the same X-position (0 and 2 in
CHARS) and both have the same Y-position (1 and 3 in CHARS.
First, SAME puts the X-positions on the stack and tests to see if they
Taking Decisions 97
are equal. If they are equal, ‘equals’ leaves true (1) on the stack. If
they are unequal, it leaves false (0). Next, SAME puts the Y-
positions on the stack and tests these to see if they are equal, leaving
a second flag on the stack. There are now four possible pairs of flags
on the stack:
SCR # 22 16 1-1
0 ( UFO GAME - PART 5 >
1
2 J HIT YOU SAME IF SCORE. (? 3.-
3 SCORE I 7 EMIT 7 EMIT THEN t
4
5
6 : FINISH 3.2 >VDU 5 0 DO CR
7 LOOP .* YOUR SCORE IS a
8 SCORE (? . 2 0 0 DO CR LOOP t
9
3.0
3.3. : UFO GO PLAY 20 0 0 0 DO 0 BLANK IT
3.2 MUFO HIT YOU DELAY MYQU HIT UFO
3.3 DELAY LOOP FINISH }
3.4
3.5
Fig. 8.15. Screen 22.
After the loop is over the game terminates with: FINISH. When all
is ready, type UFO, press RETURN, and the chase commences!
Variations
To summarise
• IF ... ELSE ... THEN (flag ...) used in the form <flag> IF
<action 1> ELSE <action 2> THEN <continue>.
• =>< equals, greater-than and less-than, the conditional operators
for comparing two numbers (nl\n2 ... flag).
• 0= 0> 0< ‘zero-equals’, ‘zero-greater’, and ‘zero-less’, for
comparing a number with zero (n ... flag).
• C! ‘C-store’ for storing a byte at a given address (byte\address...)
• R: R; ‘R-colon’ and ‘R-semicolon’ for beginning and ending
definitions of recursive words.
• NOT (flag ... inverted flag).
Explore more
(1) Define a word which, given two values on the stack, displays the
greater of the two.
(2) Define a word which, given two values on the stack, divides the
greater value by the smaller one and displays the result.
(3) Extend the definition of YN? so that, if the user types a letter
other than Y or N, a message such as “Please key Y or N” is
displayed. The routine then waits for one of the correct keys to be
pressed.
loop uses a loop index, I, which is incremented each time the loop is
repeated. Before the loop is begun, the finishing and starting values
of this index are placed on the stack. The finishing value is at second-
on-stack and the starting value is at top-of-stack. DO reads these
two values. The action which is to be repeated usually consists of one
or more words placed between DO and LOOP. Occasionally there
may be no words between DO and LOOP, when we merely want to
delay the computer, so as to slow down an application.
When the computer gets to LOOP, it increments I. Then it tests I
to find out if it is equal to or greater than the finishing value. If it is
not, the action is repeated. This continues, with I being incremented
each time round the loop until, eventually, I is equal to or greater
than the finishing value. There are no further repetitions of the
action. Note particularly that there is no Ith repetition.
One important feature of loops is that they cannot be initiated
immediately, by typing direct commands on the keyboard. They
must always be part of a definition of a word. The word
COUNTING demonstrates how the loop operates:
1 COUNTING 6 0 DO I ♦ CR
LOOP ;
The action consists of putting I on the stack and then displaying it,
followed by a carriage return (see Fig. 9.2).
COUNTING
0
1
3
4
5
OK
Fig. 9.2.
i COUNTING 6 0 DO I ♦ CR
2 +LOOP t
COUNTING
0
2
4
OK
Fig. 9.3.
: COUNTING 0 6 DO I , CR
-1 LOOP ;
We have reversed the values, making I start at 6 and finish as 0:
COUNTING
6
OK
J COUNTING 0 6 DO I * CR
-1 +LOOP }
Figure 9.4 shows its action. This example shows that the action of
TLOOP differs from that of LOOP when I is being decremented. It
repeats the action if I equals the finishing value, and stops only when
I exceeds the finishing value. This gives an extra repetition that we
do not get when I is being incremented.
104 Exploring FORTH
COUNTING
6
5
4
3
X-
1
0
OK
Fig. 9.4.
VARIABLE FIBO
OK
1 FIBO !
OK
Fig. 9.5.
FIBO @ FIBO
I FIBO I
DUP FIBO I
FIBO I
FIBO ! FIBO
+LOOP
The value of FIBO placed on the stack at the beginning of the loop is
the value which I had during the previous loop. The current value of
I is displayed and is stored away in FIBO ready for the next loop. At
Over and Over 105
the end of the loop, +LOOP adds the current value of I to the
previous value of I (FIBO). The result of this repeated action is to
give a FIBONACCHI series, in which each term is the sum of the
two previous terms:
FIBONACCHI
0 1 1 2 3 5 8 1.3 2,1 34 55 89 144 233
377 610 987 1597 OK
The series stops at 1597 because the next value calculated for I takes
it above its finishing value. Increasing the 2000 to some higher
amount makes the calculation continue for more terms of the series.
Loops may be nested one inside the other, provided that the inner
loop is completely enclosed by the outer one. Figure 9.6 shows an
example. The outer loop is to start with its index equal to 0 and
{ LOOPS 4 0 DO 9 6
DO J * I , CR
LOOP
LOOP *
Fig. 9.6
finishes with it equal to 4. The inner loop starts with its index equal
to 6 and finishes with it equal to 9. Obviously two indexes are
involved, one for each loop. With nested loops, the index I refers to
the inner loop. Another index, J, refers to the loop surrounding the
inner loop. The values of I and J may be used within the inner loop
only, as in the example above. Figure 9.7 shows that for each stage of
LOOPS
0 6
0 7
0 8
1 6
1 7
1 8
2 6
2 7
2 8
3 6
3 7
3 8
OK
Fig. 9. 7.
106 Exploring FORTH
the outer loop (J equals 0, 1, 2 and 3) the inner loop runs through its
own stages (I equals 6, 7 and 8).
I has more uses than just counting the number of repetitions. We
can make use of it in repeated calculations, as in this word which
calculates a table of squares:
I is duplicated twice, filling the top three positions of the stack. One
copy of I is displayed directly. The other two are multiplied together
and their product, the square of I, is then displayed as in Fig. 9.8. It
may happen that a calculation has to proceed until its result reaches
SQUARES
0 0
.1. 1
2 4
3 9
4 16
5 25
6 36
7 49
8 64
9 8.1.
OK
Fig. 9.8.
a given value. We might want only the squares which are less than
40, for example. This could be arranged by setting the finishing
value of the loop to 7. It is easy to work out in advance that 6 squared
is 36, while 7 squared is 49. The action is not executed for 1=7, so
squares of numbers from 0 to 6 are displayed. If the calculation is a
complicated one, especially if it includes unpredictable values that
are calculated by other actions of the application, we may not be
able to say in advance what the finishing value of I should be. In this
case we can use the word LEAVE to escape from the loop (see Fig.
9.9). I and its square are calculated as before. The value of I-squared
is left on the stack to be tested by ‘greater-than’ as described in
Chapter Eight. If I-squared is greater than 40, a true flag is left on the
stack. The IF causes LEAVE to be executed and the computer leaves
the loop (see Fig. 9.10).
SQUARES
0 0
11
2 4
3 9
4 16
5 25
6 36
7 49
OK
Fig. 9.10.
least it would do if we did not have ESCAPE and BREAK keys and a
power switch on the computer. The loop (Fig. 9.11) repeats the
action for as long as the normal running of the computer continues.
Here we use BEGIN ... AGAIN to display a message repeatedly:
USER-FRIENDLY
H E L L 0 H E L. L 0 H E L. L 0 H E L L 0 H E L L 0 FI E L L. 0 H E I... L 0 H E L L 0
HELLOHELLOHELLOHELLOHELLOHELLOHELLOHELLO
H E L L 0 H E L. L 0 H E L L 0 FI E L LOME L L 0 H E L L 0 H E L. L 0 H E L L 0
H E L L (3 H E L L 0 HELLOHELLOHE L L 0 H E L. L 0 H E L L 0 H E I... L. (3
Escape
When the game is over, the computer waits for a key to be pressed.
This gives the player time to read the score. The game is repeated
when any key is pressed.
If your FORTH does not have BEGIN ... AGAIN, a practicable
alternative is to use a DO ... NEXT loop with a suitable high
finishing value. One can safely assume that most people would
become bored with UFO (or exhausted!) and switch off before
‘20000 0 DO UFO KEY LOOP’ runs to completion.
Fig. 9.13. Comparing'(a) a BEGIN ... UNTIL loop with (b) a BEGIN ... WHILE
REPEAT loop.
110 Exploring FORTH
tested at the end of the loop. This means that the loop is executed at
least once, even if the condition is true to begin with. Figure 9.13(b)
illustrates the BEGIN.. .WHILE.. .REPEAT loop. This is discussed
in Chapter Ten.
We will now design a reaction-time testing word, TEST, as an
example of the use of BEGIN ... UNTIL. The variable TOTAL is to
hold the total reaction time as it accumulates. RESET is used to
reset TOTAL to zero before the test is to be performed. DELAY
uses DO ... LOOP to give a delay of several seconds and, after that,
displays the letter A on the screen (see Fig. 9.14).
VARIABLE TOTAL
OK
{ RESET 0 TOTAL ! i
OK
J DELAY 20000 0 DO LOOP 65 EMIT i
Fig. 9.14.
The main part of it is a BEGIN ... UNTIL loop. The loop starts by
putting TOTAL on the stack, adding 1 to it and storing the result as
the new value of TOTAL. In short, TOTAL tells us how many times
the loop has been repeated. The action of REACTION depends on
?KEY. As explained in Chapter Seven, ?KEY makes the computer
wait for a definite length of time for a key to be pressed. If no key is
pressed during that time the computer continues. The value 2 is put
on the stack before ?KEY is used, making the computer wait for one
fiftieth (two hundredths) of a second each time around the loop. The
loop repeats indefinitely, until the A key is pressed. Each time round
the loop, test the value left by ?KEY. This is done by placing 65 (the
ASCII code for A) on the stack and then using ‘equals’. If a true flag
is left on the stack by ‘equals’, the loop is not repeated. The action of
REACTION, so far, is to wait again and again for a key to be
pressed until the A key is pressed. Then the computer leaves the loop
and, after a carriage return, displays the latest value of TOTAL. This
tells us how many fiftieths of a second have elapsed since the loop
was started.
Over and Over 111
TEST
A
16 OK
The example above shows the sequence of events. TEST is keyed in
and RETURN is pressed. Nothing happened for a few seconds, until
the A appeared. The A key was pressed immediately after that, but
not before the loop had repeated 16 times. The user’s reaction time is
16/50 second, or 0.32 second. You could add routines to TEST to
display the final result in seconds or hundredths of a second. Test
your own reaction times - it should be easy to beat the result shown
above.
YN? revisited
In Chapter Eight we devised a word YN? which waits until the user
has typed either Y or N. It made use of recursion to repeat the input
sequence until one or the other of these keys was pressed. There were
certain unsatisfactory aspects to this approach and it is now clear
that a BEGIN ... UNTIL loop could provide a better solution to the
problem. We need to repeat the keying in sequence until either the Y
or the N has been pressed. The new definition of YN? given in Fig.
9.15 has a snag, as will be explained later, but it makes a starting
point for exploring the possibilities.
The loop begins with KEY, which waits indefinitely for a key to be
pressed and then puts its ASCII code on the stack. We are going to
want to test the value left by KEY, to see if it corresponds to Y
(ASCII code = 89), to test it again to see if it corresponds to N
(ASCII code = 78) and finally to display the result as Y or N on the
O
112 Exploring FORTH
YN?
Y
OK
YN?
N
OK
Fig. 9.15.
screen. Or we might use the value (78 or 89) left on the stack for some
other purpose. In all we need to have three copies of the ASCII code,
and this is provided by ‘DUP DUP’. The sequence of finding out if
the Y or the N has been pressed is as follows (X is the value left by
KEY):
Flag -1 Flag -2
If Y pressed 1 0
If N pressed 0 1
If any other 0 0
key pressed
the loop. If Y or N is pressed, so that the flag is true, the loop ends.
The value X left at top-of-stack is then displayed, using EMIT.
This word works as expected but is faulty in one important
respect. Whenever a key other than Y or N is pressed its value (X) is
left on the stack when the loop repeats. A series of values is gradually
accumulated on the stack during the running of an application. It is
a general rule that values must not be allowed to accumulate in this
way, otherwise a ‘Stack Full’ error may be caused. Words should
leave on the stack only such values as may be required by succeeding
words. DROP seems a likely candidate for getting rid of the
unwanted X, but the problem is where to use it. It must be used
inside the loop, yet we cannot use it immediately before UNTIL. We
do not know if X is to be dropped or not until UNTIL has tested the
flag! The solution is to drop it at the beginning of the loop,
immediately after BEGIN. Each time the loop repeats, the value X
left on the stack by the previous loop is DROPped, before the next
key-press is accepted. This new version of YN? begins by placing a
dummy value (0, but it could be anything) on the stack ready for
DROPping the first time DROP is encountered:
On the second and subsequent times round the loop the computer
returns to BEGIN, not to the 0, so it is the value X which is dropped.
This word leaves nothing behind on the stack.
Which number?
NO?
3
OK
Fig. 9.16.
114 Exploring FORTH
Flag-1 Flag-2
key 0 or less 0 1
key 1 1 1
key 2 1 1
key 3 1 1
key 4 1 1
key 5 or more 1 0
NOS?
3
cr
8
1
OK
2 VALUES 0 .
2 OK
4 VALUES 6 ♦
1 OK
Fig. 9.18.
Some FORTHs lack BEGIN ... AGAIN, but this deficiency can
easily be remedied by adapting the BEGIN ... UNTIL loop. The
word immediately preceding UNTIL is 0:
This places a false flag on the stack every time round the loop. As a
result, UNTIL never finds true and the loop never ends.
To summarise
Explore more
(1) Write a word to display a table of numbers and their cubes, for all
the number from 1 up to the largest number with a cube less than
30000.
(2) Write a word to check that a number of one or more digits, typed
in by the user, lies within a given range, for example (1-99).
(3) Devise a simple game along the following lines, and write the
words needed for it. An aeroplane flies repeatedly across the screen
from left to right. Each time it crosses the screen the player’s score is
increased by 1. The player has to shoot the aeroplane down by
pressing the space-bar at the exact moment the aeroplane reaches
the centre of the screen. If the key is pressed at the right time, the
plane explodes, the game ends and the player’s score is displayed. If
the player presses the key too early or too late to hit the aeroplane,
the player’s score is increased by 1. The object of the game is to
obtain a very small score.
Chapter Ten
Sorting Numbers
SCE #23 17 H
0 ( SORTING - PART 1 )
1 J ARRAY CREATE 2 * ALLOT
2 D0ES> SWAP 2 * + f
3 5 ARRAY VALUES
4
5 VARIABLE. NEXT
6 J SET 37 0 25 1 48 2 21 3 30 4 5 0
7 DO VALUES ! LOOP }
8 : SHOWVAL 5 0 DO I VALUES @ . LOOP i
9
10 l COMP DUP VALUES 8 ROT DUP
11 VALUES 6 ROT OVER OVER
12 > IF ROT VALUES' ! SWAP VALUES !
13 ELSE 2DR0P 2DR0P THEN t
14
15 -->
Fig. 10.1. Screen 23.
In this case the values would be swapped so that the lesser value
(123) goes into the location with smaller number (location 4). If the
values held were as follows:
COMP expects nl n2
DUP nl n2 n2
VALUES @ nl n2 (n2)
ROT n2 (n2) nl
DUP n2 (n2) nl nl
VALUES @ n2 (n2) nl (nl)
ROT n2 nl (nl) (n2)
OVER OVER n2 nl (nl) (n2) (nl) (n2)
Sorting Numbers 119
The above shows what happens if the values are in the wrong order
and so need to be swapped. If ‘greater-than’ (>) leaves a true flag, the
IF branch of the routine comes into operation. If the flag is false,
there is no swapping.
ELSE finds 4 values on the stack. These are got rid of by using
2DROP twice. 2DROP is really intended for DROPping double¬
precision values from the stack (see Chapter Eleven), but we can use it
here for DROPping two single-precision values at once.
Exchange sort
The first type of sort that we shall look at is the exchange sort. The
way this operates is shown in Fig. 10.2. The rows of the diagram
show the contents of VALUES, each time COMP has just
exchanged two values. Ignore the words at the right-hand of the
figure for the present.
The principle of this sort is as follows:
(1) Take the value in the first location and compare it with the values
in the other locations in turn, starting with the value in the second
location and proceeding to the end of the array. If the value in the
first location is greater than the one it is compared with, exchange
them. The result of this is to place the smallest value of all in the first
location. Figure 10.2 shows two such exchanges, resulting in the
smallest value (21) being placed in location 0.
(2) Repeat the above action but this time compare the value in the
second location with all others in succeeding locations. Exchange when
necessary. This puts the second smallest value (25) in the second
location (1).
(3) Continue in this way comparing the third and fourth (last-but-
one) locations with succeeding locations. Eventually the numbers
will be in ascending numerical order.
:EXSORT
LOCATIONS IN VALUES BEGIN
0 = FROM 1 2 3 4 = END OVER OVER
37 25 48 21 30 SWEEP (40)
21 25 48 37 30
21 25 48 37 30 SWEEP (42)
X
21 25 37 48 30
21 25 30 48 37
21 25 30 48 37 SWEEP (43)
X
21 25 30 37 48
1+ OVER OVER — UNTIL
2 DROP;
Fig. 10.2. Exchange Sort: (left) values in array VALUES after each swap.
(Right) stages in the action of EXSORT. The numbers in brackets after SWEEP
indicate the range of locations over which it sweeps. Shaded locations hold
values sorted into their final places.
SET
OK
SHOWUAL
37 25 48 21 30 OK
Fig. 10.3.
Sorting Numbers 121
SCR * 24 18 H
0 < SORTING - PART 2 )
1 t SWEEP DUP 1+ NEXT !
2 BEGIN OVER 1+ NEXT (» >
3 WHILE DUP NEXT (? COMP NEXT (»
4 1+ NEXT ! REPEAT 2DR0P f
5 J EXSORT BEGIN OVER OVER
6 SWEEP 1+ OVER OVER = UNTIL 2DROP I
7
8
9
10
11
12
13
14
.1.5 —>
SWEEP expects e f
DUP e f f
1+ e f f+1
NEXT ! e f
BEGIN e f
OVER e f e
1+ e f e+1
NEXT @ e f e+1 (next)
> e f flag
If the value of NEXT (listed above as (next) ) is less than e+1, the
sweep is to continue. The flag is true and WHILE portion of
SWEEP will be executed. Note that e and f have been retained on the
stack throughout all the above stages, ready for use in the loop:
WHILE e f
DUP e f f
NEXT @ e f f (next)
COMP e f
NEXT @ e f (next)
1+ e f (next)+l
NEXT ! e f
REPEAT e f
The loop tells COMP (p. 118) to compare the number in the first
location with the number in the next location. Then NEXT is incre¬
mented. Note that e and f are left on the stack ready for BEGIN. In
constructing a loop it is essential that the values required for setting the
flag in the BEGIN... WHILE section are left on the stackjust before
the REPEAT.
During the loop, NEXT is incremented so that, as the sweep
proceeds, the contents of location 0 are compared in turn with the
contents of location 1, then with location 2, and so on to the end of
the array.
SWEEP ends with 2DROP to get rid of e and f when the loop no
longer repeats.
Now to see SWEEP in action. As explained earlier, the first sweep
Sorting Numbers 1 23
4 0 SWEEP
OK
SHOWVAL
21 37 48 25 30 OK
Fig. 10.5.
4 1 SWEEP
OK
SHOWVAL
21 25 48 37 30 OK
Fig. 10.6.
4 2 SWEEP
OK
SHOWVAL
21 25 30 48 37 OK
4 3 SWEEP
OK
SHOWVAL
21 25 30 37 48 OK
Fig. 10.7.
After using SWEEP, EXSORT adds 1 to the value off (at top-of-
stack) ready to repeat the sweep with a new starting location.
However, if f now equals e, no further sweeps are required. So e and
the new f are copied by OVER OVER and tested with ‘equals’. If
they are equal, the flag left on the stack is 1 and UNTIL does not
cause a repeat. The remaining values of e and f are disposed of by
2DROP. The stages of action of EXSORT are indicated at the right-
hand side of Fig. 10.2. Figure 10.8 shows EXSORT in action. The
SET
OK
4 0 EXSORT
OK
SHQWVAL
21 25 30 37 48 OK
Fig. 10.8.
Bubble sort
2 DROP;
Fig. 10.9. Bubble Sort (left) values in array VALUES after each swap. (Right)
stages in the action of BUBSORT. Shaded locations hold values sorted into
their final places.
The Bubble Sort derives its name from the way in which the
smaller values tend to rise toward the beginning of the array, like
bubbles in a liquid. This is why we have called the sweeping word
RISE. It requires the values of e and f on the stack. It is left to the
reader to work out how it operates, by setting out the stack action as
for the Exchange Sort words described above. Use SET to place
values in the array as before and use RISE repeatedly, until
SHOWVAL indicates that the sorting is complete. Compare the
results at each stage with the rows of Fig. 10.9.
126 Exploring FORTH
SCR * 25 19 H
0 ( SORTING - PART 3 )
1 VARIABLE SWAPPED
2 : COMPS DUP VALUES 0 ROT DIJP
3 VALUES (? ROT OVER OVER
4 > IF ROT VALUES ! SWAP VALUES !
5 0 SWAPPED !
6 ELSE 2DR0P 2DR0P THEN J
7 t RISE BEGIN DUP DUP 1+ COMPS
8 1+ OVER OVER = UNTIL DROP DROP J
9 : BUBSORT BEGIN 1 SWAPPED !
10 OVER OVER RISE SWAPPED (?
11 UNTIL 2DR0P i
12
13
14
15 —>
SET
OK
SHOWVAL
37 25 48 21 30 OK
4 2 EXSORT
OK
SHOWVAL
37 25 21 30 48 OK
1 0 EXSORT
OK
SHOWVAL
25 37 21 30 48 OK
Fig. 10.11.
Sorting Numbers 127
Quicksort
Exchange Sort and Bubble Sort are useful when the number of items
to be sorted is small. Their disadvantage is that as the number of
items increases the number of comparisons to be made increases
alarmingly. Doubling the number of values to be sorted increases
the sorting time fourfold. Bubble Sort can be quick if the values are
in more-or-less the right order to start with, but takes much longer
than the Exchange Sort if the numbers are in reverse order. It has the
disadvantage that exchanges always occur between adjacent
locations so that a value can never move more than one place at a time
along the array. A low number which begins at the top end of the
array is going to take a long time to reach its destination.
Quicksort has a different approach to sorting, as illustrated in Fig.
10.12. Widely separated numbers can be exchanged, so movement is
rapid. The first step is to choose a value called the comparand. For
convenience, this may be taken as value in the ‘middle location’ of
the array. If the array has an even number of locations, it is the last
location in the first half of the array. In Fig. 10.12 the comparand
(48) has a circle drawn round it. There are two variables, called the
left pointer (LP) and the right pointer (RP). To start with, these
point at the ends of the array. Thus LP = 0 and RP = 4 in this
example. We begin by incrementing the value of LP until the value
in the location it points to is equal to or greater than the comparand.
In this example, we move LP to location 2, which contains the
comparand itself since the values in locations 0 and 1 are both less
than the comparand. Next we decrement RP until the value in the
location it points to is equal to or less than the comparand. In this
example, it already points to the value 30, which is less than the
comparand, so no change is necessary. Later we shall see that a
BEGIN.. .WHILE.. .REPEAT loop is used to move the pointers.
The example of RP given above is a case in which no change is
128 Exploring FORTH
LOCATIONS IN VALUES
0= FROM 1 2 3 4 = END
FULLY UNSORTED
ARRAY
SWAP VALUES
POINTERS
CROSSED
RP SETS END OF
NEW SUB-ARRAY
SWAP VALUES
SWAP VALUE
WITH ITSELF
POINTERS
CROSSED
LP SETS FROM OF
NEWSUB-ARRAY
POINTERS CROSSED
LP AT END; RP
LP
BEYOND FROM;
SORTING COMPLETE
KEY:
t
LP
t
RP
POSITIONS OF POINTERS AT
START OF EACH STAGE
POSITIONS TO WHICH
POINTERS ARE MOVED
o COMPARAND IS CIRCLED
Fig. 10.12. Quicksort. Shaded locations hold values sorted into their final
location.
Sorting Numbers 129
SCR * 26 1A H
0 ( SORTING - PART 4)
1 UAPIABLE END VARIABLE FROM
2 VARIABLE LP VARIABLE RP
3 I LEFT BEGIN DUP LP (? VALUES 0 >
4 WHILE LP 0 1+ LP ! REPEAT DROP f
5 : RIGHT BEGIN DUP RP (? VALUES (? <
6 WHILE RP 6 1- RP ! REPEAT DROP ?
7 J READ G? VALUES B *
8 J PUT (? VALUES ! *
9 t EXCH LP READ RP READ LP PUT RP PUT
10 L.P L» i+ LP ! RP 6 1- RP ! t
11 t SORT BEGIN DUP DUP LEFT RIGHT
12 LP (? RP 0 > DUP
13 IF ELSE EXCH THEN UNTIL DROP i
14 J COMPARAND OVER OVER LP ! RP !
15 +2/ VALUES (? ; —>
OK
Fig. 10.13. Screen 26.
SCR * 27 IB H
0 ( SORTING - PART 5)
1 RJ QUICK COMPARAND SORT
2 FROM (? RP @ <
3 IF RP (? DUP END ! FROM & QUICK
4 THEN
5 LP Q END Q <
6 IF END (? LP @ DUP FROM ! QUICK
7 THEN Rt
8 : QUICKSORT OVER OVER FROM ! END !
9 QUICK t
10
11
12
13
14
15
Fig. 10.14. Screen 27.
LEFT and RIGHT are routines to move the left pointer along the
array as described above. RIGHT does the same thing for the right
pointer. These routines contain WHILE, which makes it possible for
no change of the pointer to occur if it is already in an acceptable
position.
Sorting Numbers 131
This will display the array and the key variables after each swap is
done.
QUICKSORT works extremely fast. It was tested by setting up a
special array of 101 values, 0 to 100 in reverse (descending) order.
Sorting these into ascending order took EXSORT about 20 seconds.
A BASIC version of Quicksort took 3*/2 seconds. QUICKSORT
took less than 2 seconds.
To summarise
Explore more
(1) Adapt one of the sorting routines to sort numbers into reverse
(descending) order.
(3) Write a word to sort words into alphabetical order, using the
ASCII codes of the letters. Hints: (a) Define a word to define an array
to hold the words (each word could be up to, say, 10 letters long),
(b) Write a word to compare two words letter-by-letter. It starts by
comparing the first letter of each, then the second and so on. As soon
as it finds that the words are in the wrong order it stops comparing
them and swaps them.
Chapter Eleven
Kinds of Numbers
30000 20000 + ♦
-15536 OK
The answer is obviously wrong. The reason for this is that, as explained
in Chapter Five, the computer is using 15 bits of the 2 bytes for the
value of the number, and the 16th bit for its sign. Adding these two
numbers together has carried over into the 16th bit. It becomes a T,
134 Exploring FORTH
30000 20000 + U.
50000 OK
The addition has taken exactly the same course as before. The
difference comes when the computer goes to the top two bytes of the
stack and works out what number it represents. ‘U-dot’ makes the
computer take all 16-bits as part of the value. It follows that this
word should not be used following calculations which could
possibly produce a negative result.
The range of unsigned single-precision numbers is from 0 to
65535.
Double-precision numbers
12, 400GOO. D+ D.
400012 OK
6000 7000 M* D.
42000000 OK
12000139. 3000 U/ , .
4000 139 OK
136 Exploring FORTH
Floating-point numbers
SCR # 28 1C H
0 ( FLOATING-POINT INPUT )
1 VARIABLE FIGS VARIABLE PLACES
2 : ACC 48 - DUP 1 ♦ R FIGS (» 10 *
3 + FIGS ! PLACES (? 1+ PLACES ! f
4 J CALC DUP 46 = IF 0 PLACES ! EMIT
5 ELSE ACC THEN f
6 t SLASH DUP 47 = IF ELSE CALC THEN f
7 t VALID DUP DUP 45 > SWAP 58 < =
8 IF SLASH THEN DROP t
9 t FPIN 0 FIGS ! 0 PLACES !
10 BEGIN KEY DUP 13
11 = NOT WHILE
12 DUP VALID REPEAT DROP !
13
14
15
Fig. 11.1. Screen 28.
variable FIGS stores an integer which has the same digits as the
floating-point number but no decimal point, while the variable
PLACES stores the number of decimal places in the floating-point
number. For example, if the number is 123.45, FIGS holds 12345,
and PLACES holds 2. FIGS and PLACES are intended to hold the
results of the conversion but not necessarily to store the results
permanently. If the application deals with several floating-point
numbers, as is likely, the values in FIGS and PLACES are to be
transferred to other variables or to arrays. Figure 11.2 shows how the
conversion works. There are two ways in which the conversion can
be tackled. The user could type all the characters of the number at
the keyboard and these would then be held as a string in the input
buffer. The conversion could then be carried out on the contents of
the buffer, perhaps after it had been transferred to the word buffer.
The other approach is to analyse each key-press as it is made. This is
the method adopted here. Each key-press is examined and is
accepted only if it is a numerical character, a decimal point or a
RETURN (indicating that the number is complete). The whole
application is included in a BEGIN ... WHILE loop which accepts
input until a carriage-return is detected. The 13 on line 10 of Screen
28 (Fig. 11.1) refers to the ASCII code for carriage return, which is
13.
If the key pressed is not the RETURN, VALID checks that one of
the other acceptable keys has been pressed. Since the ASCII code for
138 Exploring FORTH
V is 46 and the codes for the numerals run from 48 to 57, it is simpler
to accept all codes in the range 46 to 57 at this stage and reject code
47 (/) later. Code 47 is rejected by the word SLASH. This takes the
micro to CALC where the calculation begins. The first step is to
detect if the key is V. If so, PLACES is set to zero and the decimal
point is displayed on the screen. If a numeric key has been pressed,
the action passes to ACC which accumulates the figures of the
number in FIGS. The code is converted to the actual number by
substracting 48. This is displayed on the screen. The effect, as far as
the user is concerned, is that the numbers or the decimal point
appear on the screen just as they do when being entered in the
ordinary way. The figures are accumulated in FIGS by taking the
value already in FIGS (see line 2 of Screen 28 (Fig. 11.1)), multiplying
Kinds of Numbers 139
it by 10, adding the current figure to it and storing the result in FIGS.
The final step is to increment PLACES, to count the number of
figures that has been typed in. CALC resets PLACES to zero when a
decimal point is encountered. PLACES thus holds the number of
figures entered after the decimal point. One thing to be considered
is that if the user keys in an integer (for example, 456), PLACES will
hold 3. Yet 456 has no decimal places. It is essential that the user
should type a decimal point at the end of the number when working
with floating-point numbers (for example, 456. ). Some FORTHS
with floating-point facilities require this terminal point, as already
mentioned. In Fig. 11.3 we see FPIN in action.
FPIN
123 *450K
FIGS (3 . PLACES (? .
12345 2 OK
FPIN
30 * 0 06OK
FIGS (? , PLACES (? *
30006 3 OK
Fig. 11.3.
The limitation of FPIN depends on the fact that the figures are
stored in FIGS, which holds a signed integer. FPIN can accept no
more than 5 digits in total and the maximum values it can accept are:
Addition or subtraction: If the ‘places’ are the same, ‘just add the
‘figures’. If the places are different, take the number with the smaller
‘places’, multiply the ‘figures’ by 10, and add 1 to the ‘places’. Repeat
until ‘places’ is the same for both, then add or subtract the ‘figures’.
140 Exploring FORTH
SCR # 29 ID H
0 < FLOATING-POINT OUTPUT )
1 i DIVIS 1 BEGIN PLACES (» DUP 0>
2 WHILE SWAP 10 * SWAP 1-
3 PLACES !
4 REPEAT DROP i
5 : INTEG DUP FIGS 0 SWAP /MOD 5 .R f
6 i ZEROES BEGIN OVER OVER <
7 WHILE 10 / . * 0“ REPEAT i
8 : DPL.S SWAP DUP 0 =
9 IF 2DR0P
10 ELSE ♦" ♦’ 10 / ZEROES
11 DROP . THEN ?
12 : FPOUT DIVIS INTEG DPLS 1
13
14
15
Fig. 11.4. Screen 29.
Value in Value of
PLACES divisor
0 1
1 10
2 100
3 1000
4 10000
5 100000
Kinds of Numbers 141
INTEG divisor
DUP divisor divisor
FIGS @ divisor divisor FIGS
SWAP divisor FIGS divisor
/MOD divisor remainder quotient
5 .R divisor remainder
FPIN
33.440K
FPOUT
33♦44 OK
Fig. 11.5.
Some suggestions for adding to FPIN and FPOUT appear at the
end of this chapter, under EXPLORE MORE.
FORTH has several variables of its own which are automatically set
142 Exploring FORTH
to certain values when the computer is switched on. The one we are
interested in in this section is BASE:
BASE 0 .
10 OK
The initial value in BASE is 10. This is the base of the number system
that FORTH normally uses, the decimal system. The value in BASE
can be changed:
8 BASE !
OK
6 5 + ,
13 OK
6 4 * ♦
30 OK
2 BASE !
OK
1 1 + ♦
10 OK
10 11 * ,
110 OK
110 DECIMAL ♦
6 OK
The value 110 placed on the stack is now displayed in decimal form.
Of course, the computer is actually working in binary all the time.
Numbers stored are stored on the stack in binary and all the
calculations are in binary. Changing the base to 8, 10 or any other
value simply tells the micro which base we want to work in. It then
knows that we want it to accept our input with reference to the
selected base. For example, if we type TOO’ when BASE is 2, it stores
it directly as ‘100’. But if BASE is 10, it knows we mean ‘a hundred’,
and stores the value as the binary equivalent of a hundred, 1100100.
Similarly, when displaying a number on the screen, it displays it in
whatever base we have selected. Here is a word to make the
computer display in decimal a value which we have entered in
binary:
This word is very useful when you are working out the values for
user-defined graphics, as in Chapter Six. First put the computer into
binary mode by typing ‘2 BASE !’. Then use BIN, as below:
11010110 BIN
214 OK
HEX
6 5 + ♦
B OK
3 5 * ♦
F OK.
F 1 + ♦
10 OK
Fig. 11.6.
J HD DECIMAL U. HEX
FFEE HD
651=118 OK
Fig. 11.7.
bases. You can work in any of a wide range of bases. Figure 11.8
shows what happens in base-70. Obviously base-70 requires 70
different characters to express its numbers. It uses the numerals 0 to
9, then it will need all the letters of the alphabet. This provides only 36
70 BASE !
OK
Z 1 + ,
C OK
Z Z * ♦
HZ OK
Fig. 11.8.
we had better look at the entire range of symbols. Figure 11.9 shows
how the word BASE70 displays these.
J BASE70 11 0 DO I ♦ LOOP ?
OK
BASE?0
0 1 '? 3 4 5 6 7 8 9 ABC D E F G H I J
K L M N 0 p G R 3 T U U W X Y Z r: \ 3 t
i 3 b e d e f G h i J k 1 n o p G r
<r» t u V w v w 2 ■c 1 1.0 OK
Fig. 11.9.
Random numbers
A + C
VARIABLE SEED
OK
J RNDl SEED @ 2011 * 5 +
32767 AND DUP SEED !
OK
: END RNDl 32767 %/ *
OK
Fig. 11. W.
Kinds of Numbers 147
the 16th digit, so keeping the value of the number within the range of
positive signed integers (0 to 32767). The new value is stood in SEED
and a copy of it is left at top-of-stack.
RND1 has produced a random number in the range 0 to 32767.
But to simulate the throwing of a dice, for example, this range is far
too great. RND allows us to specify the range. RND requires you to
put a value on the stack to indicate the maximum value the random
should have. ‘Times-divide’ then multiplies the random number (left
by RND1) by your maximum value, and divides the product by
32767. This produces an integer in the required range. Since division
rounds down in FORTH, the maximum value that the random
number can have is one less than the value you have placed on the
stack.
Figure 11.11 shows a word to simulate the throw of a six-faced
dice. Rather than have just one throw we use a loop to ‘throw’it 100
DICE
1 5 2544345 3 446651615 2
3 5 4 6 323651 3 623666643
4 5 3 2 62 3 4621146445455
5356 1 224 1 56 3 5 2516142
5344444144643411 1 233
OK
Fig. 11.11.
LOOP i
CRAPS
’8 6 7 8 4 5 .1.0 10 11.1 7 8 9 6 5 10 10 7
■ 10 5 7 5 9 6 9 9 5 5 7 7 9 4 3 10 9 9
9 3 5 3 6 5 6 4 '7
X- 8 7 7 10 9 7 8 11 6 7
7 6 9 8 4 8 9 2 5 9 6 7 8 4578 11 9
7 7 8 7 5 12 6 9 7 3 3 9 2 8 12 10 4 4
5 6 6 5 11 7 OK
Fig. 11.12.
148 Exploring FORTH
typed by the user) into the input buffer. WORD transfers this to the
word-buffer with a space as delimiter. NUMBER converts this into
a double-precision value, placed on the stack. Since we are dealing
only in single-precision values, DROP gets rid of the top two bytes,
which each hold only zero. This leaves the users’s answer on top-of-
stack with the computer’s answer below it.
CHECK compares the two answers. If they are equal, it displays
‘CORRECT’. If not, it states that the answer is wrong and displays
the correct answer. A carriage return follows, so that the next
problem is displayed on the line below. MULTIPLY has all these
words inside a BEGIN ... AGAIN loop, so that an endless series of
problems is presented. Figure 11.14 shows how it runs. As can be
seen, the way to end the inquisition is by pressing ESCAPE
MULTIPLY
8 TIMES 7 ~ 56
CORRECT
1 TIMES 3 = 77
WRONG , IT MAKES 3
1 TIMES 8 = 8
CORRECT
7 TIMES 7 = 77
WRONG f IT MAKES 49
3 TIMES 2 =
Escape
OK
Fig. 11.14.
To summarise
Explore more
(2) Revise FPOUT so that there is no need for the user to key a
decimal point when there are no decimal places in the number.
(3) Revise FPIN and FPOUT so that FIGS can hold a double¬
precision value, so allowing the range of acceptable floating-point
numbers to be increased.
(5) Write a word DH which lets the user type in a decimal number
and then displays it in hexadecimal.
(7) Write the words for this simple game. Use RND to place 10
aliens (user-defined characters) at randomly chosen positions on
the screen. Then steer your spacecraft (another special character)
around the screen, using four keys to direct it. When your craft ‘runs
over’ an alien, the alien disappears from the screen. The aim is to
destroy all the aliens with the least number of changes of direction
(least number of key-presses), so you need to plan your route very
carefully.
Chapter Twelve
AND and OR
This chapter deals with the way in which FORTH performs logical
operations. There are two main kinds of logical operation, one of
which we have met already and have used a lot. Flag logic, as one
might call it, requires a flag value to be left on the stack. A flag is a
value, the main purpose of which is to signal to the computer that a
condition is true or false. There are a number of words which look at
the value on top-of-stack, treating it as a flag and acting accordingly,
The flag is taken to mean ‘false’ or ‘true’ according to whether its
value is zero or not. Words which use flags in this way are:
IF
UNTIL
WHILE
?DUP
The last in the list is a version of DUP which acts only if the top-of-
stack is a true flag. In other words, if the top-of-stack is zero, then
zero is left at top-of-stack. If it is some other value, it is duplicated.
Note this word does not remove the flag from the stack, whereas the
other words do.
A flag can be placed on the stack either as the result of a
calculation or by the action of a conditional operator. The
conditional operators that we have used are:
= 0=
> 0>
< 0<
These leave 0 on the stack to indicate ‘false’ and 1 to indicate true. All
of the words mentioned above are concerned with the truth, or
otherwise, of given conditions and enable the computer to take
decisions based on logic.
Another logical word that we have used several times is NOT.
AND and OR 153
IF J = 3 AND K= 6 THEN X= X+ 1
12 10 AND .
8 OK
Obviously it is capable of operating on numbers, but the results it
produces do not conform to any ordinary arithmetical operation.
Here is OR at work:
12 10 OR .
14 OK
Once again, it is doing something with the numbers, but exactly
what it is doing is not clear. The term ‘bitwise’ gives us a clue. AND
and OR are operating on the bits (the binary digits) of the numbers,
rather than on the numbers as a whole. To follow what is happening,
we need to put the micro into binary (base-2) mode:
2 BASE !
OK
154 Exploring FORTH
Here is the same operation (10 AND 12) but keyed in and displayed
in binary:
12 is 110 0
10 is 10 10
Result 8 is 10 0 0
11.00 1.010 OR .
1110 OK
12 is 1 10 0
10 is 1 0 1 0
Result 14 is 1 1 10
Shifting
Now that we are treating bits as individuals, there are several other
operations that we can perform on them. One of these is known as
AND and OR 155
0 0 0 1 0 1 0 0
A left shift moves each bit one place to the left:
0 0 1 0 1 0 0 0
Successive left shifts give:
0 1 0 1 0 0 0 0
1 0 1 0 0 0 0 0
0 1 0 0 0 0 0 0
1 0 0 0 0 0 0 0
00000000
At each shift the left-most bit is lost, and the right-most bit becomes
a zero. The right-shift operation works in the opposite direction.
It might seem to be a complicated matter to perform a left-shift,
but in practice it is extremely easy. All that has to be done is to
multiply the binary number by 2! Use LS to demonstrate this:
J LB PUP IJ. 10 * i
10100 LS
10100 OK
LS
101000 OK
L-S
1010000 OK
LS
10100000 OK
Fig. 12.1.
Bit maps
Bit no. =0 if = 1 if
0 1-storey 2 or more storeys
1 furnished unfurnished
2 built pre-war built post-war
3 completed under construction
4 no central centrally heated
heating
5 no double- double-glazing
glazing
6 near shops no shops near
7 no garage garage
0 0 0 10 0 1 1
AND and OR 157
Identifying by logic
The example of the house agent, shows how we can use computer
logic for identification. The house buyer tells the agent which
features are most important in selecting the house. The agent then
uses the computer to search the stored information to identify which
house or houses conform to the requirements of the buyer. If the
buyer specifies only a few features, the computer may be able to find
many dwellings which will satisfy the buyer. If the buyer stipulates a
long list of essential features, the computer may find only few or
perhaps no dwellings to meet the buyer’s demands.
Here is another application of the same idea. Those who are
interested in bird life often want to identify the birds they see, but
this is not always as simple as it might be. The bird may be a long way
away or may be partly hidden by vegetation, so that all of its features
cannot be clearly seen. Or it may fly away out of sight before the
watcher has had time to notice all its features. The kind of question
the computer could answer is: ‘I saw a long-legged bird beside the
lake. It was white in parts, but the light was too poor to pick out its
other colours. What kind of bird could it be?’. The computer then
searches the stored bytes, looking for any birds that spend their time
near water, have long legs and are at least partly white. It will ignore
158 Exploring FORTH
other features and pick out all those kinds of birds which conform
to the description. It prints a list of all such birds it finds listed in its
memory.
Screen 30 (Fig. 12.2) lists nine birds. It is easy to extend the list to
cover several screens if required. The screen is being used for storing
SCR f 30 IE H
0 ( NAMES OF BIRDS
1 SPARROW y USE * M. BI..K * THROAT 64
2 ROOK: BARE FACE PATCH CF CROW 94
3 blackbird: m. yell, beak; f.bwn i.o
4 MARTINy HSE: WHT RUMP CF SWALLOW 56
5 swallow: longer TAIL THAN MARTIN 54
6 REDBREAST/ROBIN 18
7 HERONy GREY: DARK FLIGHT FTHRS 152
8 GULL y COMMON: GR-YELL BEAK & LEGS 184
9 TIT y GT: BL.K STRIPE ON BELLY 36
10
11
12
13
15
Fig. 12.2. Screen 30.
text, any line of which can be picked out and displayed. Since the
whole screen is text and there are no FORTH words or definitions,
the whole screen is enclosed in brackets. The first bracket, ‘(’, at the
start of line 0 is in fact a FORTH word, called‘bracket\ This has been
used on line 0 of all the other screens shown in this book. Since it is a
FORTH word it must be followed by a space. When the computer
encounters such a bracket, it ignores what follows until it finds a
reversed bracket,')’, which acts as a delimiter. Normally we place the
delimiting bracket at the end of line 0, so that only the title of the
screen is ignored. Here we place the reversed bracket online 15. The
main point to remember when keying in the text is that it must not
include brackets. ‘Bracket’ can be used in word-definitions too, to
hold remarks about the action of the word.
Screen 30 (Fig. 12.2) lists the birds and follows their names
with brief notes on special features. The house sparrow has
a note that the male has a black throat. The rook has a
note to the effect that it has a bare face patch which the crow does
not have ( CF stands for compare with). These notes help the user to
confirm the identification, by quoting features that help distinguish
the bird in question from other birds of similar appearance. The
numbers at the end of each line are references to pages in a bird book
AND and OR 159
BCR # 33. IF H
0 ( BIRD FEATURES
3. 3. IS LESS THAN 20 CM LONG
2 2 HAS BLACK FEATHERS
3 3 HAS WHITE FEATHERS
4 A HAS BROWN FEATHERS
5 5 HAS RED FEATHERS
6 6 SPENDS TIME ON THE GROUND
77 A WATER OR WATER-SIDE BIRD
8 8 NESTS IN TREES» NOT BUSHES
9
3. 0
3.3.
3.2
3.3
3T1
3.5 ) —>
Fig. 12.3. Screen 31.
so that the user can key in the numbers corresponding to the features
of the bird. These features are of several kinds. There are visual
features such as overall size (line 1), and feather colours (lines 2 to 5).
Certain birds frequent particular localities so the place the bird is
seen at can be a useful identifying clue (linpsTFan4 7). The habits of
the bird also can be very helpful (line 8). It is not intended that the
user shall be able to decide one way or the other about all of these
features. As explained earlier, the difficulty with bird identification
is that one is able to discover only a few features on a given occasion.
The user is to key in those features which are positively known.
Screen 31 (Fig. 12.3) covers only eight features, enough for storing
in a single byte. It is easy to add many more features, adapting the
application to deal with two, three or more bytes for each bird.
(3) The computer builds up a byte, called BYTE, in which the bits
indicate those features keyed in by the user.
160 Exploring FORTH
(4) The computer already holds in its memory a set of bytes, one for
each bird, in which the bits indicate the features possessed by each
bird. It searches these bytes to find those which have bits in common
with BYTE. For any it finds it displays the name of the
corresponding bird.
Screen 32 (Fig. 12.4) holds all the FORTH words for identifying
birds, using the word BIRDS.
SCR # 32 20 H
0 ( IDENTIFYING BIRDS )
1 CREATE FEATS 175 Cr 162 C, 42 C,
2 7 Cr 23 Cv 61 Cr 198 Cr 102 Cr
3 135 C*
4 VARIABLE BYTE
5 t BITS 0 DO 2* LOOP 2/
6 BYTE (? OR BYTE ! i
7 ! FEATURES 0 BYTE ! BEGIN QUERY 32
8 WORD NUMBER DROP DUP 0> WHILE
9 1 SWAP BITS REPEAT »
10 J GET BYTE 090 DO DUP DUP I FEATS
11 + CO AND = IF I 1+ 30 * LINE CR
12 THEN LOOP DROP J
13 t BIRDS 12 >VDt.J 9 1 DO I 31 «LINE
14 CR LOOP CR .“ KEY THE NUMBERS!*
15 CR FEATURES CR .* IT COULD BE!" CR GET r
Fig. 12.4. Screen 32.
The first thing to be done is to store the data about the birds in
memory. Before writing the application we set out a table like this:
Nests in
Bird trees Wat. Grnd. Red Brn. Wht. Blk. <20 Decimal
Sparrow 1 0 1 0 1 1 1 1 175
Rook 1 0 1 0 0 0 1 0 162
Blkbird 0 0 1 0 1 0 1 0 42
H Martin 0 M> 0 0 0 1 1 1 7
Swallow 0 0 0 1 0 1 1 1 23
Redbrst 0 0 1 1 1 1 0 1 61
Heron 1 1 0 0 0 1 1 0 198
C Gull 0 1 1 0 0 1 1 0 102
Gt Tit 1 0 0 0 0 1 1 1 135
The table shows a 1 if the bird possesses the feature given at the head
of the column and a 0 if it does not. Note that the female blackbird is
brown, as allowed for in the table. The column headings are in the
reverse order to the listing of features in Screen 31 (Fig. 12.3) for
reasons which will be apparent later. Each row of the table shows the
AND and OR 161
composition of the byte which holds the features of each bird. The
right-hand column gives the equivalent decimal value of that byte.
Now we are ready to put this information into memory.
Line 1 of Screen 32 (Fig. 12.4) shows a new way of using
CREATE. This time it is not being used with DOES> to define a
defining word. Instead it is being used simply to create the head of a
word in the dictionary. The word is FEATS. Its code field contains
the address of a routine to put the address of its parameter field on
the stack every time FEATS is used. The parameter field of FEATS
is filled immediately by using 'C-comma'. This word stores a byte at the
next available address in the dictionary. In other words, it places it in
the first free byte of the parameter field of FEATS. The values, which
you will recognise as the values calculated from the table above are
stored in succession ontheparameterfield of FEATS. Thisisasimple
way of providing an array filled with data.
The variable BYTE is defined next, to hold the details of the bird
as the user keys them in.
BITS is the word which puts the bits into BYTES. It requires at
second-on-stack the feature number (1 to 8) as keyed in by the user.
It also requires 1 at top-of-stack. It goes through a loop from 0 to the
feature number, multiplying by 2 each time. We use the word ‘2-times’,
a special \gord for fast multiplication by 2. This gives a shift-left
operation, as described in the previous section. For example, if the
user keys in 4 (brown feathers), BITS shifts 1 four times:
Start 0 0 0 0 0 0 0 1
1st shift 0 0 0 0 0 0 1 0
2nd shift 0 0 0 0 0 1 0 0
3rd shift 0 0 0 0 1 0 0 0
4th shift 0 0 0 1 0 0 0 0
This has taken the 1 too far. The ‘2-divide’ after the loop shifts it one
space to the right again:
Right-shift 0 0 0 0 1 0 0 0
The reason for this is that a DO ... LOOP must always execute at
least once. So the 1 is always shifted at least once, to position 2. The
‘2-divide’ is to compensate for this. This again is a special fast-action
word. The resulting byte is then combined with the value already in
BYTE, using OR. The reason for this is that the user will probably
key in several numbers and the corresponding bits are to be stored in
BYTE one at a time. For example, if the user has already keyed in 2
and 6, BYTES will hold:
162 Exploring FORTH
0 0 1 0 0 0 1 0
(bits numbered from right to left, 1 to 8). Now the user keys in 4 as
already described. BITS has produced:
0 0 0 0 1 0 0 0
BYTES holds 0 0 1 0 0 0 1 0
OR these two 0 0 10 10 10
The new bit, positioned by BITS, has taken its place among those
already present in BYTES. The bits already there are not affected by
this operation.
FEATURES is the word which accepts the numbers typed in by
the user. It starts by putting zero into BYTES, clearing it ready for
storing the bits. Then a BEGIN ... WHILE ... REPEAT loop
accepts numbers, one at a time. The routine used was described in
Chapter Eleven. The result is to put the number on top-of-stack.
This is duplicated to keep a copy for later action. The top copy is
tested to see if it is greater than zero. If so, action continues. If the
user has keyed 0, it is taken to indicate that there are no more
features to be typed in and FEATURES ends. The action of
FEATURES in response to a non-zero number is to put 1 on the
stack and swap it with the number. It then calls BITS to position the
bit and insert it in BYTE.
GET is the word which searches FEATS comparing each byte
there with the value in BYTE. Its stack action is:
GET
BYTE @ BYTE
9 0 DO BYTE
DUP DUP BYTE BYTE BYTE
I FEATS BYTE BYTE BYTE I
+ BYTE BYTE BYTE addr
C @ BYTE BYTE BYTE byte
AND BYTE BYTE BYTE, byte
BYTE flag
11 BYTE
I 1+ BYTE 1+1
30 BYTE 1+1 30
. LINE CR BYTE
THEN BYTE
LOOP BYTE
DROP
AND and OR 163
blackbird byte is 0 0 1 0 1 0 1 0
BYTE might be 0 0 0 110 1 0
AND gives 0 0 0 0 10 1 0
This value is not the same as BYTE. The bird described by BYTE has
red feathers, so is not a blackbird. In another case the user might
have seen a brown bird on the ground. The comparison would be:
blackbird byte 0 0 10 10 10
BYTE would be 0 0 1 0 10 0 0
AND gives 0 0 10 10 0 0
Now the value obtained by ANDing is equal to the value of BYTE.
Although the user did not key in all the features of blackbird (i.e. the
bird seen did not have black feathers since it was a female) all of the
features keyed in are possessed by blackbirds. The bird might be a
blackbird! The text relating to blackbirds is displayed. Looking at the
table given earlier, it can be seen that the same result would be
obtained with sparrow and with redbreast. All three names would be
displayed.
If the ANDed value equals BYTE the IF action is executed. A line
from Screen 30 (Fig. 12.2) is printed. The word ‘dot-line’ does this. It
requires the stack to have the number of the Screen at top-of-stack
and the line required at second-on-stack. The line required is 1+1,
since the screen lines are numbered from 1, while I is being
incremented from zero.
The whole application is put together in the word BIRDS. ‘12
>VDU’ clears the screen. Use the equivalent words in your version of
FORTH. The first loop displays all eight lines of Screen 31 (Fig.
12.3), so displaying a numbered list of features. The user is then asked
to key in the numbers. FEATURE accepts and processes these. The
bits are gradually assembled in BYTE. FEATURES ends when zero
is keyed. The computer then displays ‘IT COULD BE:’ followed by a
list of birds conforming to the description, as obtained by GET.
164 Exploring FORTH
BIRDS
1 IS LESS THAN 20 CM LONG
2 HAS BLACK FEATHERS
3 HAS WHITE FEATHERS
4 HAS BROWN FEATHERS
5 HAS RED FEATHERS
6 SPENDS TIME ON THE GROUND
7 A WATER OR WATER-SIDE BIRD
8 NESTS IN TREESt NOT BUSHES
KEY THE NUMBERS:
4
8
0
IT COULD be:
SPARROW» USE: M. BLK♦ THROAT 64
OK
BIRDS
1 IS LESS THAN 20 CM LONG
2 HAS BLACK FEATHER'S
3 HAS WHITE FEATHERS
4 HAS BROWN FEATHERS
5 HAS RED FEATHERS
6 SPENDS TIME ON THE GROUND
7 A WATER OR WATER-SIDE BIRD
8 NESTS IN TREES > NOT BUSHES
KEY THE NUMBERS:
1
2
0
IT COULD be:
SPARROW» HSE: M. BLK. THROAT 64
MARTIN > HSE! WHT RUMP CF SWALLOW 56
SWALLOW: LONGER TAIL THAN MARTIN 54
TIT r G r: BLK STRIPE ON BELLY 36
OK
Fig. 12.5.
AND and OR 165
To summarise
• OR and AND are used for bit mapping and other logical
operations.
• Bit-maps enable compact storage of data.
• Bit-maps are useful as the basis of identification applications.
Explore more
.S Displays the stack without altering it. This is the most difficult
one to provide for in general terms, for it depends on the exact
addresses used for the stack and for holding stack vectors in your
system. Your handbook may suggest a definition if it is not already
provided. The word in Fig. A.l should work with most FORTHs.
Before defining this version of‘dot-S’, define an array VALS to hold
as many values as you are ever likely to want displayed (see Chapter
Six). Then define dot-S’ as above. To use the word you must first
place on the stack the number of values that you want to be
displayed. The word takes the values off the stack in order, starting
168 Exploring FORTH
BASE Some FORTHs use only a single byte for BASE. If this is
so, tho way to change the base is to use ‘C-store’, as in this example in
which the computer is being put into binary mode:
2 BASF- C!
l HEX 16 BASE C! i
* hex 16 BASE ! f
KEY This waits for a key to be pressed, then places its ASCII Code
on the stack. ?KEY is similar but waits for a limited period of time.
The length of time it waits depends on the value of a number placed
on the stack immediately before ?KEY is used. Some FORTHs have
only INKEY, which accepts input from the keyboard, but does not
wait. INKEY can be used in a definition of KEY:
NOT If this is not in your FORTH, use 0=, which has exactly the
same action.
0 VARIABLE RATE
Appendix B
ASCII Codes
GO, 91 VQUERY, 60
greater-than, 84 query-DUP, 48
query-KEY. 60
HALT, 93 QUICK. 130
PD, 144 QUICKSORT. 130
vHERE, 66
n/HEX, 143, 169 - R-colon, 81, 83
HITUFO, 96 REACTION, 110
HITYOU, 98 READ, 130
HOPS, 57 READY, 54, 56
RESET, 110
I, 35, 102 RIGHT, 91, 130
s. IF, 79 RISE, 126
INKEY. 169 RND, 146
INTEG, 140 ROLL, 48
vfeOT, 48
J. 105 R-semicolon, 81, 83
RUN, 51
VKEY, 59,60. 93. 169 RUNSLOW, 52
KEYX. 96
KEYZ, 96 SAME, 52
semi-colon. 20
LEAP, 56 SESSION, 108
-/LEAVE. 107 SET. 117
LEFT, 91, 130 SHOW, 67, 68,75. 148
)ess-than, 85 SHOWVAL, 117
v'TLOOP, 68. 103 SHTR, 54
LS, 155 SLASH, 137
SORT, 130
S-P-fetch, 25
minus, 17
SQUARES, 106
MOVEIT, 51, 53
STEP, 54
■^M-times, 135
store, 37
MUFO,93
STORPLACE, 47, 53
MULTIPLY, 148
.STR, 73
MYOU, 96
STR!, 72, 73
STRVAR, 71, 73
NAME?, 73, 75, 85 J SWAP, 32, 48
NO?, 113, 114 SWEEP, 121
NOS?. 114
NOT, 87, 152, 169 TEST, 111
VNUMBER, 149 n/THEN, 79
tick, 68
OFF, 55 times, 30
one-plus, 47 times-divide, 31
'/.OR, 153 TRIM, 84
- OVER. 47, 48 TRUE?, 80
TURN1, 74
■ PAIR, 148 two-divide, 161
PICK, 47, 48 two-drop, 117, 119, 134
PLACEIT, 46, 53, 169 two-dup, 134
PLAY, 93 two-over, 134
plus, 93 two-swap, 134
PUT, 130 two-times, 161
174 Index of FOR TH Words
The Author
Owen Bishop is a freelance technical writer and
programmer. He is the author of forty books including a
number on popular computing. He is a well known and
regular contributor to computing journals.
INTRODUCING LOGO
Boris Allan
0 246 12323 0
GRANADA PUBLISHING
Printed in Great Britain 0 24612188-2 £6.95 net