% This file was converted to LaTeX by Writer2LaTeX ver. 1.0.2
% see http://writer2latex.sourceforge.net for more info
\title{Forth--Compiler--Hilfsbefehle als High--Level--Befehle}
\author{Willi Stricker}
\date{2011-07-10}
\begin{document}

\maketitle

\begin{multicols}{2}
Der Forth--Compiler benötigt verschiedene Hilfsbefehle, die er für den
Programm--Ablauf compilieren muss, die der Programmierer aber nicht
„sieht.“ Sie werden compiliert anstelle „sichtbarer“ Befehle, die
wiederum nicht direkt umgesetzt werden können.

Einfachstes Beispiel sind die Befehle BRANCH und ?BRANCH, die der
Compiler für sämtliche Strukturen in Forth einsetzt
(IF\dots{}ELSE\dots{}THEN, BEGIN\dots{}UNTIL,
BEGIN\dots{}WHILE\dots{}REPEAT). Der Programmierer benötigt die beiden
Befehle normalerweise nicht.

Bei Forth-Systemen werden die im Folgenden beschriebenen Hilfsbefehle in
Assembler geschrieben (aus Gründen der Geschwindigkeit). Wenn aber, wie
beim STRIP--Forth--Prozessor, keine Assembler-Befehle zur Verfügung
stehen, müssen sie in High--Level geschrieben werden.

Die Befehle beeinflussen alle den Instruction Pointer (IP), dazu
folgen einige Vorbemerkungen:

Bei Aufruf eines High--Level--Befehles (also eines Unterprogramms) liegt
immer die Adresse des nachfolgenden Befehls auf dem Return--Stack
(Return--Adresse), die ist aber gleich dem jeweils aktuellen Wert des
IP.

Man könnte damit die Befehle IP@ und IP! definieren:

\begin{verbatim}
: IP@ ( -- ip ) R> DUP >R ;
: IP! ( ip -- ) R> DROP >R ;
\end{verbatim}

Der Befehl IP! wäre dann ein indirekter Sprung.

Man erhält demnach durch den Befehl R> den aktuellen
IP-Wert, muss aber dafür sorgen, dass eine gültige Return--Adresse
zurückgelegt wird, anderenfalls wird beim nachfolgenden Return in die
nächsthöhere Programm (Struktur)--Ebene zurückgesprungen.

Anmerkungen zur Schreibweise: Wie in der Forth--Literatur üblich, werden
die Compiler--High--Level--Hilfsbefehle in Klammern geschrieben. Bei der
Darstellung des Speichers werden das Name--Field und das Link--Field
weggelassen.

\section{Befehle VARIABLE und CONSTANT}

Die Variable (VARIABLE) benutzt normalerweise die Hilfs--Routine „dovar:“

Anordnung im Speicher: [dovar] [Speicherplatz für die Variable]

Funktion der „dovar“-Routine: Sie gibt als Ergebnis die Adresse des
nachfolgenden Speicherplatzes aus ( -- address), deren Inhalt dann z.\,B.
mit dem @-Befehl ausgelesen werden kann, außerdem reserviert der
Compiler den nachfolgenden Speicherplatz für die Variable. Wichtig: Das
Programm wird an der Stelle fortgeführt, an der die Variable aufgerufen
wurde (übergeordnete Ebene)!

Daraus folgt die Realisierung der Routine dovar in High-Level sehr
einfach (address = Return--Adresse des den Befehl (VAR) aufrufenden
Programms):

\begin{verbatim}
: (VAR) ( -- address )
\ return stack: ( address -- )
     R> ;
\end{verbatim}

Die Konstante (CONSTANT) benutzt normalerweise die Hilfs-Routine
“docon:“ Anordnung im Speicher: [docon] [konstanter Wert]

Funktion der „docon“-Routine: Sie gibt als Ergebnis den auf dem
nachfolgenden Speicherplatz liegenden konstanten Wert aus ( --
constant). Wichtig auch hier: Das Programm wird an der Stelle
fortgeführt, an der die Konstante aufgerufen wurde! (übergeordnete
Ebene).

Daraus folgt die Realisierung der Routine „docon“ in High-Level:

\begin{verbatim}
: (CON) ( -- constant )
\ return stack: ( address -- )
     R> @ ;
\end{verbatim}

\section{Die Befehle CREATE und CREATE\allowbreak\dots\allowbreak{}DOES>}

Der Befehl CREATE hat in Forth eine Doppelbedeutung:

\begin{enumerate}
\item Er markiert die Anfangsadresse eines beliebig großen Daten--Feldes
\item Er wird benutzt in der Kombination CREATE\dots\allowbreak{}DOES>
\end{enumerate}

Zu 1. Datenfeld:

Der Hilfsbefehl ist hier identisch mit dem der Variablen: „dovar,“ es
wird also kein neuer Befehl benötigt.

Anordnung im Speicher:

[dovar] [Speicherplatz beliebiger Größe]

Im Unterschied zur Variablen wird hier vom CREATE--Befehl kein
Speicherplatz reserviert, sondern explizit vom Programmierer z.\,B. durch
den ALLOT--Befehl.

Zu 2. Kombination CREATE und DOES>

Die Kombination aus CREATE und DOES> kann nur als
„Definierender Befehl“ benutzt werden, z.\,B.

\begin{verbatim}
: XYZ CREATE ... DOES> ... ;
\end{verbatim}

Der Befehl XYZ erzeugt eine Gruppe von Befehlen, bei denen der „Create“--Teil
ein Feld mit Daten beliebiger Art definiert und der „Does“--Teil ein
Programm zur Bearbeitung dieser Daten. Wird ein Befehl des Typs XYZ
programmiert, dann wird lediglich das zugehörige Feld definiert und das
Programm des Does--Teils aufgerufen.

Funktion: der Create--Befehl muss das Programm des Does-Teils aufrufen
und gleichzeitig die Adresse des nachfolgenden Feldes übergeben.

Anordnung im Programm:

Create--Teil: [Adresse des Does--Teils] [Datenfeld beliebiger Größe]

Does--Teil: [DOES-Befehl] [Does-Programm]

Demgemäß muss lediglich ein Befehl (DOES) definiert werden, der dem
„Does“--Teil die Daten--Adresse des „Create“--Teils übergibt (cadr =
Adresse des „Create“--Teils, radr = Return--Adresse des den Create-Teil
aufrufenden Befehls):

\begin{verbatim}
: (DOES) ( -- cadr )
\ return stack: ( cadr radr -- radr )
     R> R> SWAP >R
;
\end{verbatim}

Funktion:

Beim Aufruf des Does--Programms liegt die (Daten--)Adresse des
Create--Teils auf dem Return--Stack (erster R>--Befehl), darüber die
Adresse des aufrufenden Programms (zweiter R>--Befehl). Der SWAP--Befehl
vertauscht die Adressen, der >R--Befehl legt die Return--Adresse auf
den Return--Stack zurück für den abschließenden Return, so dass nun
die Adresse des Create--Teils für das Does--Programm zur Verfügung
steht.

\section{Die Befehle DO \dots{} LOOP/+LOOP}

Die Loop-Befehle sind, wie der Name besagt, Programmschleifen, die
jeweils mit dem Befehl DO eingeleitet und mit dem Befehl LOOP oder mit
dem Befehl +LOOP abgeschlossen werden. Es werden jeweils 2
Eingagsparameter erwartet:

Der Startindex (Index) und der Endindex (Limit). Der Compiler benutzt
dazu die Hilfsbefehle (DO), (LOOP) und (+LOOP) folgendermaßen:

[(DO)] [Schleifenbefehle beliebiger Anzahl]

[(LOOP/+LOOP)] [start-address]

Der Befehl (DO) markiert den Anfang, der Befehl (LOOP/+LOOP) das Ende
der Schleife, die Adresse „start address“ ist die auf den Befehl (DO)
folgende Adresse (1. Befehl der Schleife). 

Der vom Compiler anstelle des DO--Befehls compilierte Befehl (DO)
verschiebt die beiden Parameter vom Parameter-Stack auf den
Return--Stack, damit sie innerhalb der Schleife nicht „stören“ aber
ständig zugreifbar sind. Auch hierbei ist zu beachten, dass es sich um
einen High--Level--Befehl handelt und deswegen der oberste Wert auf dem
Return--Stack die Return--Adresse (radr) enthält, die vor dem Rücksprung
wieder dort liegen muss:

\begin{verbatim}
: (DO) ( limit index -- )
\ return-stack: ( radr -- limit index radr )
     SWAP R> SWAP >R SWAP >R >R ;
\end{verbatim}

Die Verschiebung erfolgt derart, dass der Index als oberstes Wort auf
dem Return--Stack liegt. Er wird als Variable benutzt, die mit jedem
Schleifendurchlauf verändert wird.

Anstelle des LOOP--Befehls wird vom Compiler zunächst der Befehl (LOOP)
compiliert und anschließend die Start--Adresse der Schleife. Der
(LOOP)--Befehl muss zunächst den Index weiterzählen (inkrementieren),
dann prüfen, ob der Index kleiner als der Limit ist. In dem Fall muss
er auf die Startadresse zurückspringen (nächster Schleifendurchlauf),
anderenfalls die Schleife verlassen, indem er auf den nachfolgenden
Befehl springt (die Startadresse überspringt).

\begin{verbatim}
: (LOOP) ( -- )
\ return stack: ( limit index radr --
\                 limit index startadr / radr+2 )
     R> R> 1+ DUP R@ < \ index+1 < limit ?
     IF >R @ \ next loop -- return to start
     ELSE DROP R> DROP 2+
     \ loop end -- branch to address+2
     THEN >R ;
\end{verbatim}

Anmerkung: In Forth wird die Schleife durchlaufen, solange der Index
kleiner als der Limit ist, anderenfalls wird die Schleife abgebrochen!

Die +LOOP unterscheidet sich von der einfachen LOOP dadurch, dass der
Index nicht um 1 pro Schleifendurchlauf erhöht wird, sondern um einen
programmierten Wert „data:“

\begin{verbatim}
: (+LOOP) ( data -- )
\ return stack: ( limit index radr --
\                 limit index startadr / radr+2 )
     R> SWAP DUP R> + SWAP OVER R@ <
     \ index+data < limit ?
     IF    >R @ \ next loop - return to start
     ELSE DROP R> DROP 2+
     \ loop end - branch to address+2
     THEN >R ;
\end{verbatim}

Forth stellt weitere spezielle Befehle für die Schleifenbearbeitung zur
Verfügung. Zwei werden kurz beschrieben:

Der Schleifenbefehl I (für Index) greift auf den Index im Return--Stack
zu, ohne ihn zu verändern.

\begin{verbatim}
: I ( -- index )
\ return stack ( limit index radr -- limit index radr )
    R> R@ SWAP >R ;
\end{verbatim}

Der Schleifenbefehl LEAVE bewirkt, dass die Schleife beim
nachfolgenden LOOP oder +LOOP--Befehl unabhängig vom aktuellen Index
verlassen wird. Das wird erreicht, indem der Index gleich dem Limit
gesetzt wird.

\begin{verbatim}
: LEAVE ( -- )
\ return stack: ( limit index radr -- limit limit radr )
   R> R> DROP R@ >R >R ;
\end{verbatim}

Anmerkung zu den Befehlen (VAR) und (CON) anstelle von „dovar“ und
„docon:“

Die in Assembler geschriebenen Routinen „dovar“ und „docon“ werden
direkt von der next--Routine aufgerufen und benutzen deren Word--Pointer
(WP), ohne auf den Return--Stack zuzugreifen. Wenn man sie durch die
High-Level-Befehle (VAR) und (CON) ersetzt, dann muss ihnen eine
„docol“--Routine vorangestellt werden (nicht nötig beim
STRIP--Forth--Prozessor)! Die Teil--Programme „docol,“ „dovar“ und „docon“
werden als Routinen bezeichnet, da sie keine Forth--Befehle sind.
\end{multicols}
\end{document}
