Von Rainer Saric
Inhalt:
Literatur zu Forth
Die Ausführung von Worten
Vektorisierung von Worten
Erstellung von Standalone-Programmen
Eine Anwendung
Ende des Forth-Kurses
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:
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
WORD | Stack | Beschreibung |
immediate | -- | markiert letztes Wort als 'immediate' |
restrict | -- | markiert letztes Wort als 'restrict' |
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
WORD | Stack | Beschreibung |
' | -- <name> | legt PFA von <name> auf ToS |
perform | adr -- | Führt Wort aus das adr hält |
: perform ( adr - ) @ execute ; | ||
execute | pfa -- | Führt Wort aus, dessen pfa auf dem Stack liegt |
defer | -- <name> | erzeugt defered Wort <name> |
is | adr -- <name> | setzt PFA von <name> mit adr |
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)
WORD | Stack | Beschreibung |
return | n -- | Ü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 | -- adr | Bei TRUE, werden alle Worte, die über ['] |
compilert wurden in das Wort mark compiliert. | ||
prgflags | flags -- | Hält für make die Programmflags. |
Mögliche Flags: | ||
SUPER GLOBAL PRIVAT MALLOCALT LOADALT FASTLOAD | ||
setssize | fs rs -- | Setzt Forth - und Returnstacksize. |
Defaultwerte sind fs = 2Kb und rs = 3Kb. | ||
output | typ -- | Legt Programmtyp fest: .app .tos .ttp .acc .prg |
Beispiel: .app output | ||
pause | -- adr | Unterbricht listing nach jedem gefundenen |
neuen Wort und wartet auf einen Tastendruck. | ||
Setzen mit: pause on | ||
listing | -- adr | Ausgabe der Labels während make. |
Setzen mit: listing on | ||
writeSYM | -- adr | Schreibt die Symbole eines mForth Programms auf Disk |
Setzen mit: writeSYM on | ||
debug | -- adr | Schreibt die Labels in die Symboltabelle des |
Programms. (Devpac Format) |
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 ;
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