<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">% 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&amp; Ma\-schi\-nen\-pro\-gramm\-zÃ¤hler &amp;Der Motor des Mikro--Controllers.\\
\\
SP&amp; Datenstack--Pointer &amp; Zeigt auf das zur Zeit oberste Element des Datenstacks.\\
\\
RP&amp; Returnstack--Pointer&amp; Zeigt auf das zur Zeit oberste Element des Returnstacks.\\
\\
IP&amp; Interpreter--Pointer&amp; Zeigt auf die nÃ¤chste Anweisung (Forth--Definition), die ausgefÃ¼hrt werden soll; Ã¼berwacht die Reihenfolge der   AusfÃ¼hrung.\\
\\
W&amp; Wort--Pointer&amp;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| &amp; \verb|W|,\\
\verb|R26| -- \verb|R27| &amp; \verb|X|,\\
\verb|R28| -- \verb|R29| &amp; \verb|Y|,\\
\verb|R30| -- \verb|R31| &amp; \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}}
&amp;Pseudo--Code &amp; Was der tut\\
\\
1. &amp;INC SP\newline MOV (SP),data &amp; Datenstack wÃ¤chst nach oben, erst Pointer anpassen, danach dann Daten ablegen.\\
\\
2. &amp; MOV (SP),data \newline INC SP &amp;   Datenstack wÃ¤chst nach oben, erst Daten   ablegen, danach dann Pointer anpassen.\\
\\
3.&amp; DEC SP\newline MOV (SP),data &amp;  Datenstack wÃ¤chst nach unten, erst Pointer   anpassen, danach dann Daten ablegen.\\
\\
4. &amp; MOV (SP),data\newline DEC SP &amp; 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}
</pre></body></html>