
Heute möchte ich mit einem kleinen, mehrteiligen Kurs beginnen, der die Grundlagen von FORTH sowie die Systemprogrammierung unter FORTH (GEMDOS, BIOS, GEM, VDI) beleuchtet.
Dieser Ausgabe der ATOS liegt ein komplettes FORTH-System bei, mit dem Sie die Beispiele im Kursus sogleich ausprobieren können. Sie können den Ordner an einen Ihnen genehme Stelle kopieren. Dort sollte sich dann folgende Ordnerstruktur ergeben:
\FORTH forth.ini forth.tos readme \FORTH\BIN bios.bin dsp.bin gemdos.bin mint.bin vt52.bin xbios.bin \FORTH\SOURCE date.4th files.4th heap.4th hello.4th iconify.4th sieve.4th \FORTH\SYSTEM command.4th copy.4th extend.4th util.4th \FORTH\GUIDES forth.hyp
mForth Version 0.95                                            
                                                               
(c) Rainer Saric (1995,96)                                     
(c) Joerg Plewe  (1988)                                        
                                                               
                                                               
Loading: FORTH.INI                                             
   Loading: system\command.4th                                 
                                                               
Command set loaded; Vocabulary: commands                       
available commands:                                            
                                                               
dir del rd md cd ls pwd p: l: k: j: i: h: g: f: e: d: c: b:    
a: rename DTA nil                                              
23 words in dictionary.                                        
                                                               
   Loading: system\util.4th                                    
   Loading: system\extend.4th                                  
                                                               
Current Directory: \FORTH\SOURCE                               
   Free ST memory: 1550028  bytes                              
                                                               
NVDI Version: 4.11 Date: 16.03.1996                            
Geneva cookie is set to: 872218                                
                                                               
Transient: command                                             
  Current: forth                                               
  Context: command forth gem vdi only                          
                                                               
& 0 ok>                                                 
Betrachten wir zunächst die letzte Zeile.
Das Zeichen & zeigt uns die augenblickliche Zahlenbasis (Decimal) an. 0 gibt die Anzahl der auf dem Stack befindlichen Werte bekannt und ok> bedeutet, daß das System von Ihnen eine Eingabe erwartet.
Hinweis: FORTH-Befehle (Worte) müssen durch mindestens ein Leerzeichen getrennt werden.Tippen Sie words und anschließend drücken Sie Return (ab jetzt: <cr>). Das Fenster (oder der ganze Bildschirm) wird mit vielen Wörtern gefüllt. Aber keine Angst, zu Anfang sind nicht alle Worte nötig, um einfache Programme zu schreiben.
Hinweis: Sie verlassen FORTH, indem Sie bye eingeben und <cr> drücken.Bei FORTH handelt es sich um eine stackorientierte Sprache, die sowohl Compiler als auch einen Interpreter zur Verfügung stellt. Ihre Eingabe von vorhin ("words") wurde interpretiert.
FORTH gehört ebenso zu den 'typenlosen' Sprachen, d.h. der Stackwert kann z.B. als Adresse, als Zahl oder ähnliches interpretiert werden. Letztendlich entscheidet der Programmierer über die Weiterverarbeitung des oder der Werte.
FORTH besitzt einen Datenstack und einen Returnstack, die beide nach dem LIFO-Prinzip arbeiten (LIFO = Last In First Out). Zu Verdeutlichung des Prinzips führen wir nun ein paar einfache Rechnungen durch.
Auf dem Stack sieht es jetzt so aus:
| Stack | 
| 1 | 
Geben Sie nun noch z.B. eine 4 ein
| Stack | 
| 4 | 
| 1 | 
und jetzt + <cr>
| Stack | 
| 5 | 
Ist es Ihnen aufgefallen? FORTH rechnet nach der UPN - oder Postfix-Notation (UPN = Umgekehrte polnische Notation; sie ist durch HP-Taschenrechner sehr bekannt geworden). Im Gegensatz zur Infix-Notation gibt man bei UPN erst die beiden Operanden (Zahlen) und dann erst die gewünschte Operation ein.
1 + 4 . liefert eine Fehlermeldung, da der Operator + zwei Zahlen auf dem Stack erwartet.
Schön, wir wissen was 1 + 4 ist und wir wissen auch, daß es oben auf dem Stack liegt (ToS; Top of Stack), trotzdem wäre es schön, das Ergebnis 'schriftlich' zu haben. Dafür gibt es "dot", genauer . Geben Sie also einen . ein und drücken anschließend <cr>. Voila! Addition ist keine Kunst; doch FORTH kann noch etwas mehr.
Unsere nächste Aufgabe ist schon etwas schwieriger: Die Berechnung von Quadratzahlen!
2 2 * . <cr> Einfach! Oder? Das Problem läßt sich jedoch universeller lösen. Zunächst die Lösung des 'Problems':
: qz dup * . ; <cr>
Aber der Reihe nach.
Das neue Wort im Einsatz:
3 qz <cr>
qz ist jetzt Bestandteil des Forthvokabulars. Geben Sie nochmals words ein. Wenn Sie während der Ausgabe eine Taste drücken, wird die Ausgabe angehalten; bei nochmaligem Drücken wir die Ausgabe fortgesetzt - Escape bricht die Ausgabe ab.
Wort-Typen
Einige mathematische Befehle aus dem FORTH Grundwortschatz:
| WORD | Stack | Beschreibung | 
| + | n1 n2 - n1+n2 | Addition | 
| - | n1 n2 - n1-n2 | Subtraktion | 
| * | n1 n2 - n1*n2 | Multiplikation | 
| / | n1 n2 - n1/n2 | Division | 
| mod | n1 n2 - rest | Rest n1/n2 | 
| /mod | n1 n2 - rest quo | Division mit Rest | 
| */ | n1 n2 n3 - n1*n2/n3 | Mixed Mul/Div | 
| negate | n1 - -n1 | Vorzeichen ändern | 
| abs | n1 - abs(n1) | Absoluter Wert | 
| 1+ | n1 - n1+1 | Add 1 zum Top of Stack | 
| 1- | n1 - n1-1 | Sub 1 zum Top of Stack | 
| 2+ | n1 - n1+2 | Add 2 zum Top of Stack | 
| 2- | n1 - n1-2 | Sub 2 zum Top of Stack | 
| 2* | n1 - n1*2 | Mul mit 2 (schnell) | 
| 2/ | n1 - n1/2 | Div mit 2 (schnell) | 
| : <name> ... Befehlsfolge ... ; | 
Dabei ist <name> des neuen Wortes. Der Doppelpunkt leitet die Kompilation des Wortes ein. Das Semicolon beendet sie wieder, schaltet FORTH vom Compilier- in den Interpretermodus zurück.
| variable <name> | - | Definition Variable | 
"variable" ist ein Definitionswort. Es erzeugt eine Variable, indem <name> ins Wörterbuch eingetragen und Platz für den Wert geschaffen wird (4 Bytes). Die erzeugte Variable ist mit 0 initialisiert.
variable x <cr>
Der Zugriff auf die Variable kann, wie nicht anders zu erwarten war, schreibend oder lesend erfolgen.
Das Lesen der Variable erledigt das Wort @ (fetch): x @ . <cr> 0 Das Schreiben gelingt mit dem Wort ! (store): 123 x ! <cr>
In einer colon-Definition:
: get-x ( -- x ) x @ ;
Speicherbefehle:
| WORD | Stack | Beschreibung | 
| @ | adr - n | Liest 32bit-Wert aus adr aus | 
| ! | n adr - | Schreibt 32bit-Wert in adr | 
| w@ | adr - w | Liest 16bit-Wert aus adr aus | 
| w! | w adr - | Schreibt 16bit-Wert in adr | 
| ( | - | Kommentar bis zur nächsten schließenden Klammer ")" | 
| constant <name> | n - | Definition Konstante | 
"constant" bewirkt einen Wörterbucheintrag von <name>, reserviert Platz und weist der Konstante den auf dem Stack befindlichen Wert zu.
100 constant hundert <cr>
Bei einem Aufruf von hundert wird jetzt sofort die Zahl 100 auf dem Stack abgelegt.
Mit einem kleinen Trick läßt sich der Wert von Kostanten jederzeit ändern - sinnvoll ist dies allerdings nicht.
Die wichtigsten Worte zur Stackmanipulation sind swap, dup, over und drop.
FORTH stellt ein Servicewort zur Verfügung mit dem Sie sich den Inhalt des Stacks komplett ansehen können: .s (dot-s). Als Test geben Sie ein paar Zahlen ein und anschließend .s
Beispiel:
Stack vor Aufruf von swap
| Stack | 
| 1 | 
| 3 | 
Stack nach Aufruf von swap
| Stack | 
| 3 | 
| 1 | 
| WORD | Stack | Beschreibung | 
| .s | - | Inhalt des Stack ausgeben | 
| sp0 | - addr | Startadresse des Stack | 
| sp@ | - addr | Stackpointer-Inhalt vor sp@-Aufruf | 
| sp! | - | Stackpointer initialisieren | 
| depth | - n | Stacktiefe vor Aufruf von depth | 
| over | n1 n2 - n1 n2 n1 | Kopiert 2. Stackwert auf TOS | 
| rot | n1 n2 n3 - n2 n3 n1 | Macht 3. Eintrag zum TOS | 
| drop | n - | Löscht TOS | 
| dup | n - n n | Kopiert TOS | 
| ?dup | n - n n oder n - n | Dupliziert wenn n<>0 | 
| pick | ... n i - n n.i | Kopiert i.ten Eintrag auf TOS | 
Haben sich viele Zahlen auf dem Stack angesammelt, können Sie diese löschen, indem Sie ein unbekanntes Wort eingeben - oder alternativ dot'ten, bis der Stack leer ist.
| A B | A and B | A or B | A xor B | 
| 0 0 | 0 | 0 | 0 | 
| 0 1 | 0 | 1 | 1 | 
| 1 0 | 0 | 1 | 1 | 
| 1 1 | 1 | 1 | 0 | 
Die FORTH-Befehle and, or, xor und not wirken diesen Regeln entsprechend (bei mForth 32bit breit)
Beispiele:
Vergleichsoperatoren mit 0
| WORD | Stack | Beschreibung | 
| 0= | n - flag | -1, wenn n=0 | 
| 0> | n - flag | -1, wenn n>0 | 
| 0< | n - flag | -1, wenn n<0 | 
| 0<> | n - flag | -1, wenn n<>0 | 
| 0>= | n - flag | -1, wenn n>=0 | 
| 0<= | n - flag | -1, wenn n<=0 | 
| WORD | Stack | Beschreibung | 
| = | n1 n2 - flag | -1, wenn n1=n2 | 
| > | n1 n2 - flag | -1, wenn n1>n2 | 
| < | n1 n2 - flag | -1, wenn n1<n2 | 
| <> | n1 n2 - flag | -1, wenn n1<>n2 | 
| >= | n1 n2 - flag | -1, wenn n1>=n2 | 
| <= | n1 n2 - flag | -1, wenn n1<=n2 | 
Beispiele:
| 1 0= . <cr> 0 | 
| 0 0= . <cr> -1 | 
| 1 3 < . <cr> -1 | 
| 3 1 > . <cr> -1 | 
Die bekannteste Entscheidungsstruktur ist sicherlich die if ... else ... endif Struktur.
| if <true-Teil> else <false-Teil> endif | 
| if <true-Teil> endif | 
Die if .. endif-Struktur kann natürlich geschachtelt werden. Bei diesen Verschachtelungen muß jedoch auf folgendes geachtet werden:
if              
   if           
                
   endif        
                
else            
                
   if           
                
   else         
                
   endif        
                
endif           
Ein Wort soll testen, ob die auf dem Stack befindliche Zahl gerade oder ungerade ist:
| : | ?gerade ( n - ) | 
| 1 and /* <>0, wenn Zahl ungerade */ | |
| if ." Zahl war ungerade!" | |
| else ." Zahl war gerade!" | |
| endif ; | 
| WORD | Stack | Beschreibung | 
| /* | - | Ignoriere Text bis */ | 
| ." | - | Kompiliert String ins Wörterbuch | 
Schauen wir uns vorab die zur Verfügung stehenden Worte des VDI an. Geben Sie Sequenz mforth vdi words ein (es sind noch nicht alle VDI-Aufrufe implementiert).
Hier die Erläuterung der einzelnen Befehle:
Zu Beginn müssen wir eine virtuelle Workstation öffnen:
1 1 1 1 1 1 1 1 1 1 2 opnvwk <cr>
Die genaue Bedeutung der Parameter ist im Profibuch nachzulesen (ich möchte an dieser Stelle nicht näher darauf eingehen). opnvwk füllt die Werte in das interne INTIN-array. Die Rückgabewerte befinden sich im INTOUT-array (intin und intout sind im GEM-Vocabular definiert).
Jetzt müssen wir das vdi-handle holen und in der Variablen v_handle sichern:
contrl 12 + w@ v_handle w! <cr>
v_handle sollte > 0 sein. v_handle w@ . <cr>
Wenn Sie FORTH jetzt verlassen, müssen Sie vorher den Befehl clsvwk ausführen! Warum? Sie haben eine virtuelle Workstation geöffnet (opnvwk = open virtual Workstation) und sollten sie jetzt vor dem Verlassen wieder schließen (clsvwk = Close Virtual Workstation). Damit räumt man quasi den Arbeitsplatz, den man vorher eingerichtet hat, wieder auf.
Ein paar Worte zum Ausprobieren:
| WORD | Stack | Beschreibung | 
| line | x y x' y' - | Linie zeichnen von x,y -> x',y' | 
| bar | x y x' y' - | Gefüllte Box zeichnen von x,y -> x',y' | 
| clrwk | - | Workstation löschen | 
| circle | x y radius | Kreis zeichnen | 
| sl_width | width - | Liniendicke ändern | 
| sl_color | color - | Linienfarbe ändern | 
Um Ihnen Tipparbeit zu ersparen, empfehle ich Ihnen, den Text hier im Hypertext zu markieren und in das Clipboard zu kopieren. Scrap.txt kann dann mit jedem ASCII-Editor ediert werden. Speichern Sie den Text unter \.\forth\source\turtle.4th ab.
Sie können das File in FORTH einladen mit der Befehlssequenz:
include \.\forth\source\turtle.4th
So jetzt aber los:
/* -------------
   Turtle-Grafik
   -------------
   Original-Code von R. Zech, angepaßt an mForth
*/
mforth vocabulary graphic /* Eine eigenes Vokabular für die Befehle */
graphic also definitions  /* .. alle Definition ins graphic Voc */
/* x,ycenter sind systemspezifisch, Initialisierung mit ST-Hoch */
integer xcenter   320 to xcenter
integer ycenter   200 to ycenter
2variable center
2variable pix_cur          /* x,y-Position der Kröte */
 variable angle            /* Richtung, in der unsere Kröte kriecht */
create crt_bounds 8 allot  /* Ausmaße des Bildschirms/Fensters */
/* Wir definieren ein paar Farben für die Linien */
variable colors /* Anzahl der Farben */
: color: ( -- <name> )
   create w, does> w@ sl_color ;
0 color: white
1 color: black
2 color: red
3 color: green
4 color: blue
5 color: cyan
6 color: yellow
7 color: magenta
8 color: gray
9 color: darkgray
hide color: white  /* color: aus dem Wörterbuch "entfernen" */
/* Verschieben des Koordinatensystems */
: shift  ( deltay deltay -- )
   negate center 4+ +! center +!
   center 2@ pix_cur 2! ;
/* Turtle bewegen ohne irgendwelche Spuren, sprich Linien, zu
hinterlassen */
: skip-xy ( deltax deltay -- )
   negate pix_cur 4+ +! pix_cur +! ;
/* Absolut setzen */
: goto-xy ( x y -- )
   center 2@ pix_cur 2! skip-xy ;
: home ( -- ) /* Zurück zum Ursprung */
   0 0 goto-xy angle off ;
: new  ( -- )
   xcenter ycenter center 2!
   home black ;
: turn   ( winkel -- ) angle +! ;
: turnto ( winkel -- ) angle  ! ;
/* Der Sinus wird aus einer Tabelle ausgelesen, Genauigkeit 5-Stellen
skaliert mit 10000.
Dies ist die weitaus schnellste Methode. Die Genauigkeit reicht für
die meisten Anwendungen aus. So habe ich, mit der um 3D-Funktionen
erweiterten Turtlegrafik, Drahtmodelle chem. Verbindungen in hoher
Geschwindigkeit drehen können */
: skip ( radius -- )
   dup  angle @ cos * 10000 /
   swap angle @ sin * 10000 / swap skip-xy ;
: goto ( radius winkel -- )
   angle ! center 2@ pix_cur 2! skip ;
: dot   ( -- ) /* Punkt an Turtleposition zeichnen */
   pix_cur 2@ 2dup line ;
: draw  ( radius -- )  /* Eine Linie mit dem Radius radius zeichnen */
   >r pix_cur 2@ r> skip pix_cur 2@ line ;
/* Initialisieren, init muß(!) vor dem ersten draw-Befehl aufgerufen
werden */
: init ( -- )
   1 1 1 1 1 1 1 1 1 1 2 opnvwk /* Virtuelle Workstation öffnen */
   contrl 12 + w@ v_handle w!   /* Handle holen und sichern */
   intout 26 + w@ colors !      /* Anzahl der Farben */
   0 0 intout 2w@ crt_bounds 4w! /* Ausmaße des Bildschirms */
   intout 2w@ 2/ to ycenter      /* Center neu */
              2/ to xcenter ;
/* Aufrufen, bevor FORTH verlassen wird */
: xinit ( -- )  clsvwk ;
mforth
bload vt52.bin >voc vt52
vt52 also graphic also
/* Eine Box mit der Turtle zeichnen */
: BOX ( laenge breite -- )
   over [ graphic ] draw 90 turn dup draw
   90 turn swap draw 90 turn draw
   -270 turn ; /* Turtle wieder zurück */
: wonder ( -- )
   new 45 turnto
   200 0 do i draw 90 turn
            i 7 mod sl_color
         loop black ;
: stern  ( -- )
   361 0 do 100 draw 0 0 goto-xy i turnto loop ;
: demo   ( -- )
   init new clrwk
   [ vt52 ] home ." Eine Box"
   100 100 BOX key drop
   clrwk [ vt52 ] home ." Ein Stern"
   new stern   key drop
   clrwk [ vt52 ] home ." I wonder"
   new wonder  key drop
   cr ." Vor Verlassen des FORTH-Systems bitte xinit aufrufen!" ;
Ich wünsche Ihnen viel Spaß beim Experimentieren.
| Feldname | Länge in Byte | 
| Link Field | 4 | 
| Word Parameter Field | 4 | 
| Code Field Adress | 4 | 
| Name Field | >= 4 | 
| Code Len Field | 2 | 
| Parameter Field | >= 2 | 
Anrufen (06431-71188 ab 20:00 Uhr) oder schreiben.
EMail: Maus-Adresse von Rainer Saric