PET4TH User's Guide version 00.01

PET4TH is Another Forth Programming Language for DOS

by Petrus Prawirodidjojo <pet4th@gmail.com>Contents

Introduction
Chapter  0 Getting Started
Chapter  1 Integer, Double, Character and String
Chapter  2 Variables and Control Structures
Chapter  3 Floating-point and PETS teaching/learning aid
Chapter  4 RECURSE CREATE DOES>
Chapter  5 Compile
Chapter  6 More Words and PETS
Chapter  7 Sample Files
Chapter  8 Assembly Language
Chapter  9 Source Style
Chapter 10 Let's Try
Appendix A Specification
Introduction

Forth is a very special programming language invented by Charles Moore in the
seventies. A lot has been said about Forth and I will not tell you more,
please browse www.forth.org and follow the links, also read comp.lang.forth
to learn more.

This PET4TH was based on eForth by C. H. Ting and Bill Muench. Unlike eForth,
PET4TH uses native code for many of its words definition, for speed. Also
PET4TH is written for all kind of users, providing floating-point arithmetic
and strings. By adding ANS Forth layer, PET4TH is almost ANS Forth 1994
compliant. It will be modified from time to time, especially those non-
standard words.

While ANS Forth asks for uppercase word, PET4TH is case sensitive and uses
lowercase word. That is why ANS Forth layer may be needed.

Currently most words can run on 8088 with the exception of floating-point
words which need arithmetic coprocessor. PET4TH uses MSDOS calls and BIOS
calls, so that some words can not run on WinNT, Win2000 and WinXP.

This User's Guide is also updated from time to time. If you are a novice,
remember, that the best way to read this is to go from beginning to the end
(do not stop too long on a subject), then start again from beginning to the
end.

My thanks to Forth Community. Without you all, I will not be able to write
PET4TH, Another Forth Programming Language.

Author,
Petrus Prawirodidjojo.
Chapter 0 Getting Started
How to run PET4TH, to exit, to use it as calculator, and hello program.

First you need to install PETFORTH.ZIP:
     pkunzip -d petforth<cr>
You may use PKZIP or other compression program. I hope this PET4TH is not the
first program that you would like to use.

Run PET4TH from DOS prompt:
     pet4th<cr>
ie, type pet4th then press Enter which is represented by <cr>. Do not type
'<cr>'! You will see banner like:

PET4TH for DOS version 01.00.0014  \ (C) 1998-2004 Petrus Prawirodidjojo
_

and now PET4TH is waiting for your commands.

To exit PET4TH, type:
     bye<cr>

BYE is one of standard Forth word. Usually it is described under heading
like:
15.6.2.0830 BYE  ( -- )
Use BYE to exit Forth.

Notes:
-    15.6.2.0830 is the number for BYE word in the ANS Forth Standard, search
     this number to find information on BYE.
-    Inside bracket is the stack notation, the left side of '--' is the
     parameter before operation, the right side of '--' is the result after
     operation. This BYE does not use any data before operation and does not
     left any result after operation.

A word is a sequence of ASCII characters delimited by space or white space
like a tab <ht>, or a carriage return <cr>. Space <space> is very important.
When typing Forth program, be careful about <space>.

A floating-point (fp) number is a number which has a decimal point which is
not the last character. Actually an fp number is also a word but since it is
not found in the word list, but qualify for an fp number, it became an fp
number.

Sample of a floating-point number:
1.0  is 1
2E   is 2
-3.5 is -3.5
4.56E-6   is 0.00000456
+5.3 and 6.2E+1 are not valid floating-point number, since + is not allowed.
4. (four dot) is accepted as a floating-point number, but during compile and
interpret, double integer is checked first, so 4. is a double, not a
floating-point number.
25 is accepted as a floating-point number, but during compile and interpret,
integer is checked first, so 25 is an integer, not a floating-point number.
Though PET4TH is almost ANSI Forth compliant, whenever necessary, PET4TH
choose 'ease of use' or 'ease of implementation' over rigid definition of the
standard.

Now, run PET4TH again, then type:
     1.0 3.0 f/ f.<cr>
result:
     1.0 3.0 f/ f.  0.333333333333333333  [ok]

1.0 and 3.0 are fp numbers, F/ and F. are words. When PET4TH finds an fp
number it will be put into the fp stack. F/ is word that divide first fp
number by second fp number and leave the result also in the fp stack. F.
means print the number in fp stack, so you will see 0.333333333333333333. F.
also discards the result from the fp stack.

PET4TH does not keep the type of the number in stack. That is why PET4TH has
many operators for each operation, each for different data type. For example,
for addition:
+    for integer addition
d+   for double integer addition
f+   for floating-point number (sometimes called real number) addition.

See the content of the fp stack after each word:
1.0            1.0
1.0 3.0        1.0 3.0
1.0 3.0 F/     0.333333333333333333
1.0 3.0 F/ F.  [empty]

Try the following:
1 + 9 =
type:
     1.0 9.0 f+ f.<cr>
result:
     1.0 9.0 f+ f.  10  [ok]

1 - 2 =
type:
     1.0 2.0 f- f.<cr>
result:
     1.0 2.0 f- f. -1  [ok]

(1+2) x (3+4) =
type:
     1.0 2.0 f+ 3.0 4.0 f+ f* f.<cr>
result:
     1.0 2.0 f+ 3.0 4.0 f+ f* f.  21  [ok]

(5x6) - (7/8) =
type:
     5.0 6.0 f* 7.0 8.0 f/ f- f.<cr>
result:
     5.0 6.0 f* 7.0 8.0 f/ f- f.  29.125  [ok]

12 / {(5x6)-(7/8)} =
type:
     12.0 5.0 6.0 f* 7.0 8.0 f/ f- f/ f.<cr>
result:
     12.0 5.0 6.0 f* 7.0 8.0 f/ f- f/ f.  0.412017167381974249  [ok]

or type:
     5.0 6.0 f* 7.0 8.0 f/ f- 12.0 fswap f/ f.<cr>
result:
     5.0 6.0 f* 7.0 8.0 f/ f- 12.0 fswap f/ f.  0.412017167381974249  [ok]

FNEGATE negates (change sign,multiply by -1) fp number on top of the stack.
     1.0 fnegate f.<cr>
result:
     1.0 fnegate f. -1  [ok]

FSWAP swaps first fp number and second fp number. FSWAP F/ means divide
second fp number by the first.

FDUP copies the fp number on the top of the stack.
1.23456789 x 1.23456789 =
     1.23456789 fdup f* f.  1.5241578750190521  [ok]
which is better than
1.23456789 ^ 2 =
     1.23456789 2.0 f** f.  1.5241578750190521  [ok]
since power calculates by logarithm which usually reduce the precision.

FDROP discards the fp number on the top of the stack. For example on the
stack we have: 2.0 3.0 1.0 and you want to multiply 2.0 and 3.0, type
     fdrop f* f.

FOVER duplicates the second fp number. From: 1.0 2.0 becomes 1.0 2.0 1.0.
FOVER stack notation is ( F: r1 r2 -- r1 r2 r1 )

FROT rotates the third fp number to the top of the stack. From: 1.0 2.0 3.0
becomes 2.0 3.0 1.0.

FSQRT takes square root of fp number. Try this:
     3.0 fsqrt fdup f* f.

FMAX and FMIN gets the bigger or small number between two numbers
respectively.
     3.0 5.0 fmax f.
     3.0 5.0 fmin f.

FABS will return absolute value of fp number.
     -3.0 fabs f.

FLOOR will return integer nearer to minus infinity.
     -3.5 floor f.

FROUND will round fp number. Currently set at normal rounding, that is round
to nearest or even integer.
     2.5 fround f.
     3.5 fround f.
     3.49 fround f.
     2.51 fround f.

Up until now we see that if a word is found in the list (defined) it will be
executed (F+ F-). If it is not found in the list but qualify as fp number it
will be put on top of the fp stack. And if it is not qualified as a number,
all types of number, PET4TH will display error number [-13] meaning undefined
word. Try:
     !@#$%<cr>
And if no error, PET4TH will return [ok] prompt, ready to get next
instructions.

Let us now try to program in Forth, defining new words or instructions. Colon
(:) means define and semicolon (;) means end of definition. Try this:
     : square fdup f* ;<cr>
     1.23456789 square f.<cr>
result:
     1.23456789 square f.  1.5241578750190521  [ok]

     1.23456788 square f.<cr>
result:
     1.23456788 square f.  1.5241578503276944 [ok]

In the above example we define SQUARE as FDUP followed by F*. Let us try
another definition:
     : .square fdup f* ." Square: " f. ;<cr>
     1.23456789 .square<cr>
result:
     1.23456789 .square  1.5241578750190521  [ok]

." means start of text to be displayed. Please follow ." with a space so that
it will be a word (delimited by a space). And end the text with ". All
characters between space after ." and " including the space before the " will
be displayed when .SQUARE is run. Please compare with (look carefully):
     : .square2 fdup f* ." Square:" f. ;<cr>
     1.23456789 .square2<cr>
result:
     1.23456789 .square2 Square: 1.5241578750190521  [ok]


CR is to have a new line. Try this:
     : hi ." First line" cr ." Second line" ;<cr>
     hi<cr>
result:
     hi First line
     Second line  [ok]

Usually we do not program (define words) directly in PET4TH (hopefully later
I can improve PET4TH to facilitate this). Instead, we write a program in a
separate file, like SAMPLE\HELLO.PET.

===========================================================
\ stand alone application running 'main' at start-up
\ pet4th fload hello.pet   will produce HELLO.COM w/o dictionary
\    use 'fsave' instead of 'turnkey' to include dictionary

cr
.( ...including hello.pet producing hello.com )

\ main
\     display banner, 'Hello World!' and exit with code 0
: main
    ." HELLO v01.00 ..." \ your application banner
    .forth2              \ credit, second line of banner!
    cr ." Hello World!"       \ display hello
    0 bye ;              \ exit code 0

     mainword main       \ with main routine
\    fsave HELLO.COM          \ with dictionary
     turnkey HELLO.COM   \ without dictionary

unused u. .( bytes free.) cr
0 bye
\    end of application, - do not delete -
===========================================================

This file is typed using an editor. You may run it by typing the following
while in DOS prompt:
     pet4th fload hello.pet<cr>
Then type:
     hello<cr>
and see the result. Please note that FLOAD is not ANS Forth Standard.

\ means treat the rest of the line as remark, does not execute it. .( and )
means display the text in between, this is just like ." " which is used
inside definition, while .( and ) is used outside definition.

This program uses TURNKEY to save instead of FSAVE, which result in smaller
file because the dictionary is not saved. Both are not ANS Forth Standard.

Since we have in the HELLO.PET file
     mainword main
upon running HELLO, word MAIN is executed. The application HELLO.COM does not
wait for instruction line anymore!

You may look-up the meaning of other new words in the glossary, in file
PET4TH.GLO or from draft of ANS FORTH 1994 available somewhere in the net.
Chapter 1 Integer, Double, Character and String
Introduction to data type and programming in Forth.

An integer is a number without decimal point. While an fp number is a 10 byte
number, an integer is a 2 byte number. If it is treated as integer number,
the range is from -32768 to +32767. And when it is treated as unsigned
integer, the range is from 0 to 65535.

Mapping from Integer to Unsigned integer
     Integer        Unsigned integer
     -32768         32768
     -32767         32769
         -1         65535
          0             0
          1             1
     +32767         32767

Integers are stored in Data Stack, not in fp stack. Since PET4TH is 16 bit, a
cell, an integer, is 2 bytes. Forth used to be a controller, which uses
integers heavily, so integer is more important than fp. That is why for
addition, fp use F+ while integer use +. And other operations too.

Let us try the following:
34000 + 5 = 34005
     34000 5 + u.
where U. means display integer as unsigned.
5 - 8 = -3
     5 8 - .
where . means display integer as signed integer.

Most operation that prefixed with F for fp, now is used without prefix for
integer, DUP OVER SWAP ROT DROP MAX MIN ABS. To see the number of integers on
the Data Stack, use .S definition. Try this:
     1 2 3 .s abort
Note ABORT will stop operation and clean up the stack.

To count the number of integers on the Data Stack, use DEPTH definition. Try
again:
     1 2 3 depth . abort

For assembly language programmer:
Intel x86 processors are of little endian type, low byte is stored in lower
address, high byte is stored in higher address. $1234 (=hexadecimal 1234)
stored in location $A000:
$A000 contains $34
$A001 contains $12
Try:
create abc 2 allot
$1234 abc !
abc 2 dump

A double integer is a number with trailing decimal point. It is a 4 byte
number, ranging from -2,147,483,648 to 2,147,483,647 for signed double, and
from 0 to 4,294,967,295 for unsigned double. This type of number was also
prefered by Forth programmers compared to fp. And we have D+ D- and D.

Double is stored low integer first, followed by high integer. Please compare
the result of these:
     123 456 d.
     29884539. d.
     456.0 65536.0 f* 123.0 f+ f.
The double integer stored as two integers has the value of second integer
multiplied by 65536 and added with first integer.

For assembly language programmer:
Forth favors big endian for double integer. 0x12345678 is stored from lower
address to higher address as 0x34 0x12 0x78 0x56 (using 2!). That is why I
define D! and D@ to supplement 2! and 2@ of ANS Forth Standard. D! stores it
as 0x78 0x56 0x34 0x12 like Intel processors.
Try:
create abc 4 allot
$12345678. abc 2!
abc 4 dump
create def 4 allot
$12345678. def d!
def 4 dump

This is what PET4TH do when receiving word from a user.
If the word is found in the list (defined), then it is executed.
If it is an integer, it is put in Data Stack.
If it is a double integer, it is also put in Data Stack.
If it is an fp number, it is put in fp stack.
Otherwise, return 'undefined word' error message, that is [-13].

A character is an integer, but only lower byte is used, the high byte is 0.

A string was represented by an address, where the first byte is count and the
rest is the string. It is called counted string. But now string is
represented by address and count, which enabled string of length 65535.
PET4TH saves the string as counted string, so length is limited to 255.

ACCEPT needs an address to save the string and maximum length expected, and
return the length received. PAD will return user space for buffer. TYPE needs
two parameters, address and length of the string to be printed. Let's try
this:
     : input cr pad 20 accept cr pad swap type ;
     input
CR will move the cursor to a new line
PAD will return user space address
20 is the maximum characters from the user
PAD 20 ACCEPT means wait from user maximum 20 characters to be typed, try
typing more than 20 characters!
CR will move the cursor to a new line
ACCEPT PAD leave string count and address on the stack, but TYPE needs
address and string count on the stack, so we need to use SWAP before TYPE

To input an fp number, use ACCEPT and STRING>FLOAT (not ANS Forth Standard).
     : inputf pad 26 accept pad swap string>float ;
     : askage cr ." Age: " inputf cr ." Age = " f. ;
     askage

Except for testing, I suggest you type the program and put it in the file.
When something goes wrong, go back to the editor and modify it. The
programming cycle is edit, compile, run, edit, compile, run...

ACCEPT and STRING>FLOAT entries in the glossary, PET4TH.GLO:
ACCEPT  ( c-addr +n1 -- +n2 ) 6.1.0695
string>float  ( c-addr u -- ) ( F: -- r )
See that ACCEPT expects two parameters and return one. ACCEPT is a standard
word. Note that it is written in uppercase and have a number 6.1.0695. While
STRING>FLOAT is not a standard word. STRING>FLOAT return a floating point
number r in fp stack, see that ( F: -- r ).

We have to comment the program so that later we can immediately understand
how it works. Here it is:

===========================================================
\ program to ask age and display age again

\ inputf  ( F: -- r )
\     get an fp number from keyboard
: inputf
    pad 26 accept pad swap string>float ;

\ askage
\     ask age and display it again
: askage
    cr ." Age: " inputf
    cr ." Age = " f. ;

askage
===========================================================

saved as AGE.PET

At the DOS prompt, type
     pet4th fload age.pet
to compile and run.

If you does not want to give the source code, change the last line of the
program:
     askage
to:
     mainword askage
     turnkey age.com
     0 bye
Don't forget to add:
     0 bye
as the last word of askage. See below:

===========================================================
\ program to ask age and display age again

\ inputf  ( F: -- r )
\     get an fp number from keyboard
: inputf
    pad 26 accept pad swap string>float ;

\ askage
\     ask age and display it again
: askage
    cr ." Age: " inputf
    cr ." Age = " f.
    0 bye ;    \ when run, it will go back to operating system

mainword askage
turnkey age.com
0 bye          \ when compiling, it will go back to os
===========================================================

To compile:
     pet4th fload age.pet
To run:
     age
Chapter 2 Variables and Control Structures
Introduction to variables, flag, comparisons and control structures.

Usually Forth programmers tend to avoid variables to save space, they use
only stack. But sometimes, it is easier to use variables than juggling the
stack. There are FVARIABLE VARIABLE and 2VARIABLE for fp, integer and double,
and DVARIABLE and SVARIABLE which are not ANS Forth Standard, for double
little endian and string variable.

     fvariable age       \ define age as fvariable
     48.0 age f!         \ age is 48.0, f! means store as fp
     age f@ f.           \ display age, f@ means fetch as fp

DVARIABLE is like 2VARIABLE, but instead of using 2! and 2@, please use D!
and D@. DVARIABLE is ideal for passing value to native code.

     dvariable sector#   \ define sector# as dvariable
     $12345678. sector# d!    \ save 0x12345678
     sector# d@ du.      \ display 0x12345678 in decimal
     sector# 4 dump      \ see contents of sector# in memory

SVARIABLE is for string, something Forth programmers do not like too. That is
why it is not required in ANS Forth Standard. SVARIABLE take one argument as
maximum string length. Many string operation words in PET4TH are not ANS
Forth Standard words.

     21 svariable name   \ define name as string, no more than 21 characters
     s" PET4TH" name s!  \ s" " address and length of string
     name s@ type        \ display content of name
     name s?             \ same as above
     s"  V1.00" name s+  \ concatenate
     name s?             \ see the new name

One more data type, a flag. A flag is actually an integer. FALSE is defined
as 0 while TRUE is defined as not 0, better if it is 65535 or -1.

     false .
     true .         \ as integer it is -1
     true u.        \ as unsigned integer it is 65535

IF THEN control structure will transfer execution to word after IF if it is
true, in other words, words between IF and THEN are executed only if it is
true. All control structure words like IF and THEN must be used within
definitions, for reasons explained later.

     : expecttrue  ( flag -- )
         if ." Oh, thank you" then ;
     true expecttrue
     false expecttrue

IF ELSE THEN control structure executes words between IF and ELSE if it is
true, and words between ELSE and THEN if it is false.

     : .flag  ( flag -- )     \ display flag
         if ." It's true." else ." It's false." ;
     3 5 < .flag         \ 3 is smaller than 5
     1 1 = .flag         \ 1 is equal 1
     0 .flag             \ 0 is false
     -1 .flag            \ -1 is true
     2 .flag             \ 2 is true
     100000. 100000. d= .flag \ 100000. is a double

DO LOOP control structure executes words between DO and LOOP until
termination number is reached. The index is I. LOOP increments index I by
one.

     : 2^x  ( # -- )          \ display table of 2^x
         0 do
           cr ." 2^" i .
           [char] = emit      \ display =
           i int>float        \ int>float to convert integer to fp
           2.0 fswap f** f.   \ f** operates on fp numbers
           loop ;
     17 2^x                   \ from 0 to 16, 17 times, but 17 not included

The above sample is rather long to type, cut the sample program only, and
save in a file, for example SCR.PET. Now launch PET4TH from DOS prompt:
     p
then type:
     fload scr.pet
and see the result. You may see the compiled definition by SEEing the word:
     see 2^x

BEGIN UNTIL control structure will loop between BEGIN and UNTIL until flag is
true.

     : x*2  ( # -- )
         0 begin         \ ( termination_number index )
           dup 2* cr .   \ duplicate index, multiply by two
           1+            \ increment index by 1
           2dup = until  \ compare termination_number and index
         2drop ;         \ discard termination_number and index
     17 x*2              \ from 0 to 16, 17 times, but 17 not included

BEGIN WHILE REPEAT control structure will loop until top of stack is false
when executing WHILE.

     : x*2  ( -- )
         1 begin         \ ( index )
           dup 2*        \ ( index index*2 )
           dup 16 <= while    \ ( index index*2 )
           cr . 1+       \ ( index+1 )
           repeat
         2drop ;         \ discard index and index*2
     x*2

CASE ENDOF ENDCASE control structure is for multiple selections.

     : .digit  ( number -- )
         case
           0 of ." zero" endof
           1 of ." one" endof
           2 of ." two" endof
           3 of ." three" endof
           endcase ;
     0 .digit cr
     1 .digit cr
     2 .digit cr
     3 .digit cr

COMPARE returns result of string comparison. If first string sorted before
second string, result is -1. If first string match second string, result is
0. If first string sorted after second string, result is +1.

     : .sort  ( c-addr1 u1 c-addr2 u2 -- )
         2dup type                 \ type string first
         s" Forth" compare case
           -1 of ."  is sorted before Forth" endof
            0 of ."  matches Forth" endof
            1 of ."  is sorted after Forth" endof
            endcase ;
     s" Forth" .sort cr
     s" Alpha" .sort cr
     s" Zulu" .sort cr
Chapter 3 Floating-point and PETS teaching/learning aid
>FS FS> FS@ >FS2 FS2> FS@

Current implementation of PET4TH uses hardware floating-point stack which is
only 8 stack depth. If more than 8 floating-point numbers are entered, there
will be error. F. uses additional 2 stack entries, so if there are already 7
stack entries, top of the stack number can not be displayed using F. To clear
error, use FNINIT, which also clear all numbers from the stack.

     fninit 1.0 2.0 3.0 4.0 5.0 6.0 f. f. f. f. f. f.

Let us look at .FS, .S equivalent for floating-point:
\ .fs
\     display floating-point stack
: .fs
    fdepth dup 0 ?do
      dup i - 1- fpick f. loop
    drop ;
FPICK will use one more stack entry, and .FS is now limited to 5 stack
entries (FDEPTH max. is 5 to use .FS).

     : .fs fdepth dup 0 ?do dup i - 1- fpick f. loop drop ;
     fninit 1.0 2.0 3.0 4.0 5.0 .fs

To avoid stack overflow, limit the number in the stack to 5. Save some of the
numbers to variables. Later you may use other technique available in the
current implementation of PET4TH.

In the sample directory, there is PETS.PET. It is a teaching/learning aid.
First compile it:

     pet4th fload sample\pets.pet

Now run it:

     pets
     floatmode

The first part is Data Stack display, address and content in hexadecimal. Top
of the stack is the last entry.

The second part, to the right of Data Stack, is Floating-point Stack, again,
top of the stack is the last entry. The stack entries are named(from top of
the stack), X Y Z T 4 and 5, only six out of eight stack entries are
displayed.

The third part is scratch area, default PAD, a user buffer with maximum
length 84 characters. Only 48 of 84 characters are displayed.

The fourth part is display area, just beneath scratch area.

The fifth part is line input area, where you type commands or words to be
executed.

Let us try the following:

     1 2 + .

You will see 3 on the display area. To see the process, type a number or a
word one at a time, and see Data Stack and display area:

     1
     2
     +
     .

Now back to floating-point:

     1.0 2.0 f+ f.

If you type a number or a word one at a time, you will see at the right of FP
Stack, number inside square bracket. It is the hexadecimal representation of
a floating-point number. Ignore it.

Now try each of this, and look at the PAD:

     -1 pad !
     pi pad f!
     s" seattle" pad swap move
     s" seattle" pad s!

What you see is internal presentation of integer, floating-point and string.
Now you might like to try typing Forth instructions in the PETS. To quit,
type

     bye

as usual.

Back to floating-point. There are >FS FS> and FS@ which save (retrieve) the
state of FPU to (from) FSTACK which can contain up to 4 states. Each state
have all 8 registers and status of FPU.

     fninit 1.0 2.0 pi >fs
     fs>

There are >FS2 FS2> FS2@ which save (retrieve) top of the stack to (from)
FSTACK2 which can contain sixteen fp numbers.

You may use FNSAVE at the beginning of your program to save contents (state)
of FPU and FRSTOR at the end of your program to restore the contents of FPU.
It will be saved in NPXST.

Sometimes you may want to discard the bottom of stack, to reserve place for
the next number on the top of the stack without overflowing the stack. Use
FFREE7 for that.

Perhaps the most important displaying floating-point number word is F.R . It
needs width (# columns) reserved for the display and number of digits to the
right of decimal point. Try:

     pi 1E5 f* 20 3 f.r

It is printed right justified!
Chapter 4 RECURSE CREATE DOES>
String

5 sum is 0+1+2+3+4+5

     : sum  ( n1 -- n2 )
         0 swap 0 do
           i 1+ +
           loop ;
     5 sum .

Or sum(0) is 0

     : sum ;   \ if input is 0

And sum(1) is 1+sum(0) = 1, like
: sum dup 1- sum + ;


Also sum(2) is 2+sum(1) = 3, like
: sum dup 1- sum + ;

So the definition of sum is:
: sum dup if dup 1- sum + then ;

But during defining SUM, SUM is not recognized yet. Use RECURSE instead:

     : sum  ( n1 -- n2 )
         dup if
           dup 1- recurse + then ;
     5 sum .

Using recursive definition sometimes simpler, but it takes a lot of memory
for Return Stack. Now let us see data structures.

CREATE will create a variable but no data space allocated. While VARIABLE has
2 bytes allocated. This is VARIABLE defined using CREATE:
: variable create 2 allot ;

Since PET4TH is 16 bit Forth, 1 cell is 2 bytes. It is better to say:
: variable create 1 cells allot ;
So the definition also work for 32 bit Forth.

Defined this way, VARIABLE is not initialized to zero. All variables in
PET4TH are initialized to zero, equivalent to:
: variable create 0 , ;
: 2variable create 0 , 0 , ;
: fvariable create 0 , 0 , 0 , 0 , 0 , ;
, (comma) claims 1 cell and store tos (top of stack value).

     variable abc
     abc .
     abc @ .

Variable name will return address of the first data. To get the content use @

     create pqr 5 ,
     pqr @ .

Suppose we want to display day of week as literal, 0 Sun 1 Mon 2 Tue 3 Wed 4
Thu 5 Fri 6 Sat 7 Sun. I like Sun as 0, but ISO 8601 standard states Sun as
the last day of the week, that is 7. Our first approach is:

     : .dow  ( u -- )    \ .dow is read as print day of the week
         7 mod           \ wrap 7 to 0
         case
           0 of ." Sun " endof
           1 of ." Mon " endof
           2 of ." Tue " endof
           3 of ." Wed " endof
           4 of ." Thu " endof
           5 of ." Fri " endof
           6 of ." Sat " endof
           endcase ;
     5 .dow

Note that Forth usually ends printing value with a space, that is
." Sun "
not
." Sun"

Now try other approach:

     : .dow  ( u -- )
         7 mod
         3 *        \ 3 characters for each dow
         s" SunMonTueWedThuFriSat" drop +    \ drop length, add base addr
         3 type space ;
     7 .dow

Another approach:

     create dows 21 allot
     s" SunMonTueWedThuFriSat" dows swap move     \ copy to dows
     : .dow 7 mod 3 * dows + 3 type space ;
     3 .dow

The above S" is not used inside definition, the string is stored in 80
characters data area whose address is returned by POCKET. The area is used by
WORD which is in turn used by S" Since the area is used by WORD which is used
by many words, we can not expect the string will not be overwritten. We move
it to DOWS

Compare this one:

     s" abc" type
     s" pqr" type

And this one:

     s" abc" s" pqr" 2swap type type

When S" is used inside a definition, the string is stored in that definition.

CONSTANT allow calling a value instead of address (VARIABLE). Compare:

     variable three
     3 three !
     three @ .

     5 constant five
     five .

CONSTANT can be defined as
: constant create , does> @ ;

     5 constant five
     five .

FIVE variable will be created by CONSTANT, 5 is the value. But now FIVE will
not return the address as in VARIABLE, but also @ (fetch) it. That is
specified in DOES> 
FIVE stack notation is FIVE  ( -- 5 )

Let us define array using CREATE and DOES>.

     : array create cells allot does> swap cells + ;
     10 array abc
     10 5 abc !
     5 abc @ .
     100 array pqr

10 array abc: create abc 10 cells allot
5 abc: 5 abc swap cells +
5 abc: abc 5 cells +
10 5 abc !: store 10 to fifth cell of abc
5 abc @ .: display content of fifth cell of abc
100 array pqr: create pqr 100 cells allot
Chapter 5 Compile
POSTPONE VOCS ORDER ALSO PREVIOUS DEFINITIONS VOCABULARY

During interpreting, if a word is found in the vocabulary (defined), it is
executed. If it is not defined, but qualify as a number, it is put in the
stack.

During compiling, that is after : (colon), if a word is found in the
vocabulary the address is stored in code area. If it is not defined, but
qualify as a number, it will be stored in code area to be fetched to top of
the stack during execution. This way, word defined looks like a macro,
calling words one after another.

Word ? is defined as:
: ? @ . ;
Now ? is in the vocabulary. Calling ? is calling @ and then .
; (semicolon) ends definition of ?, back to interpret mode. Note that during
defining ?, @ and . are not executed, but stored.

     : display @ . ;
     variable age
     5 age !
     age display

     : five 5 ;
     five .

FIVE contains 5. When FIVE is executed, 5 will be popped to the tos.

Now consider this:

     : onehundred 2 50 * ;

When ONEHUNDRED is executed, 2 will be popped to the stack followed 50 then
multiplied to produce 100. The multiplication is done during execution. If
ONEHUNDRED called 10 times, the multiplication is done ten times too.

When we do multiplication on a paper,

     : onehundred 100 ;

we lost how 100 is computed. Look at this:

     : onehundred [ 2 50 * ] literal ;

[ means start interpreting. ] means end interpreting, back to compiling.
LITERAL means tos is stored and will be popped up during execution. The last
two examples produce same definition.

2 50 * is calculated during compiling, it does not affect run time. How can [
start interpreting/executing (leaving compile mode) while in compile mode?
Address of [ should be stored in ONEHUNDRED definition and executed when
ONEHUNDRED is called.

The answer lies in a so called immediate mode. When defining [ we add
IMMEDIATE after ;
: [ .. ; immediate
The compiler will not store the address of [ to the ONEHUNDRED definition,
but executing [. 

LITERAL must be IMMEDIATE too.

Let us refine the algorithm, during compiling, if a word is found,
-     if it is immediate then execute it immediately,
-     if it is not immediate then store the address in the definition.

     : .. [char] x emit ;
     : .. [ char x ] literal emit ;

Those two examples do the same thing, display character x on the screen.
[CHAR] must be IMMEDIATE word. Note that:

     : abc char emit ;
     abc xyz

will display x, being the first character in xyz. Not displaying e from emit.

What will happen to
: abc char x emit ;
?

If x is not found, the compiler will issue x undefined word error.

Let us look at the following definition:

     \ .n<>0  ( n -- )
     \   display n if n<>0
     : .n<>0
         ?dup if . then ;
     0 .n<>0
     1 .n<>0

If we want to have macro that will produce sequence of:
?dup if . then
we can use POSTPONE and IMMEDIATE.

     \ .n<>0macro
     \    macro to display n if n<>0
     : .n<>0macro
         postpone ?dup
         postpone if
         postpone .
         postpone then ; immediate

     \ .n<>0  ( n -- )
     \   display n if n<>0
     : .n<>0
         .n<>0macro ;
     0 .n<>0
     1 .n<>0

Words are grouped into word lists. Use VOCS (vocabularies) to see all
available word lists. When searching for a defined word, word lists will be
searched according to the order, use ORDER to see search order, the context.
ORDER also display compilation word list (current vocabulary), the word list
into which a new defined word is put.

     vocs
     order
     words

When a vocabulary name is called, it replaces the first searched vocabulary,
first in the list of context. ALSO will copy the first searched vocabulary.
PREVIOUS will discard the first searched vocabulary in the search list.
DEFINITIONS will set the current vocabulary the same as the first searched
vocabulary. And WORDS will list all words in the first searched vocabulary.

     previous order
     words
     previous order
     words
     definitions order
     also fpvoc also application definitions order

To create new vocabulary use VOCABULARY <name>

When a new word is defined, and it has the same name as one of the defined
word in the search order, Forth will display warning 'redefine'. Since Forth
searches word from the last defined word to the first defined word, the
previous defined name may not be accessible anymore. If those two words are
in the different word list, by arranging search order, previous defined word
may be accessible again.

The new word will be used for the next definition only. Try this:

     : plus + ;
     : + * ;
     5 3 + .
     5 3 plus .

Chapter 6 More Words and PETS
>R R> R@

There is standard for Forth, current standard is ANS Forth 1994. The standard
is good but it is not designed to be a tutorial. The standard can be bought,
and the draft can be downloaded from internet. If you have read until this
chapter, you may start reading the ANS Forth 1994 standard.

PET4TH does not have all standard words, and have plenty of non-standard
words. To find out what words are available in PET4TH, please look at file
PET4TH.GLO. Words in uppercase and have numbers in the entry is standard
word.

This user guide will not discuss technical matters which will be in a
separate manual, PET4TH Technical Reference (not available yet). But using
PETS, you may learn the internals of PET4TH. And there are several samples
that display the internals of PET4TH in the sample directory.

Currently there are 3 types of windows in PETS:

intmode  (0 windows !)
     Used for learning/teaching program writing without floating-point,
integer mode. This is default windows. It shows:
- part of Data Stack,
- part of Return Stack and
- scratch area area with part of PAD area as default.
scratch area default: pad to scratcharea

floatmode (-1 windows !)
     Used for learning/teaching program writing with floating-point, float
mode. It shows:
- part of Data Stack,
- part of floating-point stack and
- scratch area with part of PAD area as default.
scratch area default: pad to scratcharea
This may not be default since floating-point is not standard, and needs
coprocessor or processor better than or equal 486DX.

forthmode (1 windows !)
     Used for learning/teaching Forth writing, it shows Forth internals,
forth mode. It shows:
- part of Data Stack,
- part of Return Stack,
- name area with part of name (dictionary) area as default,
- code area with part of code area as default.
name area default: np @ 16 - to namearea
code area default: here 16 - to codearea
     To refresh name area and code area, do forthmode again.

Now we will use PETS for learning more Forth words. Run PETS:

     pets
     floatmode
     forthmode
     intmode

Most of the windows display number in hexadecimal so that it takes less
space. In the left part usually address is displayed, also in hexadecimal. To
convert a hex number to decimal, prefix the hex number with $ and use
uppercase for digits after 9. Prefix binary number with % . And if base is
not decimal, prefix decimal number with #

     $55 .
     $F000 .
     %10101010 .
     %1111000000000000 .
     #85 .
     #61440 .

PET4TH provides B. W. DW. to display byte, word and double word in
hexadecimal, non-standard words.

     85 b.
     61440 w.
     -4096 w.
     61440 85 dw.
     5632000. dw.

If you want to see the numbers on the Data Stack, do not display it.

     85
     61440
     5632000.

You may need word to discard all data in the Data Stack, instead of using
DROP for each cells.

     : clrds depth 0 ?do drop loop ;
     clrds

Definition name is limited to 31 characters. Use good (and short) names for
mostly used definitions, and choose ugly names for seldom used definitions.

If you consider CLRDS is good definition and want to have it always in PETS,
save it:

     fsave pets.com
     bye
     pets
     words

If you SEE a word, but too long to display in console window of PETS, try
using PAGE and .. to freeze the display before returning back to PETS.

     page see words ..

.. uses KEY DROP
KEY for waiting one character pressed, and DROP to discard the characters.
KEY return the ASCII code of the character. EMIT display the ASCII code. Try
this:

     key .
     65 emit

ASCII code of A is 65, while 97 is ASCII code of a (lowercase). There is
ASCII.PET in sample directory to display ASCII table. BYE from PETS, compile
ASCII.PET, run it, then back to PETS.

     bye
     pet4th fload sample\ascii.pet
     ascii
     pets

Sometimes we need temporary space but do not want to create another variable.
In that case, we can try to use Return Stack. Be careful that no data can be
left in Return Stack when EXITing the definition like this:
: abc 5 >r ;
>R store top of Data Stack to top of Return Stack, R> back to Data Stack,
while R@ do not discard the data still in Return Stack.

Usually we use base 10. If we want to display in base 2 unsigned:
2 base ! u.
but then the base is changed forever. We can use DECIMAL inside a definition
since we are not sure what is the previous BASE:
2 base ! u. decimal

     : bin. base @ >r 2 base ! u. r> base ! ;
     10 bin.


To see data in Return Stack, use .PETS in the source:

     : scr base @ >r .pets key drop 2 base ! u. r> base ! ;
     5 bin.

To print to prn port, use >PRN and to stop printing use >CON
Connect the printer:

     : scr >prn ." This is printed" >con ;
     scr

To send all characters to file use >FILE
s" scr.txt" >file
will create file SCR.TXT, or write over SCR.TXT:

     : scr s" scr.txt" >file words >con ;
     scr
     bye
     type scr.txt
     pets

To append, use >>FILE

     : scr s" scr.txt" >>file order >con ;
     scr
     bye
     type scr.txt
     pets

To capture all characters, use >MEMORY and to get memory use MEMORY@:
pad 80 >memory
to reserved maximum 80 characters in PAD

     : scr pad 80 >memory pi f. >con ;
     intmode scr
     memory@ type

You can see PI in Scratch Area and in console window.

All words that build Forth is visible and can be used by a programmer. Though
some of them can be used, they must be used with extra care.
Chapter 7 Sample Files

MEMMAP.PET
MEMMAP shows memory map of PET4TH, the source code shows how to get the
pointers.

MODEL.PET
PET4TH is build on Intel x86 processor, so that all PET4TH's registers are
simulated. MODEL.PET shows the virtual machine
-    Instruction Pointer
-    Data Stack Pointer
-    Return Stack Pointer
-    PAD, a user buffer
and the current contents. And the source code shows how to get those.

DICMAP.PET
DICMAP shows dictionary of PET4TH, the source code shows to get the pointers.

TYPE.PET
TYPE shows how to do simple file read.

USER.PET
USER shows how to access user variable and execution vector.

ALLWORDS.PET
ALLWORDS shows how to search all words available in all word lists. Use >FILE
to send the output the file.

SEARWORD.PET
SEARWORD shows how to search words in all searched wordlists.

PROMPT.PET
PROMPT shows how to customize prompt by changing execution vector, and how to
do it permanently as default.

ERRNO.PET
PET4TH is a COM program, and designed for small system. To save space, error
is given as a number. To change error number to verbal, use ERRNO.

ALTDIC.PET
Currently PET4TH uses 48KB, upper memory for name area, lower memory for code
area. The memory between 48KB to 64KB can be used for alternate dictionary.
It can be used for assembler and other temporary definitions which is not
saved with the compiler.

TYPEBLK.PET
PET4TH does not support BLOCK. TYPEBLK is used to see Forth 79, Forth 83
source codes (*.FTH *.SRC). It is a simple program, it can be modified
easily. To get text file, simply redirect to file
     typeblk <filename1> > <filename2>

CPUID.PET
CPUID shows Intel cpu id. PET4TH words other than floating-point words use 86
codes so that hopefully PET4TH can run on an PC XT. But floating-point words
may use 486 and above codes. CPUID shows how to detect processor and how to
extract processor serial number.

CHECKF.PET
CHECKF will display crc-32 of a file, or add crc-32 to a file. This is to
make sure that the file has not been tampered. Usually the target is COM
file. You may add crc-32 to a file so that CHECKF will return FFFFFFFF.
Unlike TYPE.PET which read file by character, CHECKF.PET read file by a block
of 16,384 bytes.
     checkf <filename>
     checkf <filename> addcrc

BASE64.PET
Some communication systems may not transmit or receive 8 bit characters. This
include codes. Before sending, convert 8 bit character or code to 6 bit
character called base64 character. After receiving, convert back to 8 bit
character or code.
     base64 <filename1> encode > <filename2>
     base64 <filename1> decode > <filename2>

CREATEF.PET
CREATEF will create a file for test purpose.
     createf <filename> <size>

CHECKF2.PET
This is sample coding of SHA-1 message digest on PET4TH.

TIMER.PET
Stop watch program for 16 events. This program shows how to use MS55 .

COMMENT.PET
This program will extract line which start with \ and <space> for glossary of
used definition of a source program. Therefore it is suggested that each word
definition start with
     \ word ( stack comment )
     \     information of this word
If there is comment that is not expected to show up in the glossary, use
<tab> instead of <space>, \ <tab> ...

COMMENTA.PET
This program will extract line which start with ; and <space> for glossary of
used definition of a source program. Therefore it is suggested that each word
definition start with
     ; word ( stack comment )
     ;     information of this word
If there is comment that is not expected to show up in the glossary, use
<tab> instead of <space>, ; <tab> ...

CALC.PET
The full features calculator is PETCALC.PET . CALC.PET is the base of
PETCALC.PET and is supplied here so that you can make your own calculator,
starting from this CALC.PET . This is also a sample on how to use floating-
point of PET4TH, especially >fs fs> >fs2 fs2> ffree7 .

WC.PET
This program display words in a file and calculate word count. Word count is
double integer.

F94WORDS.PET
This utility will display unavailable standard words in PET4TH and also
available standard words in PET4TH.

TYPELINE.PET
Like TYPE but line by line.

CTRLC.PET
Sample control-c handler.
Chapter 8 Assembly Language

Forth is easier to use than assembler. But assembly language is faster than
Forth. Sometimes we have to use an assembler.

Current version of PET4TH does not have assembler. To define a word in
assembler, we must use a separate assembler. The following example is for
MASM v4.

================================================================
; native code preparation (dw2.asm)
; registers used in pet4th CS DS SS SI SP BP

; iseven  ( x -- flag )
;     return true even parity
iseven:
     pop  bx        ; get tos
     xor  ax,ax          ; assume false
     or   bx,bx          ; check parity
     jpo  iseven9        ; odd parity
     dec  ax        ; true
iseven9:
     push ax        ; push flag
     lodsw               ; next word
     jmp  ax
even                ; word aligned
     end
;===End of DW2.ASM===
================================================================

Please note that the last two instructions are
lodsw
jmp ax

Don't forget assembler directives
even
end

The result should be in COM format, either directly to COM or
from OBJ using EXE2BIN. Suppose the result is DW2.COM. 

     masm dw2,,nul,nul
     link dw2,,nul,;
     exe2bin dw2.exe dw2.com

To ease entering DW2.COM to PET4TH, use DW3.COM. This is DW3.PET.

================================================================
\ (dw3.pet)
\ pet4th fload dw3.pet

cr
.( ...compiling dw3.pet producing dw3.com )

: dos_to_tib
    $80 count dup #tib ! tib swap cmove 0 >in ! ;

\ >std  ( n -- )
\     redirect emit to handle n, 1 ~ 4
: >std
    [ ' file! 3 cells + ] literal !
    [ ' file! ] literal 'emit ! ;

\ >stdout
\     redirect emit to stdout
: >stdout
    1 >std ;

\ >stderr
\     redirect emit to stderr
: >stderr
    2 >std ;

\ >stdaux
\     redirect emit to stdaux
: >stdaux
    3 >std ;

\ >stdprn
\     redirect emit to stdprn
: >stdprn
    4 >std ;

80 svariable filename

: main                   \ copyright notice
    >stderr ." DW3      v01.02     \ Petrus Prawirodidjojo"
    .forth2 cr >stdout        \ *** 1000 ms
    dos_to_tib           \ get filename
    bl word count filename s! \ copy to filename
    0 filename s@ + c!        \ null delimited
    ." dw native "
    filename s@ r/o open-file \ ( file-id ior )
    if drop else
      begin $C000 $2000 2 pick read-file     \ ( file-id u ior )
        0= over 0= 0= and while
        0 do
          i 14 mod 0= if cr space space then
          $C000 i + @
          [char] $ emit w. space [char] , emit space
          2 +loop        \ ( file-id u )
        repeat drop close-file drop
      then cr
    0 bye ;

     mainword main
     fsave dw3.com

unused u. .( bytes free.)
     0 bye
\ end of application, - do not delete -
================================================================

The result of
     dw3 dw2.com > dw
(extract code as dw ... to file dw)
is file DW as follows:

================================================================
dw native 
  $315B , $09C0 , $7BDB , $4801 , $AD50 , $E0FF , 
================================================================

This DW can be inserted into the application, then edit the word name.

================================================================
\ iseven  ( x -- flag )
\     return true even parity
dw iseven
  $315B , $09C0 , $7BDB , $4801 , $AD50 , $E0FF , 
================================================================

Done.
Chapter 9 Source Style

There are many styles for Forth source program. Here I would explain my
preference.

1    \ .flag  ( flag -- )
2    \     display flag literally
3    : .flag
4        if              \ true
5          ." true "
6        else            \ false
7          ." false "
8          then ;

In line 1, <space> follows \ so that this line will be included in glossary
generated by COMMENT.PET program. Line 2 is also included. If the line start
with \ followed by <tab>, it will not be included in the glossary.

Forth programs basically consist of words. It is important to comment each
word, what are inputs and what are outputs, also the function of each word.
The first comment give information on the input and output, and the second
comment explain the function. It is useful to provide a glossary, which lists
all words used, its arguments and result and the function. To generate a
glossary automatically we can make use the white space.

One word and another is separated by at least one white space which is a
space or a tab or other undisplayable character. <\> followed by <white
space> signals start of comment for Forth.

COMMENT.PET will collect comments of the form:
<\><space><the rest of the line>
and ignore
<\><other than space> and others
to produce a glossary.
<\> must be the first character on the line.

Use <tab> as a white space to prevent the comment appear in a glossary, like
\    this will not be included in a glossary.

Between word name and stack comment, there are 2 spaces. In the second line
there are 5 spaces following \ .

Indenting is done by increment of two columns. I of 'if' is below l of
'.flag'. And . of '."' is below space after 'if'.

A block is represented by one line and several indented lines:

     xxxx
       xxxx
       xxxx
       xxxx

IF ELSE THEN block is written like this:

     xxxx if
       xxxx
       xxxx
     else
       xxxx
       xxxx
       then

Comment \ starts in column 33 if possible. Avoid line with length of more
than 76.
Chapter 10 Let's Try

Run PET4TH:
PET4TH for DOS version 01.00.0014  \ (C) 1998-2004 Petrus Prawirodidjojo
_

Type: words<cr>
 sys env? memseg cmdbuffer (shell") shparmb dosexec ascii sasc asc1f model
.caller .nextname memmap dicmap .wbody nointro forget see newapp anew

Type: order<cr>
Context: application fpvoc forth
Current: application 

All those words are in application vocabulary. forth contains PET4TH words
except floating-point words which reside in fpvoc.

Words in application are options, they can be discarded to make room for new
words.

sys for calling DOS. Example:
Type: sys dir /w
to see files in the current directory, listed wide.

The last two parts of PET4TH is userappl.pet and learning.pet, the source is
included.

Type: nointro
to discard learning words. To make more room, start an application with:
     anew newapp
just like userappl.pet

Rerun PET4TH
     bye
     pet4th
Then type
     unused .
to display number of bytes of available memory.
Then type
     nointro unused .
Then type
     anew newapp unused .

PET4TH consists of 7 parts:
1.   pet4th.asm
2.   pet4th.pet
3.   pet4th02.pet
4.   floating.pet
5.   defaappl.pet
6.   userappl.pet
7.   learning.pet
Most words except floating-point words in floating.pet can run on 8088.
Floating-point words may need 80387 and above. If 80387 is not available, use
PI, integer PET4TH which does not have floating.pet.

Don't forget to try: 1.0 3.0 f/ 3.0 f* 1.0 f- f.<cr> which produces 0, since
the calculation uses more digit than display. Actually it is less than 1.0E-
18 ( < 0.000000000000000001 ).
Appendix A Spesification
Input

Number input:
Signed Integer
     from -32768            -32767            to 32767
     from $8000             $8001             to $7FFF       (hexadecimal)
     from #-32768           #-32767           to #32767          (decimal)
     from %1000000000000000 %1000000000000001 to %111111111111111 (binary)
Unsigned Integer (u)
     from 0  to 65535
     from $0 to $FFFF             (hexadecimal)
     from #0 to #65535            (decimal)
     from %0 to %1111111111111111 (binary)
Signed Double Integer (d)
     from -2147483648.  to 2147483647.
     from $80000000.    to $7FFFFFFF.          (hexadecimal)
     from #-2147483648. to #2147483647.        (decimal)
     from %10000000000000000000000000000000.
          to %1111111111111111111111111111111. (binary)
     Note that decimal point as the last character is mandatory.
Unsigned Double Integer (ud)
     from 0.  to 4294967295.
     from $0. to $FFFFFFFF.                         (hexadecimal)
     from #0. to #4294967295.                       (decimal)
     from %0. to %11111111111111111111111111111111. (binary)
     Note that decimal point as the last character is mandatory.
Floating-point (f) approximately
     from -1.23456789012345678e4931 to -1.23456789012345678e-4915
     (negative)
     from 1.23456789012345678e-4915 to 1.23456789012345678e4931
     (positive)
     Uppercase 'E' can also be used for exponent.
     Note that decimal point is mandatory, and it may not be the last
     character.
