Skip to main content

Had I been present at the creation,
I  would have given some useful hints
for the better ordering  of the universe.
             Alfonso the Wise


                           STRENGTH IN FORTH

                                part ten

                          A CREATIVE LANGUAGE

When I started writing this part of the FORTH-course of ST  NEWS
I didn't know yet, how the first issue of this magazine under the 
wise ,  skillful and creative leadership of the new chief-editor, 
would look like.  I don't know Stefan Posthuma at all.  The  only 
two facts of his complexity I know of are his name and his black-
and-white drawingprogram Artist. So I could write 'wise, creative 
and  skillful'.  A skillful and creative programmer he  certainly 
is,  and  wisdom he laid in the fact that he chose to make  it  a 
P.D.- program.  I recognise these characteristics as those of  an 
artist,  as I am kind of an artist myself.  I do some drawing and 
painting now and then.  Quite well I might say, and if Rembrandt, 
Van  Gogh and some other guys hadn't stolen most of  my  creative 
and  brilliant ideas,  I would have been rather  famous all  over 
this  beautiful  world  now.   In  writing  this  course  and  in 
programming my computer in FORTH, I've found  a worthy substitute
for that  lost empire.  Now let's get serious  and see what  made 
me saying, that FORTH is a creative language.

IN THE BEGINNING...

Well,  it was in part nine that I promised you to tell  you  more 
about  a particular pair of words:  {CREATE...DOES>}.  The  first 
part  of this mighty combination we have used many times  without 
much explanation; the second part is going to be a new element in 
your  ever expanding knowledge of FORTH.  One aspect of FORTH  is 
its  extensibility.  In defining new words you take advantage  of 
this  feature.  Every new word you will enter in  the  dictionary 
will  extend FORTH's capabilities.  The creation of new words  is 
made possible by FORTH's compiler.  In generating code for a  new 
entry  in the dictionary,  the compiler always decides  to  which 
type  this  entry  will belong to.  This  decision  is  made   by 
looking  at  the defining word used.  The  most  frequently  used 
defining word in FORTH is {:}. It produces a new dictionary-entry 
with  - in its code-field - a pointer to some code to handle  the 
addresses and data compiled in its parameterfield. {VARIABLE} and 
{CONSTANT}  are two more defining words,  each  with  substantual 
different code-field pointers.  So {:}, {VARIABLE} and {CONSTANT} 
will create different types of entries. This compiler now can  be 
extended !! By you, the user. Extending the compiler means adding 
new  defining words.  And for that purpose {CREATE...DOES>}  will 
perform the right action.  To avoid total distress,  let's  begin 
our explanation of what is going on with {CREATE...DOES>} by  the 
very beginning of what you should already know......{CREATE}.

....THERE WAS DISTRESS

I've  let  you use {CREATE} many  times.  Remember  that  chapter 
dealing with strings. We then took {CREATE} to set aside a region 
of memory for use as a buffer,  so we could store a string in it. 
{CREATE}  is a word that will produce a  dictionary  head,  whose 
code  field  contains  the address of the machine  code  used  by 
{VARIABLE}.  I must tell you,  that this code leaves on the stack 
the  startaddress of the parameter area,  when a dictionary  head 
formed by {CREATE} is later executed. Unlike {VARIABLE}, however, 
no parameter space is allocated. So if we

{CREATE}  MBUF OK

the  dictionary  entry  will appear as  shown  in  the  following 
diagram:
                |¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|
                |      MBUF       | <-- Name field                 
                |_________________|
                |   Address of    |
                |  previous word  | <-- Link field
                |_________________|
  |¯¯¯¯¯¯¯¯|    |   Code field    |
  |Code of |<-- |     pointer     | <-- Code field
  |VARIABLE|    |_________________|
  |________|    |    | Free |     |
                |    |      |     | <-- Parameter field
                     V      V

Executing {MBUF} will leave the address of the first free byte in 
the  dictionary,  assuming  that {MBUF} is the last word  in  the 
dictionary.  In  this case it is the same address as returned  by 
{HERE}.  We  could now {ALLOT} some space in the  dictionary  for 
storing some values in {MBUF}'s parameter area.  Thus  if,  after 
creating {MBUF},  we then type 8 {ALLOT} we shall reserve 8 bytes 
of memory.  And whenever we'll execute {MBUF}, the address of the 
first of these bytes will be left on the stack.  To store a byte-
value in the first byte, type

134 {MBUF} {C!} OK

To  store a value into the second byte,  you'll have to  increase 
the address of {MBUF} with one

231 {MBUF} {1+} {C!} OK

Fetching  a  value from a byte is easily done with  the  help  of 
{C@}. Some difficulties may arise, if you want to store and fetch 
16-  or 32-bit values in the parameter area of {MBUF}.  To  avoid 
these difficulties you should add {EVEN}.

{CREATE}  MBUF {EVEN} 8 {ALLOT} OK

This will ensure that it all begins at an even address.  To store 
a 16-bit value, you have to use {W!} and to fetch {W@}. The words 
{!}  and {@} will accomplish the fetching and storing for  32-bit 
values.  It  is best,  if you always treat these three  pairs  of 
words in combination.  You may {C@} a value that you stored  with 
{W!},  but  you  never can tell what the result  may  be;  that's 
unpredictable.  If you will respect the combined  use of the word 
pairs  for storing and fetching,  you can use this method  for  a 
word  that  can  serve as some  kind  of  variable.  That  means, 
however,  a  lot  of typing.  Things should be  made  easier  and 
simpler.  Some parts ago I gave you some alternative  definitions 
of {VARIABLE}. Here is another one in terms of {CREATE}.

: 2VAR CREATE  ,  , ;

Now, what are the main differences between these two sequences of 
code ( MBUF and 2VAR) in both of which appears {CREATE} ?

1. In the first sequence {CREATE} acts outside a colondefinition,    
   in the second one it appears inside a colondefinition.
2. In  {2VAR} the  dictionary-entry for  {2VAR}  is not  made  by    
   {CREATE}, but by {:}.
3. The  action of the {CREATE}-word is executed in the definition 
   of {MBUF},  but it is compiled in {2VAR}. The action, thus, is 
   postponed  till the moment  {2VAR} is  executed.
4. It is when    {CREATE}  executes,  that it excepts the name of 
   the dictionary-entry it  is to make.  This means that the name
   should not be included in the colondefinition using  {CREATE}. 
   This name is required  when executing {2VAR};  it is then used
   to create  a new  entry in the dictionary by the compiled code
   of  {CREATE}.

The use of {2VAR} is 

123 234 {2VAR} DISTRESS OK

With {2VAR} we created an entry for {DISTRESS} and twice {,}  put 
two values into the parameter field of {DISTRESS}.

890 789 {DISTRESS} {2!} OK

The  above  line  of code serves  to  put two new values  in  the 
parameter  field  of {DISTRESS}.  The word {2@} fetches  the  two 
values  and pushes them onto the stack and twice {.} writes  them 
at your display in the following line

{DISTRESS} {2@} {.} {.} 890 789 OK

BE BORN TIRED

Now we have made two steps forward.  The first one was  analysing 
the history of {CREATE} outside a colondefinition. The second one 
was,  when  we looked at {CREATE} inside a colondefinition as  in 
{2VAR}.  So far the most important thing to remember is  that all 
examples  have used the fact that a word defined  using  {CREATE} 
will  leave the startaddress of the parameter area on the  stack, 
irrespective if defined outside or inside a colondefinition. What
is   secondly most important now is the fact,  that a name for  a 
{2VAR} doesn't need to be embedded in the  definition  of  {2VAR} 
itself.  So you can use any name you like each time you  executes 
{2VAR} and that as many times as you like.  In other words:  with 
{2VAR}  you  can  DEFINE  as many words of  the  type  {2VAR}  as 
necessary.  Now  let's  make the  third  step.  Your  programming 
passion  (and  laziness) will urge for the possibility  to  avoid 
additional  entering  of words,  to place on the  stack  the  two 
values stored in {DISTRESS}. If it would please you that entering
{DISTRESS}   would   place  these  two  values   on   the   stack 
automatically,  then we have to create a new type of word. A word 
with  some  quite  welcome action.  A word that  will  really  dó 
something. In fact that does it to me:

: 2VARI CREATE  ,  , DOES> 2@ ; OK

The  difference with {2VAR} is obvious {DOES> 2@}.  This part  is 
not  ment  to do anything when you are going to  use  {2VARI}  to 
DEFINE a new type of word. You may use {2VARI} as follows

123 345 {2VARI} LAZY OK     (a)

{LAZY} {.} {.} 123 345 OK   (b)

In  line (a) we have defined a {2VARI}-word {LAZY}.  It  is  done 
essentially  in  the same way as {DISTRESS} with  {2VAR}.  As  if 
there were no {DOES> 2@}-part, there isn't any additional action, 
at least not perceptable with one's eye. But things are different 
in line (b). To put on the stack the two values  in {DISTRESS} we 
had  to {2@} them and twice {.} made them visable at the  screen. 
To  put  on the stack the two values  in {LAZY},  it  suffies  to 
execute  {LAZY}.  On execution the {DOES>}-part of {2VARI}  makes 
itself useful.  It puts the two values on the stack, using  {2@}. 
Twice  {.}  remains necessary to display the  values.  We  didn't 
include {.} in the {DOES>}-part of {2VARI}. Perhaps it occured to 
you,  that  any  word defined with {2VARI} will  demonstrate  the 
above features.  And further,  did you notice, that such {2VARI}-
words  perform  identically as words defined with  {2CONSTANT}  ? 
What we were doing all the time,  was - step by step - defining a 
high level substitute for {2CONSTANT} !! But, you will ask me, is 
it possible to include twice {.} in the {DOES>}-part ?  Yes, that 
is possible ! You are lazier than I thought you were ! 

: 2VARI CREATE , , DOES> 2@ . . ;

All  words defined with this variation of {2VARI} will  have  the 
property to display their 2 values, when they are executed.

CREATING NONSENSE

Here is another example of {CREATE...DOES>}

: 2VARI CREATE , , DOES> 2@ DO 42 EMIT LOOP ;

Before you are going to use this type of {2VARI},  try to imagine 
what {2VARI} will work out in the {DOES>}-part. And of course the 
number  and the order of the parameters,  which are needed at the 
moment  of defining a {2VARI}-word.  The parameters are going  to 
serve as the loop index and limit. Here you are

20 0 {2VARI} STARDUST OK

Enter

{STARDUST} ******************** OK

Is this the result you expected from the code above ?  If so, you 
understand  the  power of {CREATE...DOES>}.  Try to  unravel  the 
following:

: 2VARI CREATE , , DOES> 2@
        CREATE ,   DOES> @ 1+ DO CR I SPACES 65 MIN DUP EMIT
                              LOOP DROP ;

It is a rather complicated defining word {2VARI},  which in  turn 
will  create  another defining word,  which at last  will  define 
words  to emit and skew characters with at least ASCII-value  65. 
Its use is as follows:

6 0 {2VARI} LOOPDEF OK

Now  {LOOPDEF}  has become a defining word  with  the  additional 
action of putting two values on the stack,  which can be used  by 
all words defined by {LOOPDEF}.

67 {LOOPDEF} ASCDEF OK

{ASCDEF}
 C
  C
   C
    C
     C OK


Another one

10 0 {2VARI} 10LOOP OK           69 {10LOOP} SKEWEE OK

SKEWEE
 E
  E
   E
    E
     E
      E
       E
        E
         E
          E OK


This,  of  course,  is  all nonsense.  But  it  demonstrates  the 
possibility to create hierarchical trees of defining words, as it 
is allowed to sort of nest the {CREATE...DOES>} couple.  This can 
be seen in the latest version of the {2VARI}-word.

STRINGS AGAIN

Let's go on and step back to part nine.  There we all fought some 
frightful  wars  with  strings,  which  we  won  of  course.  One 
complaint,  alas,  lasted nevertheless. We stayed without an easy 
method for creating strings.  This easy method can be provided by 
{CREATE...DOES>} in the following way:

: STRING CREATE 253 OVER < ABORT" String out of range !"
                HERE SWAP DUP 2+ EVEN ALLOT SWAP C!
         DOES>  1+ ;

The {DUP 2+ EVEN} sequence serves to avoid an odd address as  the 
start  of the parameter area.  In the examples above we  were  so 
heavily concentrating on extending the {DOES>}-part, that you may 
have  thought  that extending the {CREATE}-part  was  limited  to 
twice  {,}.  As  you can see now,  extending the latter  is  just 
depending on the function of the word.

128 {STRING} MBUF OK

And here you have your empty string of 128 bytes.  As you can see 
from  the  code this length is stored in the first  byte  of  the 
string.  It  may  be used to  determine  string-overflow.  If  in 
defining  a string the memory allocated for exceeds 253 bytes  an 
error-message  will  be given and the  system   will  abort.  The 
{DOES>}-part  puts  on  the stack the  start-address  +  1,  thus 
jumping over the string length byte. To store a string in {MBUF}, 
the following word will do

: STRING"   ( Stringaddr - )
         34 [COMPILE] WORD SWAP 2DUP 1- C@ SWAP C@ DUP
         ROT > ABORT" Inputstream too long !" 1+ CMOVE ;

It goes like 

{MBUF} {STRING"} NO APARTHEID !" OK

As  a delimiter {"} is used - ASCII 34.  Just  forget  [COMPILE]. 
I'll  explain that word and some family-members at a later  date. 
The  stringword  {STRING"}  checks if there is  space  enough  at 
{MBUF} to insert the string.  Defining a word that will print the 
string at {MBUF} at the screen, is all up to you. It can be  done  
very simple,  don't look too high for the solution.  

HURRAY, HURRAY, ARRAY, ARRAY

In  the  above section we didn't mention,  that the  sequence  of 
{CREATE  MBUF  8 ALLOT} was a simple form for  defining  a  byte-
array.  But it is one ,  although a rather simple one !  I  adore 
simple facts (They are very relaxing too, see !), but simple  and  
elegant   should   unite.   The  elegance  is  put   there   with 
{CREATE...DOES>}. Here is a slightly more elegant solution:

: BARRAY CREATE ALLOT DOES>  + ;

A  twelf element array for single-byte variables is  made by

12 {BARRAY} ELEGANT OK

The  elements of the {ELEGANT}-array are NOT set to some  initial 
value.  To store a value at the fifth element the following  code 
will do the job:

100 5 {ELEGANT} {C!} OK

To fetch the contents of the fifth element simply enter

5 {ELEGANT} {C@} 100 OK

For me it's easy to tell how the storing and fetching were  done, 
but  as I might hope,  for you too !  The next step could  be  to 
create  an  array to hold 2-byte values,  or one to  hold  4-byte 
values. I'll give the 2-byte version:

: 2BARRAY CREATE 2* ALLOT DOES> 2* + ;

{2BARRAY} is to be treated in the same way as {BARRAY}.  

12 {2BARRAY} ELEPHANT OK

And storing a value in {ELEPHANT} and fetching it successivily

31567 10 {ELEPHANT} {W!} OK             10 {ELEPHANT} {W@} OK

The  tiny word {W!} is one of the many words that - as  you  will 
discover  - live in your system and that are not standard  words. 
Some FORTH-systems have been equiped with a 32-bit stack. In such 
a  system  operators aligned for 32-bit values are  so  to  speak 
'standard'.  Perhaps your system is a 32-bit FORTH,  perhaps not. 
But my favourite system has a 32-bit stack. In this FORTH the {!} 
operator for instance  handles values up to 32-bits.  Sixteen-bit
values  are possible,  as are byte-values.  These explicit 16-bit
values  are  handled  with special  operators.  Their  names  are 
derived   from  the operators {!},  {,} and {@} by means  of  the 
prefix  W,  as are the byte-handlers with C.  So you'll  have  to 
detect what system you are living in and what adaptions there are 
to make.  As I pointed out before,  it is absolutely necessary to 
take good care to choose for the right combination of operators.
As you might have already noticed,  there isn't any check made in 
{2BARRAY} on the range of the index.  It could be possible to use 
an  out-of-range  index  and so to  overwrite  other  dictionary-
entries.  So here is an alternative definition,  which will check 
the range,  give an error-message if needed and that is a  4-byte 
array defining word.

: 4BARRAY CREATE DUP , 1+ 4* ALLOT 
          DOES> 2DUP @ OVER < SWAP 0< OR 
                IF @ CR ." Range-error - max index = " . 
                   ." ; used index = " . QUIT 
                THEN 4 + SWAP 4* + ;

The use is similar to that of {2BARRAY}, except of course for the 
operators to use. The extensive error-checking and reporting will 
slow  down  execution speed.  (See part  nine on the  subject  of 
error-checking). 
It is very easy to develop a two-dimensional-array defining word. 
In the following definition the elements of the array are 32-bits 
variables and the array contents are not initialised. 

: 2DIMARRAY CREATE DUP , * 4* ALLOT
            DOES> ROT OVER @ * ROT + 4* + 4 + ;

With

10 6 {2DIMARRAY} ELASTIC OK

you  have  created a 10 by 6 array called  {ELASTIC}.  The  array 
indices may range from 0,0 to 9,5 inclusive. The value 1234567 is 
stored in the 3,4 element with

1234567 3 4 {ELASTIC} {!} OK

Sometimes it may be necessary to create a  data-structure,  known 
as  'table'.  Such  a structure has only to  leave  the  starting 
address of the data:

: BYTABLE CREATE ALLOT DOES> ;

And that's all !!

23 {BYTABLE} LIST OK

will create the table {LIST} with room for 23  byte-values.  When 
{LIST}  is  executed it will leave the starting  address  of  the 
data. To reach the fifth byte, enter

{LIST} 5 + OK

From the early examples it should be clear to you,  how to  store 
and  fetch  values into and from the  table.  Another  method  of 
creating a table to store values is the following. It only can be 
used if you know the values at the time of creating the table and 
if they are not likely to be changed anymore.

 CREATE SINUS
    0 ,  175 ,  349 ,  523 ,  698 ,  872 , 1045 , 1219 , 1392 ,
 1564 , 1736 , 1908 , 2079 , 2250 , 2419 , 2588 , 2756 , 2924 ,
 3090 , 3256 , 3420 , 3584 , 3746 , 3907 , 4067 , 4226 , 4384 ,
 4540 , 4695 , 4848 , 5000 , 5150 , 5299 , 5446 , 5592 , 5736 ,
 5878 , 6018 , 6157 , 6293 , 6428 , 6561 , 6691 , 6820 , 6947 ,
 7071 , 7193 , 7314 , 7431 , 7547 , 7660 , 7771 , 7880 , 7986 ,
 8090 , 8192 , 8290 , 8387 , 8480 , 8572 , 8660 , 8746 , 8829 ,
 8910 , 8988 , 9063 , 9135 , 9205 , 9272 , 9336 , 9397 , 9455 ,
 9511 , 9563 , 9613 , 9659 , 9703 , 9744 , 9781 , 9816 , 9848 ,
 9877 , 9903 , 9925 , 9945 , 9962 , 9976 , 9986 , 9994 , 9998 ,
10000 , 

This  table reflects the values you'll need,  if you're going  to 
develop  an application to draw circles using  only  integers.  I 
shall  use  this table in a part of this course  still  to  come. 
Although,  if you only knew some of the basic principles of sinus 
and   cosinus,   you  would  be  able  to  do  the   mathematical 
calculations yourself.  If you would like to try,  remember  that 
you  are  drawing at a screen.  So you'll have to deal  with  the 
coordinates   of  the  screen;   the  solution  is  not  only   a 
mathematical one.

AN AVERAGE INTELLECTUAL

A  far  more  advanced  feature  of  arrays  -  rather  than   of 
{CREATE...DOES>} - is the phenomenon of the string-arrays and  of 
course  FORTH  is  quite capable  of  implementing  such  complex 
structures well. Perhaps I'll discuss string-arrays at some later 
date,   as now it  will  not contribute to  the  elucidation   of 
{CREATE...DOES>}.  For now I'll give you some intelligent  array-
creature,  one that will always be able to tell you the exact sum 
and average of all its elements. 

: IQARRAY CREATE DUP , 0 , 0 , 0 DO 0 , 
                                 LOOP
          DOES>  DUP DUP @ SWAP 12 + OVER 0 SWAP 
                               0 DO OVER @ + SWAP 4 + SWAP
                                 LOOP
                 SWAP DROP DUP >R SWAP / OVER 4 + ! R>
                 OVER 8 + ! 4 + SWAP 4* + ;

First comes the use of this remarkable array:

20 {IQARRAY} EINSTEIN OK

It  is  not  allowed to store values in the  0-th  and  the  1-st 
element,  because  they are going to be used to hold of  all  the 
elements  the  average and sum respectivily.  Be nice  and  start 
storing  values  not lower than in the fourth  element.  (As  the 
array starts counting at zero,  the fourth element is found  with 
3 {EINSTEIN} ).

2000 3 {EINSTEIN} {!} OK

If you now

0 {EINSTEIN} {?} 100 OK  and then   1 {EINSTEIN} {?} 2000 OK

and that's quite intelligent !

3000 8 {EINSTEIN} {!} OK             0 {EINSTEIN} {?} 250 OK

1 {EINSTEIN} {?} 5000 OK

In  the {CREATE}-part of the definition the initialising  of  the 
array  is  done  by setting the first element to  the  number  of 
elements  the  user wants the array to hold,  and  the  nexr  two 
elements  to zero.  The latter are going to hold the average  and 
the sum.  The need for these three elements is independent of the 
-  varying - length of the array.  After that space is  allocated 
and set to zero;  notice,  that if 20 elements are allocated, the 
array  will count three more elements.  The {DOES>}-part  in  the 
first  place  supplies  the address of the  start  of  the  array 
several times. Then fetches the number of array-elements and sets 
off  to  the 'real' start of the array;  the start of  the  'user 
area',  say.( 12 + !!). With {OVER 0 SWAP} the parameters are set 
for the coming {DO...LOOP}:  (1) start user area ,  (2) 0 ,   (3) 
number of elements ,  (4) 0 . Inside the {DO...LOOP} the contents 
of the succesive elements are fetched and summed.  After the loop 
has  completed  all  parameters are set in  the  right  order  to 
calculate  the  average  of all the elements and  to  store  this 
result and the result of the summation at their right place.  The 
sequence  {4  + SWAP 4* +} calculates the address  at  which  the 
value that has been input last has to be stored. This means, that 
at the moment this value has been stored, the average and sum are
not in accordance with the average and sum of the current  values 
stored  in the array.  It is when you enter 0 (or  1)  {EINSTEIN} 
{?},  that the average or  sum are updated.  I must admit, that I 
need more days to create {EINSTEIN},  than God did for this whole 
universe. Perhaps I should have asked His advice ?

                                           Halleluiah !!!

SUMMARY

In  the  following  table  I  have  brought  together  all  words 
concerned with storing and fetching values.

|¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|
|    WORD     |    STACKNOTATION      |       DESCRIPTION       |
|_____________|_______________________|_________________________|
|     C!      |   ( b addr ---  )     |Stores  byte-value b at  |
|             |                       |address addr.            |
|     C@      |   ( addr --- b  )     |Fetches byte-value b at  |
|             |                       |address addr.            |
|     C,      |   ( b  ---      )     |Compiles byte-value b  in| 
|             |                       |the first free byte of   |
|             |                       |dictionary.              |
|     W!      |   ( w addr ---  )     |Stores word-value w at   |
|             |                       |address addr.            |
|     W@      |   ( addr --- w  )     |Fetches word-value w at  |
|             |                       |address addr.            |
|     W,      |   ( w  ---      )     |Compiles word-value w in |
|             |                       |the first free word in   |
|             |                       |dictionary.              |
|     !       |   ( n addr ---  )     |Stores n at address addr |
|     @       |   ( addr --- n  )     |Fetches n from address   |
|             |                       |addr.                    |
|     ,       |   ( n   ---     )     |Compiles n in the first  |
|             |                       |free space in the dictio-|
|             |                       |nary.                    |
|    2!       |   ( n1 n2 addr --- )  |Stores n2 at address addr|
|             |                       |and n1 at addr+4.        |
|    2@       |   ( addr --- n1 n2 )  |Fetches n1 from address  |
|             |                       |addr and n2 from addr+4. |
|   ;CODE     | Used as               |Same as DOES> except for |
|             |: nnnn CREATE (words)  |the assemblercode to fol-|
|             |  ;CODE (assemblercode)|low.                     |
|             |;C                     |                         |
|   ;C        | Use see ;CODE         |Terminates assemblercode |
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
EXTRAS

Another  possible  form of {CREATE...DOES>} implies  the  use  of 
assemblercode  in the {DOES>}-part.  That  is,  the  {DOES>}-word 
itself is omitted and replaced with {;CODE}. The assemblercode is 
terminated  with {;C},  which is the machinecode  counterpart  of 
{;}.  As  I have told you in the very beginning of this course  I 
don't   know  very  much  (=  nothing)   about   68000-assembler. 
Nevertheless, here is an example:

: SOMETHING CREATE ,
            ;CODE SP )+ A0    LMOVE 
                  A0 )  D0    LMOVE
                  D0    SP -) LMOVE C;

I  don't  know  if  you have  any  working  knowledge  of  68000-
assembler,  but  if so,  you will probably have noticed that  the 
syntax of a  FORTH-style assembler is a reversed one.  The use of 
{SOMETHING} is

12345 {SOMETHING} BEAUTIFUL OK

Executing  {BEAUTIFUL}  puts  12345  on  top  of  the  stack. The 
{CONSTANT}-word  will do exactly the same !!  The code above  has 
been proven suitable for my 32-bit FORTH.  I don't know how  your 
FORTH will react when he gets this code 'fot bread and butter'.
I'll try to explain the code.  The {CREATE}-part is quite simple: 
create a header in the dictionary and store a number in the first 
four bytes of the parameter area. On  executing  a defined word - 
here {BEAUTIFUL} - {;CODE} places the parameter field address  on 
the stack. Now {SP )+} takes a 32-bit value from the stack, which 
in  this case is the PFA (Parameter Field Address) and stores  it 
in  address register A0 by {A0 LMOVE}.  Then {A0 )}  fetches  the 
contents of the PFA stored in A0 and moves them to data  register 
D0. At last the contents of D0 are moved to the top of the stack, 
overwriting the PFA, which was still there.

SOLUTIONS TO PART NINE

1.  As to include a possible space in the string.  Otherwise  the 
    following string wouldn't have been possible: a young girl.
2.  The definition of {LEFT$} is:  : LEFT$ 1 SWAP MID$ ;
3.  The optimized version of {COMPARE} looks like:
    : COMPARE >R 2DUP C@ SWAP C@ R@ >= SWAP R@ <= AND 0=
              IF ABORT" Length-count error !" THEN
              R> 1+ 1 DO 2DUP I + C@ SWAP I + C@ 2DUP <>
                         IF > IF 2DROP 1 LEAVE
                              ELSE 2DROP -1 LEAVE
                              THEN
                         THEN
                      LOOP DUP 1 > IF 2DROP THEN ;
4.  The highest number you can possibly store in a byte is 255.
5.  The  two parts in the compare word are a) the  error-checking 
    and b) the true comparison.
    a) : ?ERRCOMP ( addr1 addr2 count ---  | addr1 addr2 count )
                 >R 2DUP C@ SWAP C@ R@ >= SWAP R@ <= AND 0=
                 IF ABORT" Length-count error !" THEN R> ;     
    b) : (COMPARE)  1+ 1 DO 2DUP I + C@ SWAP I + C@ 2DUP <>
                         IF > IF 2DROP 1 LEAVE
                              ELSE 2DROP -1 LEAVE
                              THEN
                         THEN
                      LOOP DUP 1 > IF 2DROP THEN ;
    : COMPARE ( addr1 addr2 count --- 0 | 1 | -1 )
              ?ERRCOMP (COMPARE) ;
6.  Optimizing {MID$} is done by making it buffer independent and
    leaving out the error-checking:
    : MID$ SWAP >R SWAP 1 MAX 2DUP R> + SWAP C@ 1+ MIN >R OVER
           C@ MIN R> OVER - >R + R> ;
    The stacknotation: ( addr1 offset n --- addr2 count ). As you
    can see, what is left on the stack is just fine for {TYPE}.
7.  The answer is to compare the count-bytes of the 2 buffers.
    : IN$ 2DUP C@ SWAP C@ < IF ABORT" String too long !" THEN ;
    The  rest  of  the definition remains  unchanged  and  should 
    follow {THEN}.
8.  The 'neutral' {GLUE}-word: ( addr1 addr2 --- )
    : GLUE 2DUP >R >R OVER C@ OVER C@ 1+ ROT + ROT 1+ SWAP ROT
           MOVE R> R> SWAP C@ OVER C@ + SWAP C! ;
    This  {GLUE}  expects two  addresses  of  stringbuffers;  the 
    concatenated string  is placed at addr2.  With  addr2  {COUNT 
    TYPE} you can print the string at your display.
9.  Here are two adapted words:
    : LEFT$ 1 -ROT MID$ ;
    : RIGHT$ SWAP OVER C@ OVER - 1+ SWAP ROT MID$ ;    

EXERCISES 

1.  If you studied {CREATE...DOES>} carefully, you should be able 
    now to define the word {CONSTANT}.  For  practising  purposes 
    only, as this word is already in your system.
2.  Define a word that will print out any string given its start- 
    address. Call it {.STRING}.
3.  Create  a  defining  word that will  let  you  define  byte-
    variables. Call it {CVARIABLE}.
4.  What's wrong with the output of {SKEWEE}?
5.  Define  {VARIABLE} in terms of {CREATE...DOES>} in two  ways: 
    one version should initialise the parameter field,  the other 
    should not.
6.  Replace  {SWAP * +} with some other  code that will work  out 
    the same. Think it {OVER} and {OVER} !
7.  Insert some error-checking in the definition of {2DIMARRAY}.
8.  Define  a tableword that will leave its starting-address  and 
    the number of items it contains.
9.  Which word could I have used in stead of {,} in {SINUS}?
    And for what reasons would that have been an improvement?                             

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