ATOS - Around The Operating System Das ATOS-Magazin 5/96

ATOS Programmierpraxis Datenstrukturen ATOS Hilfe

Forth-Kurs





In einer früheren Ausgabe der ATOS wurde Ihnen die Sprache FORTH kurz vorgestellt.

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   




Welche Systemvoraussetzungen sind nötig?

Welche Systemvoraussetzungen müssen erfüllt sein, damit FORTH läuft?




Programmstart

Starten Sie FORTH.TOS (Im Ordner FORTH). Beim Start wird die Datei forth.ini nachgeladen.

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.




Rechnen in FORTH

Tippen Sie 1 <cr>, der Forth-Prompt hat sich nun verändert: & 1 OK>.

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.

:qz löst einen Fehler aus. (Probieren Sie's aus)

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)




Übung 1

  1. Lösen Sie: 120 * 3 / 4 unter Benutzung von zwei Forthbefehlen.

  2. Lösen Sie: 120 * 3 / 4 unter Benutzung von nur einem[!] Forthbefehl.

  3. Lösen Sie: ((69 + 3) * 5) / 18

  4. Schreiben Sie ein neues Forth-Wort, das die Kubikzahl einer Zahl errechnet.




Lösungen 1

  1. 120 3 * 4 / . <cr> 90

  2. 120 3 4 */ . <cr> 90

  3. 69 3 + 5 18 */ . <cr> 20

  4. : ku ( n - n*n*n ) dup dup * * ;




Wort-Typen

FORTH stellt schon im Grundwortschatz einige Worttypen zur Verfügung:

Der häufigste Worttyp ist das Colon-Wort. Theoretisch gibt es unendlich viele Worttypen, da Sie auch eigene Typen definieren können. In einer der nächsten Folgen schauen wir uns den Aufbau einzelner Worttypen genauer an.




Colon

: <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

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 ")"




Konstanten

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.




Stackmanipulationen

Da sich in FORTH alles um den Stack dreht, ist die Auswahl an Wörtern, die den Stack manipulieren, recht groß. Wir dürfen nämlich nicht erwarten, das Worte ihre Werte immer so auf dem Stack übergeben, daß das folgende Wort sie ohne weiteres übernehmen kann (Systemaufrufe). Dabei können wir noch Worte unterscheiden, die die Stacktiefe vergrößern, verändern oder die Anordnung ändern.

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.




Logische Grundoperationen

Bei den logischen Grundoperationen handelt es sich um bitweise Verknüpfung von Bitmustern nach festen Regeln.

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

Vergleichsoperationen liefern als Ergebnis ein Flag auf dem Stack, das entweder den Wert 0 (FALSE) ober -1 (TRUE) annimmt.

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




Entscheidungen

Der Wert einer Programmiersprache hängt natürlich auch von den Möglichkeiten der Programmablaufsteuerung ab. FORTH bietet hier genug Worte und darüber hinaus die Möglichkeit, den Entscheidungsatz zu erweitern.

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:

Beispiel:

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




Erste Exkursion ins ST-Betriebssystem

Selbst mit den wenigen Worten, die wir bislang kennengelernt haben, können wir uns an das VDI-Interface wagen.

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:

sf_updat sf_perimeter sf_color sf_style sf_interior st_alignment st_effects st_color st_font st_rotation st_point st_height sm_color sm_height sm_type sl_ends sl_color sl_width sl_udsty sl_type s_color swr_mode justified rfbox rbox ellpie ellarc ellipse circle pie arc bar recfl contourfill fillarea gtext pmarker marker pline line qt_extent qt_attributes qf_attributes qm_attributes ql_attributes q_color q_extnd r_trnfm ro_cpyfm ro_cpyfm s_clip st_unload_fonts st_load_fonts updwk clrwk clsvwk opnvwk clswk opnwk v_handle vq_gdos !intin !ptsin str>intin

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




Eine kleine Anwendung: Turtle-Grafik

Nachfolgend erfolgt die etwas ausführlichere Erläuterung des kleinen Grafikpakets. Ein paar neue Worte sind dabei, lassen Sie sich dadurch bitte nicht verunsichern. In den nächsten Folgen werden die Grafikfunktionen um einen 2D- und 3D-Teil ergänzt.

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.




Aufbau eines Forth-Wortes

Ein FORTH-Wort ist ein in sich geschlossenes Modul, das in das Wörterbuch eingebunden ist und von von dort aus aufgerufen werden kann. Ein FORTH-Wort besteht aus zwei Teilen, dem Wort-Header und dem Wort-Body. Im Body des Wortes ist die eigentliche Funktion abgelegt (mForth unterscheidet sich hier etwas von anderen Systemen):

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

Die Länge der NFA und der PFA sind wortabhängig.




Ausblick

Ich hoffe, der erste Teil war interessant genug, daß Sie auch in den zweiten Teil des Kurses reinschauen. Sie erfahren unter anderem etwas über:

Da dies mein erster "Lehrversuch" ist, möchte ich Sie bitten, etwas Nachsicht zu üben, wenn Unklarheiten aufgetreten sind. Für Anregungen und Fragen bin ich Ihnen dankbar.

Anrufen (06431-71188 ab 20:00 Uhr) oder schreiben.

EMail: Maus-Adresse von Rainer Saric

RS


ATOS Programmierpraxis Datenstrukturen ATOS Hilfe