ATOS-Magazin April/Mai 1997 Das ATOS-Magazin 2/97

ATOS Programmierpraxis ATOS Programmierpraxis

Forth-Kurs, Teil 4

Von Rainer Saric


 Bild




Inhalt:
  Literatur zu Forth
  Die Ausführung von Worten
  Vektorisierung von Worten
  Erstellung von Standalone-Programmen
  Eine Anwendung
  Ende des Forth-Kurses






Literatur zu Forth

Zunächst möchte ich Sie zum letzten und kürzesten Teil unseres kleinen Kurses begrüßen! Bedanken möchte ich mich bei Ihnen, daß Sie den Kurs bis hier verfolgt und kleinere Fehler toleriert haben.

Es konnte natürlich nicht jedes Thema so ausführlich behandelt werden, wie dies vielleicht nötig gewesen wäre. Auch sind viele Programmiertechniken in der Kürze nicht zu diskutieren (z.B.: Object Oriented Forth).

Wenn Sie sich weiter über Forth informieren möchten oder tiefer in die Sprache einsteigen möchten, gebe ich Ihnen hier noch einige Literaturhinweise:






Die Ausführung von Worten

Vergegenwärtigen wir uns nochmals den Aufbau eines Forthwortes:

Jedes Forth-Wort beginnt mit dem Zeiger auf die Link Field Adresse (4 Bytes), gefolgt von einem Word Parameter Field oder Kontrollflag (4 Bytes). Das Kontrollflag steuert das Verhalten des Wortes während der Compilezeit (die Flags können selbstverständlich kombiniert werden).

Hier eine Liste von Wörtern, die nur in einer Colon-Definition aufgerufen werden dürfen:

r> >r do loop if else endif begin until while repeat i j

Im Interpretermodus meldet Forth: compile only

Eine wichtige Eigenschaft, die Sie einem Wort geben können, ist das Setzen des immediate-Flags. Worte, bei denen dieses Bit gesetzt ist, werden während der Compilezeit sofort ausgeführt.

Zur Verdeutlichung ein unnützliches Wort:

: hallo    cr ." hallo" ; immediate
: test hallo ; /* hallo wird nach <cr> nicht compiliert! */
test

Berechnungen können so im Compile-Mode durchgeführt werden, die sonst während der Laufzeit Rechenzeit verbrauchen.

100 constant xWerte
100 constant yWerte

: ....  [ xWerte yWerte * ] literal m_alloc ...
;

Mit restrict wird ein Wort markiert, das nur in einer Colon-Definition angewendet werden kann.

: hallo cr ." hallo" ; restrict
hallo
: test hallo ;
test

WORDStackBeschreibung
immediate--markiert letztes Wort als 'immediate'
restrict--markiert letztes Wort als 'restrict'






Vektorisierung von Worten

Ihnen ist sicherlich schon aufgefallen, daß Forth während des Compilevorgangs jedes Wort bekannt sein muß.

Beispiel:

: test ( -- )  x @ ( irgendwas ) ;
variable x

Die Compilierung wird mit der Fehlermeldung x not found abgebrochen. Es ist jedoch mitunter ganz nützlich, ein Wort zu verwenden, dessen Bedeutung sich während der Laufzeit ändern kann. Ich möchte Ihnen jetzt beide Möglichkeiten zur Realisierung zeigen.



Verwendung von Variablen

Da Forth eine typenlose Sprache ist, bleibt es dem Anwender vorbehalten, mit den Daten anzustellen, was er möchte. Nehmen wir an, Sie schreiben ein Wort, das die Ausgabe wahlweise auf den Bildschirm oder den Drucker ausgibt. Die prinzipielle Vorgehensweise:

variable vOutput  /* hält Ausgaberoutine  */
' noop vOutput !  /* Tut erst 'mal nichts */

: drucker ( .. -- ) ...... ;
: console ( .. -- ) ...... ;

' console vOutput !  /* Default zum Bildschirm */
: list    ( .. -- ) vOutput perform ;

Zur Ausgabe auf den Drucker müssen Sie jetzt lediglich vOutput neu initialisieren.

' drucker vOutput !
/* In einer Colon-Definition geschieht dies mit: .. ['] drucker vOutput ! ... */



Verwendung von defer

Forth bietet Ihnen das Definitionswort defer an. Defer erzeugt einen Wörterbucheintrag und compiliert darin zunächst das Wort noop.

Probieren Sie folgendes aus:

defer myVek
' dup is myVek
1 myVek

Für unser Beispiel:

defer vOutput

: drucker ( .. -- ) ...... ;
: console ( .. -- ) ...... ;

' console is vOutput /* Default zum Bildschirm */
: list    ( .. -- ) vOutput ;

Über defer können Worte jetzt innerhalb einer Anwendung neue Funktionen zugewiesen bekommen, auch wenn das Wort erst zu einem späteren Zeitpunkt definiert wurde.

Liste der verwendeten Worte

WORDStackBeschreibung
'-- <name>legt PFA von <name> auf ToS
performadr --Führt Wort aus das adr hält
: perform ( adr - ) @ execute ;
executepfa --Führt Wort aus, dessen pfa auf dem Stack liegt
defer-- <name>erzeugt defered Wort <name>
isadr -- <name>setzt PFA von <name> mit adr






Erstellung von Standalone-Programmen

Ziel jeder Programmiersprache ist die Erstellung eines eigenständigen Programms. Viele Forthsysteme sind allerdings nur in der Lage, ihr gesamtes Programmimage zu sichern. So kann es vorkommen, daß ein einfaches hello, world leicht mehr als 50kb in Anspruch nimmt. Mit mForth dagegen ist es möglich, nur die benötigten Worte aus dem Programm zu einem Programm zu 'linken'. Als Ergebnis erhält man relativ kleine Programme.

Ganz so einfach wie bei einem C-Compiler ist es aber nicht, denn der Forth-Interpreter ist, wie Sie bereits wissen, etwas einfacher aufgebaut. Wir müssen also selbst "Hand" anlegen.

Forth kann nicht wissen, ob eine Adresse im Programm reloziert werden muß. Das Programm würde nur an der gleichen Stelle im Speicher funktionieren. Das ist aber nicht der Sinn der Sache. Stellen Sie sich vor, Sie laden ein zusätzliches ACC. Das erzeugte Programm wäre nun nicht mehr lauffähig, da alle Sprungadressen falsch wären.

Das sieht wieder schwieriger aus, als es in Wirklichkeit ist. make aus mForth nimmt Ihnen schon einiges an Arbeit ab.

Damit alle ['] Worte markiert werden, setzen Sie einfach automark on.

Man beachte, das das Wort mark nach return stehen muß, denn mark darf auf keinen Fall aufgerufen werden.

Als Beispiel soll uns ein neues Wort cat dienen, mit dem man zwei Dateien aneinanderhängen kann:

/* Cat 2 Files */

true constant APP immediate

APP #if system automark on toss #endif

mforth gemdos
#ifndef open   include system\files.4th   #endif

mforth gemdos also

create $von 64 allot /* Name File 1 */
create $an  64 allot /* Name File 2 */
FD von     /* Filediscriptor File 1 */
FD an      /* "                   2 */
create fbff 1026 allot /* Kopierpuffer */

: cat ( -- )
   2 von $von open      /* 1. File öffnen */
   if    2 an $an open  /* anfüegen an    */
         if    an fsize an fseek /* Größe bestimmen */
               begin  fbff 1024 von fgets dup
               while  fbff swap an  fputs
               repeat drop
         endif
         von close   /* Files schließen */
         an  close
   endif ;

/* Aus der cmdline Filename extrahieren */
: >arg   ( addr dest -- addr' )
   swap begin dup c@ 0= while 1+ repeat swap /* skip 0bytes */
   begin over c@ 0<> while over c@ over c! 1 1 pair+ repeat
   0 swap c! ; /* 0byte hinten dran */

: main ( -- )
   0 $von c! 0 $an  c!  /* = NULLSTRINGS */
   argv
   if    cmdline c@ 2 > /* Mehr als 2 Argumente übergeben ? */
         if    /* Alle Blanks durch Nullbytes ersetzen */
               cmdline count bounds do i c@ 32 = if 0 i c! endif loop
               /* Filename auslesen */
               cmdline 1+ $von >arg $an >arg drop
               cat /* Los geht's */
         endif
   endif
   APP #if 0 return [ macro off system ] mark #endif ;

mforth
APP #if
   system .ttp output
   make cat bye
#endif

Liste der notwendigen Worte (vocabular system)

WORDStackBeschreibung
returnn --Übergibt n und beendet das Programm.
make-- <name>Sucht main und erzeugt das Program name
mark:-- <name><name> in das Wort mark compilieren
mark--Enthält Worte, die über ['] compiliert wurden.
automark-- adrBei TRUE, werden alle Worte, die über [']
compilert wurden in das Wort mark compiliert.
prgflagsflags --Hält für make die Programmflags.
Mögliche Flags:
SUPER GLOBAL PRIVAT MALLOCALT LOADALT FASTLOAD
setssizefs rs --Setzt Forth - und Returnstacksize.
Defaultwerte sind fs = 2Kb und rs = 3Kb.
outputtyp --Legt Programmtyp fest: .app .tos .ttp .acc .prg
Beispiel: .app output
pause-- adrUnterbricht listing nach jedem gefundenen
neuen Wort und wartet auf einen Tastendruck.
Setzen mit: pause on
listing-- adrAusgabe der Labels während make.
Setzen mit: listing on
writeSYM-- adrSchreibt die Symbole eines mForth Programms auf Disk
Setzen mit: writeSYM on
debug-- adrSchreibt die Labels in die Symboltabelle des
Programms. (Devpac Format)






Eine Anwendung

Ein Kursteilnehmer fragte mich, ob es möglich sei, Texte aus Forth heraus auf den Drucker auszugeben. Daraus entstand ein einfaches more

/*
   Einfaches MORE (erweiterbar) für Forth-Kurs
   -------------------------------------------
   Use: more <name> p
   mit p wird die Datei auf den Drucker ausgegeben
   -----------------------------------------------
   Ausgabe über GEMDOS
   Beliebige Taste hält die Ausgabe an, Escape bricht ab
*/

mforth include system\files.4th
mforth gemdos also

variable ok    /* Bool */
defer mEmit    /* Ziel */
FD infile      /* Filediscriptor */

: printer ( char -- ) c_prnout drop ;
: console ( char -- ) c_conout drop ;

: _more_ ( -- )
   begin  infile fgetc EOF over <>
   while  mEmit key?
          if   key 255 and 27 =
               if    exit
               else  key drop
               endif
          endif
   repeat drop ;

: more ( -- <name> <ziel> )
   ok on ['] console is mEmit
   name nullstr? not    /* Filename ?? */
   if    1+ 2 infile rot open /* File öffnen */
         if    name nullstr? not /* Ziel ??  */
               if    1+ c@ ascii p =
                     if    c_prnos /* Drucker bereit? */
                           if    ['] printer is mEmit
                           else  cr ." Drucker nicht bereit"
                                 ok off
                           endif
                     endif
               endif
               ok @ if cr _more_ endif
               infile close
         endif
   endif ;






Ende des Forth-Kurses

So, das wars!

Sollten Sie noch Fragen, Anregungen oder vielleicht einen Fehler entdeckt haben, setzen Sie sich mit mir in Verbindung.

Anrufen (06431-71188 ab 20.00) oder schreiben:

MausNet: Rainer Saric


ATOS Programmierpraxis ATOS Programmierpraxis