% Forth von der Pike auf Teil 2 
% Content-encoding: UTF-8

\usepackage{soul}

\begin{document}
\title{Forth von der Pike auf — Teil 2}
\author{Ron Minke}
\maketitle

Die hier mit freundlicher Genehmigung der HCC--Forth--gebruikersgroep
wiederzugebende achtteilige Artikelserie erschien in den Jahren 2004
und 2005 in der Zeitschrift ``Vijgeblaadje'' unserer niederländischen
Forth--Freunde.\\ Übersetzung: Fred Behringer.

Hier ist der zweite Teil des Versuchs, ein Forth--System auf die Beine
zu stellen, dessen Voraussetzung “überhaupt nix”, oder auf gut Deutsch
“from scratch”, lautet.

\begin{multicols}{2}

\section*{16--Bit--Register auf dem AVR}

In diesem Teil werden wir versuchen, die Register des AVR--Prozessors
auf die Register der virtuellen Forth--Maschine abzubilden. Zuallererst
müssen wir uns klar machen, dass wir drauf und dran sind, ein
16--Bit--Forth auf einem 8--Bit--Prozessor hochzuziehen. Wir werden also
alle Register mit Register--\emph{Paaren} des AVR--Prozessors koppeln müssen
(die zum Glück in genügender Zahl vorhanden sind). 

Zunächst noch schnell rekapitulieren, welche Register wir nötig haben:

\begin{tabular}{lp{0.3\columnwidth}p{0.5\columnwidth}}
PC& Ma\-schi\-nen\-pro\-gramm\-zähler &Der Motor des Mikro--Controllers.\\
\\
SP& Datenstack--Pointer & Zeigt auf das zur Zeit oberste Element des Datenstacks.\\
\\
RP& Returnstack--Pointer& Zeigt auf das zur Zeit oberste Element des Returnstacks.\\
\\
IP& Interpreter--Pointer& Zeigt auf die nächste Anweisung (Forth--Definition), die ausgeführt werden soll; überwacht die Reihenfolge der   Ausführung.\\
\\
W& Wort--Pointer&Zeigt auf die Definition, die gerade ausgeführt wird; nötig, um den Parameterteil dieser Definition anzuspringen.
\end{tabular}

Die Wahl des Registers PC ist einfach: Der PC!  Der Programmzähler
eines Mikro--Controllers ist das einzige ``Register'', das das 
``Runnen'' des eigentlichen Programms, des Low--Level--Programms, steuert.

Der AVR--Prozessor hat 32 frei verfügbare 8--Bit--Register an Bord: R0 bis
R31. Zum Glück haben die Prozessor--Entwickler gut nachgedacht: 8
Register können zu 4 Registerpaaren zusammengekoppelt werden. Das
sind:

\begin{tabular}{ll}
\verb|R24| -- \verb|R25| & \verb|W|,\\
\verb|R26| -- \verb|R27| & \verb|X|,\\
\verb|R28| -- \verb|R29| & \verb|Y|,\\
\verb|R30| -- \verb|R31| & \verb|Z|.
\end{tabular}

Um die richtige Wahl treffen zu können, müssen wir uns die
verschiedenen Eigenschaften der Registerpaare ansehen.

\section*{Der Datenstack}

Wir beginnen unsere Zuordnungssuche mit SP, dem
Datenstack--Pointer. Wir wollen Daten auf den Datenstack legen. Hier
begegnen uns schon vier Wahlmöglichkeiten:

\begin{tabular}{lp{0.3\columnwidth}p{0.5\columnwidth}}
&Pseudo--Code & Was der tut\\
\\
1. &INC SP\newline MOV (SP),data & Datenstack wächst nach oben, erst Pointer anpassen, danach dann Daten ablegen.\\
\\
2. & MOV (SP),data \newline INC SP &   Datenstack wächst nach oben, erst Daten   ablegen, danach dann Pointer anpassen.\\
\\
3.& DEC SP\newline MOV (SP),data &  Datenstack wächst nach unten, erst Pointer   anpassen, danach dann Daten ablegen.\\
\\
4. & MOV (SP),data\newline DEC SP & Datenstack wächst nach unten, erst Daten    ablegen, danach dann Pointer anpassen.
\end{tabular}

Die Entscheidung darüber, ob der Datenstack nach oben oder nach unten
wachsen soll, stellen wir noch etwas zurück. Allerdings wäre es
besonders schön, wenn wir keine separaten Befehle INC oder DEC
benötigen würden. Am liebsten hätten wir einen Befehl, der automatisch
auch gleich noch inkrementiert bzw. dekrementiert. Beim AVR--Prozessor
steht diese Möglichkeit für die Registerpaare X, Y und Z tatsächlich
zur Verfügung. Mit dem Befehl ST (store)

\begin{quote}
\verb|ST X,R5|
\end{quote}

setzen wir den Inhalt des Registers R5 an die Stelle, wohin der Inhalt
des X--Registerpaares zeigt. (Ausführliche Informationen finden sich
auf der ATMEL--Webseite.)

Der Befehl

\begin{quote}
\verb|ST X+,R5|  (Auswahlmöglichkeit 2)
\end{quote}

macht dasselbe, aber danach wird in einem einzigen Zug auch noch der
Inhalt des X--Registerpaares um 1 erhöht (post increment). Und gratis
ist das obendrein: Die Post--Increment--Aktion kostet \emph{keinen}
Extra--Maschinentakt.

Der Befehl

\begin{quote}
\verb|ST -X,R5| (Auswahlmöglichkeit 3)
\end{quote}

arbeitet genauso, jedoch wird erst der Inhalt des X--Registerpaares um
1 erniedrigt, (pre decrement), bevor R5 auf dem dann angewiesenen
Platz aufbewahrt wird.

Nun müssen wir uns noch damit beschäftigen, was
der SP--Pointer macht: SP zeigt auf das oberste (oder unterste, je nach
Richtung) Element des Datenstacks. Wollen wir auf dem Stack neue Daten
ablegen, dann muss \emph{erst} Platz gemacht werden, bevor wir die Daten
abspeichern können. Von den oben genannten vier Möglichkeiten bleibt
also nur die mit Nummer 3 übrig. Damit haben wir uns für ein
Nach--unten--Wachsen des Datenstacks entschieden.

Unsere virtuelle Forth--Machine ist 16 Bit breit, so dass wir nun für den
8--Bit--AVR--Prozessor zum folgenden \mbox{Code} kommen: (Daten stehen in R4 und
R5 bereit)

\begin{quote}
\verb|ST -X,R4|\\
\verb|ST -X,R5|
\end{quote}

Man beachte, dass wir damit bereits den Maschinencode für das
Forth--Wort ``!'' (store) gemacht haben.

Aber gemach! Wir wollen unsere Wahl der AVR--Register--Zuweisung 
bequem gestalten: Sowohl das X-- wie
auch das Y-- und das Z--Register haben die oben genannte
Autodekrement--Eigenschaft. Die endgültige Wahl für SP muss also noch
etwas zurückstehen.

Wir bekommen es jetzt noch mit etwas anderem zu
tun. Es muss noch beschlossen werden, was zuerst aufzubewahren ist:
Das obere Byte oder das untere Byte der 16--Bit--Zahlenwerte. Oder auch:
Wie wollen wir die Daten auf dem Stack gelagert sehen? Dem
Forth--System selbst ist diese Frage egal. Wir müssen uns also nach
einem anderen Kriterium umsehen. Die Entscheidung darüber, welche Wahl
wir treffen, bleibt noch einen Augenblick lang offen (na ja $\ldots$
so dringend ist das noch nicht).

\section*{Der Returnstack}

Erst noch ein weiteres Forth--Register: Der RP. Der Returnstack heißt
so, weil die virtuelle Forth--Maschine ihn dazu verwendet, die
Rückkehr--Adressen (return = zurück) aufzubewahren, diejenigen
Adressen, wo besagte virtuelle Maschine weiterarbeiten soll, \emph{nachdem}
ein High--Level--Wort vollständig ausgeführt worden ist. Wenn ein
High--Level--Forth--Wort ein schon früher definiertes anderes Forth--Wort
aufruft, wird die Adresse des \emph{nächsten} Wortes in die Wortliste auf dem
Returnstack eingereiht. Diese Adresse wird wiederhergestellt, sobald
das gerade eben aufgerufene Wort vollständig abgearbeitet ist. Das
Programm kann dann vom Verzweigungspunkt aus weitergehen.

Der erste
Gedanke, der aufkommt, ist der, ob man den RP nicht an den
Maschinen--Stack--Pointer SP (nun, beim AVR heißt der nun einmal so)
koppeln sollte. Dieses AVR--SP--Register hat genau dieselbe Funktion wie
die, die wir bei der virtuellen Forth--Maschine haben wollen: Es zeigt
auf die aufbewahrten Adressen, wo der Programm--Counter weiterarbeiten
soll, wenn ein Unterprogrammaufruf beendet ist. Auto--Inkrement,
bzw. Auto--Dekrement sind auch eingebaut. Beim Aufrufen eines
Maschinensprach--Unterprogramms wird gleichzeitig die unmittelbar
folgende Adresse auf den Returnstack gelegt. Wenn das Unterprogramm
fertig ist, wird diese Adresse wieder in den Programm--Counter PC
zurückgeschrieben, so dass das Programm weiterlaufen kann, als ob da
nichts geschehen wäre. Also genau das, was wir brauchen.

Was passiert da nun genau? Angenommen, ein willkürlich herausgesuchtes Stück
AVR--Maschinencode ruft ein Unterprogramm auf (CALL). Verfolgt man den
sich ergebenden CALL--Code (siehe ATMEL--AVR--Befehlssatz auf deren
Website), so bekommt man die Sequenz (in Pseudo--Code, in Bytes):

\begin{verbatim}
MOV (AVR-SP),unteres-Byte-momentaner-PC + 1
DEC AVR-SP 
MOV (AVR-SP),oberes-Byte-momentaner-PC + 1
DEC AVR-SP
MOV PC,(momentaner-PC)
\end{verbatim}

Wir sehen hier, dass der Returnstack nach \emph{unten} wächst und dass \emph{erst}
die Daten abgespeichert werden, bevor Platz gemacht wird. Der AVR--SP
zeigt also offenbar auf den ersten freien Platz auf dem Stack. So
haben sich die Entwickler bei ATMEL das jedenfalls
vorgestellt.

\section*{Festlegungen}

Nun wissen wir zumindest einiges. Beim Codieren der virtuellen
Forth--Maschine wollen wir es uns so einfach wie möglich machen. Wir
halten uns an die oben genannte Arbeitsweise.

{\em Wir treffen drei Entscheidungen:}

\begin{enumerate}
\item Die Forth--Stacks SP und RP wachsen \emph{nach unten.} 
\item Das untere
  Byte eines 16--Bit--Wortes wird zuerst auf den Stack gelegt, darunter
  dann das obere Byte.
\item Der Forth--Pointer zeigt auf das \emph{obere} Byte
  eines 16--Bit--Wortes (und also --- in Abweichung von der Arbeitsweise
  des AVR--SPs --- nicht auf den leeren Platz unter dem Wort). Das
  entspricht der Wahlmöglichkeit 3 bei der obigen Besprechung der
  Datenstack--Zuweisung.
\end{enumerate}

Nun denn$\ldots$ jetzt kommt allmählich etwas Struktur in die
virtuelle Forth--Maschine.

\hfill --- Wird fortgesetzt ---
\end{multicols}
\end{document}
