BASIC to Forth Howto  (b2f.lp)

Why I write this and for whom this Howto?

I learnt Fortran in 1976, Assembly in 1978, BASIC in 1980, Pascal in 1982
then dBase/Clipper. I like calculators a lot, especially RPN, kind of
expert. I always try ((1/3)*3)-1 on every calculator I do find.

I ever heard Forth in 1983 but as soon as I realized that Forth does not
have floating-point (at that time), I gave up. I like floating-point. Then
in 1998 I came across Forth article. Internet is available this time, and
I can find several Forth systems. But the manuals do not suit me. Either
too easy, discussing RPN. Or too hard, discussing POSTPONE. People advise
Starting Forth and Thinking Forth in comp.lang.forth, but even if I can
get them (no I do not get them), they are not current ANS Forth 94
standard.

There are many Forth systems, manuals and tutorials out there. Considering
my previous experience, some Forths are big that I do not know where to
start, or in C (I am not used to it), or in Linux (just trying), or in
Windows (too many Windows API functions). 

Fortunately I find small Forth in DOS written in assembly language (Bill
Muench and C. H. Ting). This one is comprehensible. I learn Forth by
building a new Forth based on this eForth. Yes, PET4TH is at last usable.

I make every effort so that PET4TH will be easily understood by a newbie,
for example SEE will display the structure of a word. Unfortunately, I am
still not a traditional Forth user. Forth is a powerful language, there
are so many ways to solve a problem in Forth. So which one is Forth way?

I believe Forth is a flexible language. Though at first I must stick to
ANS Forth 94 standard to learn it, later I can use whichever way I like to
solve a problem. For some people, even ANS Forth 94 standard is viewed as
limiting the power of Forth.

I write this Howto to help BASIC users speed up learning Forth. Yet, I
will not make it long so as not to build up habit of using Forth in BASIC
way of thinking. Just a simple explanation to start using Forth. And I
hope, you will forget this Howto once you learn more from other articles.
Since surely this is not the Forth way of doing programming :).

Variables

BASIC keeps value in variable. There are several different variables, a
floating point variable (default), a string variable which suffixed by $
like A$, and others like integer variable suffixed by % like A%. The
suffix differentiates these variables:
! single precision
# double precision
$ string
% integer
& long integer.
We look at just floating-point, string and integer numbers.

In Forth, variables are determined when they are created. And the variable
specifies the location, not the value. To get the value you must fetch it.
So for floating point variable, use fvariable to define it.
fvariable abc  \ define floating point variable named abc
abc f@         \ using f@ to fetch the value
f.             \ to display it.
abc f?         \ display abc, same as abc f@ f.
3.14E0 abc f!  \ store 3.14 to abc
f? is not ANS Forth 94 standard, later we will define it.
So the prefix f means floating point, to variables and also to commands.

String variable is not in the ANS Forth 94 standard. So to speed up
learning, this time we define svariable, s@ and s? which is s@ and s.
combined.
20 svariable sss    \ max. length of sss is 20 characters
sss s?         \ display sss

Variable in ANS Forth 94 standard is integer variable, 16 bit for 16 bit
system, ranging from -32768 to 32767.
variable index \ define integet variable named index
index ?        \ display index, or index @ .

Forth number default is integer, 16 bit for 16 bit system. Operators like
+, -, * and / are all for integer. There are f+, f-, f* and f/ for
floating point number. But there are no such thing as s+, sleft, smid,
sright for string. Again, we have to define them, and it is not Forth way
of programming.

BASIC users do
(LET) A = B * C + 5      \ calculate
PRINT A                  \ print
But Forth users do not like to use variables. Maybe they are proud if they
do not use even one variable in their program :). Forth do calculation in
RPN with numbers in the stack.
varb f@ varc f@ f* 5.0E0 f+   \ calculate
f.                       \ print directly
It is like PRINT B * C + 5. Notice that Forth default is integer, so we
must specify floating point according to rule, that is 5 must be expressed
as 5.0E0 .

Numbers

PET4TH uses simpler rule for numbers. If the function or operator is only
for floating point, we may use just 5 for floating point. Otherwise:
-    number which do not have decimal point is integer, like 5
-    else if the decimal point is the last character, it is double integer
     (32 bit), like 5.
-    else if the decimal point is not the last character, it is floating
     point number, like 5.0

Functions and operators naming

Likewise we can define functions or operators for other types. To be
consistent, we can use
prefix s for string
prefix f for floating point
prefix d for double integer.
And prefix i or no prefix for integer.

INPUT

So we define inputf for floating point, inputs for string and inputi for
integer. But I do not define all kinds of operators or functions. Please
use Forth for others or you may provide it as an exercise.

\ INPUT X                inputf  ( a-addr -- )
\     input floating point variable, non standard
: inputf
    pad 64 accept pad swap >float if f! else 0.0e0 f! then ;
fvariable varx           \ define varx
varx inputf              \ input varx

\ INPUT A$               inputs  ( a-addr n -- )
\     input counted string variable, non standard
: inputs
    pad swap accept pad swap rot s! ;
20 svariable vara        \ define vara, assume max. length 20
vara 20 inputs           \ input vara, max. length 20

\ INPUT I%               inputi  ( a-addr -- )
\     input integer variable, non standard
: inputi
    0. pad 6 accept      \ 11 for 32 bit, 6 for 16 bit
    pad swap >number 2drop drop swap ! ;

BASIC has automatic variable definition, we can start with INPUT A$. If
variable A$ is not defined yet, it will automatically created. But Forth
needs you to define the variable first before using it (although we can
implement automatic variable creation).

Old BASIC limits variable names to 2 characters like A$, N1 etc. We can
also do that in Forth, but we will waste the short names that we can use
for other important functions. It is better to use longer names like varn
or varn$.

As with inputf, inputs and inputi (INPUT), we can also define printf,
prints and printi (PRINT), but better use the Forth way, that is f?, s?
and ?.

Let's look at BASIC sample and Forth interpreter conversion
\ BASIC PROGRAMMING 2nd edition
\ by John G. Kemeny and Thomas E. Kurtz p. 13
\ 100 PRINT "FAHRENHEIT";
\ 110 INPUT F
\ 120 LET C = (F-32)*5/9
\ 130 PRINT "CENTIGRADE:" C
\ 140 PRINT
\ 150 GOTO 100
\ 160 END


fvariable varf           \ define variable varf
fvariable varc           \ define variable varc
.( FAHRENHEIT )               \ line 100
varf inputf cr           \ line 110
varf f@ 32.0 f- 5.0 f* 9.0 f/ varc f!   \ line 120
.( CENTIGRADE: ) varc f? \ line 130
cr                  \ line 140


Line 150 and 160 is ignored for this sample. The above program B2F013.PET
is executed when we compile it. Before we can try B2F013.PET, we must
create special version of PET4TH by typing
p fload b2f.pet
and we get B2F.COM. B2F.PET is BASIC layer for PET4TH.
Now type the following:
b2f
fload b2f013.pet
Notice, we must call b2f first. To exit, type:
bye

Now we will see the other version B2F013B.PET, which will be executed when
the program is run. Type
b2f fload b2f013b.pet
b2f013
You must press break (control-C) to stop the program. Type
b2f
bye
to see the cursor again.

\ BASIC PROGRAMMING 2nd edition
\ by John G. Kemeny and Thomas E. Kurtz p. 13
\ 100 PRINT "FAHRENHEIT";
\ 110 INPUT F
\ 120 LET C = (F-32)*5/9
\ 130 PRINT "CENTIGRADE:" C
\ 140 PRINT
\ 150 GOTO 100
\ 160 END


fvariable varf           \ define variable varf
fvariable varc           \ define variable varc

: ftoc                   \ define function ftoc
    ." FAHRENHEIT "      \ line 100
    varf inputf cr       \ line 110
    varf f@ 32.0 f- 5.0 f* 9.0 f/ varc f!    \ line 120
    ." CENTIGRADE: " varc f?  \ line 130
    cr ;            \ line 140

: main                   \ main program
    begin ftoc again          \ line 150, press break to exit
    0 bye ;

mainword main            \ execute main upon running
turnkey b2f013.com       \ save without dictionary


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

\ is REM for Forth.

Now we will see how to implement DATA and READ.

\ DATA                   dataptr
\     data pointer variable, non standard
variable dataptr

\ newdata  ( "<spaces>name" -- )
\     start a new data set, non standard
: newdata
    create does> dataptr ! ;

\ DATA X                 f,  ( F: r -- )
\     store r in code area, non standard
: f,
    here 10 allot f! ;        \ 10 for temp, 8 for double, 4 for single

\ DATA N$                s,  ( c-addr u -- )
\     store string in code area as counted string, non standard
: s,
    here over 1+ allot s! ;

\ DATA I%                i,  ( n -- )
\     store integer in code area, non standard
: i,
    here 2 allot ! ;          \ 4 for 32 bit, 2 for 16 bit

\ READ X                 readf  ( F: -- r )
\     read floating point from code area, non standard
: readf
    dataptr @ f@ 10 dataptr +! ;   \ 10, 8 or 4

\ READ N$                reads  ( -- c-addr u )
\     read string from code area, non standard
: reads
    dataptr @ count dup 1+ dataptr +! ;

\ READ I%                readi  ( -- x )
\     read integer from code area, non standard
: readi
    dataptr @ @ 2 dataptr +! ;     \ 4 for 32bit or 2 for 16bit

And this is the sample B2F005.PET:
\ BASIC PROGRAMMING 2nd edition
\ by John G. Kemeny and Thomas E. Kurtz p. 5
\ 100 READ N, D
\ 200 LET Q = N/D
\ 300 PRINT N, D, Q
\ 400 DATA 147, 69
\ 500 END
\ RUN
\ 147 69 2.13043


fvariable varn
fvariable vard
fvariable varq
newdata data1 147.0 f, 69.0 f,          \ line 400
data1 readf varn f! readf vard f!  \ line 100
varn f@ vard f@ f/ varq f!         \ line 200
cr varn f? vard f? varq f?         \ line 300


Type:
b2f
fload b2f005.pet
bye

And for the last sample, FOR NEXT loop. This time we write in Forth way,
not BASIC in Forth!
\ BASIC PROGRAMMING 2nd edition
\ by John G. Kemeny and Thomas E. Kurtz p. 16
\ 10 FOR I = 1 TO 5
\ 20 PRINT I, I*I, I^3
\ 30 NEXT I
\ 40 END


: square_and_cube        \ display square and cube
    6 1 do               \ loop from 1 to 5
      i int>float fdup f.          \ display i
      fdup fdup f* f.         \ display i*i
      3.0 f** f. cr      \ display i^3
      loop
    0 bye ;              \ exit and end of definition

mainword square_and_cube \ execute upon running
turnkey b2f016.com       \ save without dictionary


Type:
b2f fload b2f016.pet
b2f016

Look at how we calculate without even using one variable. int>float is not
ANS Forth 94 standard, to convert from integer to floating point. bye of
PET4TH is not the same as BYE from the standard, 0 is the value returned
to DOS. fsave is SAVE and also not standard, though used by many Forth.
Also 'boot.

This Howto is not complete, and you can not start learning Forth with this
Howto too. This is just a supplement. And remember, except for the last
sample, this is not the way Forthers do programming.

2002-03-07
Petrus Prawirodidjojo <petrusp_id@yahoo.com>
http://www.geocities.com/petrusp_idGlossary

---BASIC layer for ANS Forth 94 by Petrus Prawirodidjojo---
s" b2f.ans" included
Uppercase word indicates standard word, it is already defined. Use it as
lowercase word. Standard words are listed here to see the completeness.

FVARIABLE  ( "<spaces>name" -- ) name: ( -- f-addr ) 12.6.1.1630 
    define floating-point variable
F@  ( f-addr -- ) ( F: -- r ) 12.6.1.1472
    fetch fvariable
F.  ( F: r -- ) 12.6.2.1427
    display floating-point number
F!  ( f-addr -- ) ( F: r -- ) 12.6.1.1400
    store to fvariable
f?  ( f-addr -- )
    fetch and display fvariable, non standard
svariable  ( u -- )
    define counted string variable with max. length u, non standard
s@  ( a-addr -- c-addr u )
    fetch svariable, non standard
s.  ( c-addr u -- )
    display string, non standard
s!  ( c-addr u a-addr -- )
    store string to a counted string variable, non standard
s?  ( a-addr -- )
    fetch and display svariable, non standard
VARIABLE  ( "<spaces>name" -- ) name: ( -- a-addr ) 6.1.2410
    define integer
@  ( a-addr -- x ) 6.1.0650
    fetch integer variable
.  ( n -- ) 6.1.0180
    display integer number
!  ( x a-addr -- ) 6.1.0010
    store to integer variable
?  ( a-addr -- ) 15.6.1.0600
    fetch and display integer variable
F+  ( F: r1 r2 -- r3 ) 12.6.1.1420
    return r3 where r3 is r1+r2
F-  ( F: r1 r2 -- r3 ) 12.6.1.1425
    return r3 where r3 is r1-r2
F*  ( F: r1 r2 -- r3 ) 12.6.1.1410
    return r3 where r3 is r1*r2
F/  ( F: r1 r2 -- r3 ) 12.6.1.1430
    return r3 where r3 is r1/r2
s+  ( c-addr u a-addr -- )
    append string to a counted string variable, non standard
cs+  ( char a-addr -- )
    append char to counted string variable, non standard
cs-  ( a-addr -- char )
    remove last char from a counted string, non standard
+  ( n1|u1 n2|u2 -- n3|u3 ) 6.1.0120
    return n3 where n3 is n1+n2
-  ( n1|u1 n2|u2 -- n3|u3 ) 6.1.0160
    return n3 where n3 is n1-n2
*  ( n1|u1 n2|u2 -- n3|u3 ) 6.1.0090
    return n3 where n3 is n1*n2
/  ( n1 n2 -- n3 ) 6.1.0230
    return n3 where n3 is n1/n2
inputf  ( a-addr -- )
    input floating point variable, non standard
inputs  ( a-addr n -- )
    input counted string variable, non standard
inputi  ( a-addr -- )
    input integer variable, non standard
int>float  ( n -- ) ( F: -- r )
    convert integer to floating-point, non standard
float>int  ( -- n ) ( F: r -- )
    convert floating point to integer, non standard
float>string  ( -- c-addr u ) ( F: r -- )
    convert floating-point to string, non standard
string>float  ( c-addr u -- ) ( F: -- r )
    convert string to floating-point, non standard
F<  ( -- flag ) ( F: r1 r2 -- ) 12.6.1.1460
    return true if r1 is less than r2
f=  ( -- flag ) ( F: r1 r2 -- )
    return true if r1 equal r2, non standard
f>  ( -- flag ) ( F: r1 r2 -- )
    return true if r1 is greater than r2, non standard
f<=  ( -- flag ) ( F: r1 r2 -- )
    return true if r1 is less or equal than r2, non standard
f<>  ( -- flag ) ( F: r1 r2 -- )
    return true if r1 not equal to r2, non standard
f>=  ( -- flag ) ( F: r1 r2 -- )
    return true if r1 is greater or equal than r2, non standard
s<  ( c-addr1 u1 c-addr2 u2 -- flag )
    return true if s1 before s2, non standard
s=  ( c-addr1 u1 c-addr2 u2 -- flag )
    return true if s1 = s2, non standard
s>  ( c-addr1 u1 c-addr2 u2 -- flag )
    return true if s1 after s2, non standard
s<=  ( c-addr1 u1 c-addr2 u2 -- flag )
    return true if s1 before or equal s2, non standard
s<>  ( c-addr1 u1 c-addr2 u2 -- flag )
    return true if s1 not equal to s2, non standard
s>=  ( c-addr1 u1 c-addr2 u2 -- flag )
    return true if s1 after or equal s2, non standard
<  ( n1 n2 -- flag ) 6.1.0480
    return true if n1 less than n2
=  ( x1 x2 -- flag ) 6.1.0530
    return true if x1 equal x2
>  ( n1 n2 -- flag ) 6.1.0540
    return true if n1 greater than n2
<=  ( n1 n2 -- flag )
    return true if n1 less than or equal n2, non standard
<>  ( n1 n2 -- flag )
    return true if n1 not equal n2, non standard
>=  ( n1 n2 -- flag )
    return true if n1 greater than or equal n2, non standard
dataptr
    data pointer variable, non standard
newdata  ( "<spaces>name" -- )
    start a new data set, non standard
f,  ( F: r -- )
    store r in code area, non standard
s,  ( c-addr u -- )
    store string in code area as counted string, non standard
i,  ( n -- )
    store integer in code area, non standard
readf  ( F: -- r )
    read floating point from code area, non standard
reads  ( -- c-addr u )
    read string from code area, non standard
readi  ( -- x )
    read integer from code area, non standard
CR  ( -- ) 6.1.0990
    newline
fload  ( "<spaces>name" -- )
    load source file, non standard
fsave  ( "<spaces>name" -- )
    save new system, non standard
mainword  ( "<spaces>name" -- )
    execute name when run, non standard
turnkey  ( "<spaces>name" -- )
    save application, set mainword first, non standard
BYE  ( -- ) 15.6.2.0830
    exit from Forth
BASIC PROGRAMMING 2nd edition
by John G. Kemeny and Thomas E. Kurtz inner cover

1. Elementary BASIC

INPUT A$, X
READ X, Y1, M(J+2,3), N$
DATA -1, 2.07, 31416E-4, 127829, JONES
PRINT "ANSWER = "; X, A*B, N$
LET X2=X+Y^2
GO TO 175
IF T(I,J)<=25 THEN 175
FOR N=10 TO 1 STEP -1
NEXT N
END

2. Advanced BASIC

INPUT X, Y4, Z
LINPUT A$
DEF FNG(X)=2*SIN(X)*EXP(-X)
FNEND
GOSUB 800
RETURN
RESTORE
REM BEGINNING OF SUBROUTINE
DIM A(12), B(3,5)
STOP
CHANGE A$ TO V
ON X+Z GO TO 200, 400, 700
RANDOMIZE

3. File instructions

FILES DATA; *
FILE #2: N$
INPUT #1: X, A$, Y
PRINT #1: A, B, C
READ #2: X, Y(1), Y(2)
WRITE #2: R, S+T
RESET #2: LOC(2) - 1
SCRATCH #3

4. Matrix instructions

MAT INPUT V
MAT READ Z(M,N)
MAT PRINT A
MAT C = A + B
MAT C = A - B
MAT C = A * B
MAT C = (COS(X)) * A
MAT C = INV(A)
MAT C = TRN(A)
MAT C = ZER
MAT C = CON(15)
MAT C = IDN

5. Notes

Variables      X, Y7, A, A(X), B(A(X),5), A$, B7$, N$(I),
T$(A,B)
Operations     +, -, *, /, ^
Relations      <, <=, =, >, >=, <>
Functions      SQR, SIN, COS, TAN, ATN, LOG, EXP, ABS, SGN,
INT, RND, ASC, LOC, LOF, NUM, TAB
