\label{c18-3}
\emph{Fortsetzung des Artikels von Chuck Moore von Seite \pageref{c18-2}}

\begin{multicols}{2}

\begin{comment}
\section{Compiler} 
Der c18 hat 192 Worte Speicher mit je 18 Bit. RAM-Adressen sind 0--7f, ROM sind 80--bf. Instruktionen sind 5 Bit breit. Es gibt 4 Plätze (Slots) für Instruktionen in jedem Wort. Von links nach rechts: S0, S1 und S2 sind 5 Bit breit, S3 ist 3 Bit breit und wird um zwei niederwertige Nullen ergänzt.

Der Pentium--Compiler interpretiert colorForth--Quellcode und packt Instruktionen in die niederwertigen 18 Bits seines 32--Bit--Speichers. 

Es gibt einige Einschränkungen:

\begin{itemize}
\item Nur die ...00-Instruktionen passen in Slot 3. 
\item Sprung--Instruktionen sind auf Slot 0 und 1 beschränkt. Die Sprungadresse ist in Slot$\!$ 2 und Slot$\!$ 3. 
\item Sprünge gehen zu Slot 0 des Ziels.
\end{itemize}

c18--Quellcode ist grün, genau wie Pentium--colorForth. Aber grüne Zahlen werden redefiniert, so dass sie ausgeführt werden. Jedes Wort kompiliert seinen Instruktions--Code in den nächsten verfügbaren Slot. Und grüne Zahlen werden redefiniert, so dass sie ein c18--Literal kompilieren. Gelbe Wörter oder Zahlen
sind in c18--Quellcode nicht angebracht, weil c18--Wörter selber nicht ausgeführt werden können.
 
Um c18--Wörter zu definieren, die einen Unterprogrammaufruf kompilieren, wird das colorForth--Konstrukt {\bf class} verwendet. Der Code, den die Variable {\bf class} bestimmt, wird ausgeführt, wann immer ein Wort definiert wird. Für c18 ist das ein Makro, das Pentium--Code kompiliert, der die c18--Adresse auf den Stack legen und dann ein Wort aufrufen wird, das einen c18--Unterprogrammaufruf kompiliert. 

Natürlich ist {\bf empty} neu definiert, so dass es das normale Verhalten grüner Wörter, grüner Zahlen und von {\bf class} wieder herstellt.

Beachte, dass Sprung--Instruktionen oft ein neues Instruktionswort erzwingen.
Manchmal kann ein Umsortieren von Code ansonsten leer gelassene Slots füllen.
Das Beste ist, Definitionen klein und einfach zu halten und darauf zu achten, wie gut Instruktionen sich packen.

\section{Quellcode des Compilers} 
Hier ist ein Ausschnitt des Compilers. Für die, die colorForth laufen lassen können, gibt es die 3 Blöcke voranalysierten Quellcode unter \url{www.colorforth.com}.

\begin{alltt}
S4 n  {\bf h} @ {\bf ip} ! 8192 * , 1 {\bf slot} ! ; 
S0 n   8192 * 
SN n   dup {\bf call?} ! {\bf ip} @ +! 1 {\bf slot} +! ; 
S1 n   256 * sn ; 
S2 n   8 * sn ; 
S3 n   dup 3 and drop if 7 sn s4 ; then 4 / sn ; 
I, n   {\bf slot} @ jump s0 s1 s2 s3 s4 
\end{alltt}

{\bf I,} springt zum Code für den aktuellen Slot. {\bf Slot}, {\bf h}, {\bf ip} und {\bf call?} sind Variablen. {\bf Ip} ist die nächste Instruktions--Adresse. {\bf H} ist die nächste verfügbare Adresse. {\bf H} ist {\bf ip}+1, außer wenn ein Literal kompiliert wurde. Slot 4 markiert das Ende der aktuellen Instruktion. Die meisten Instruktionen werden durch {\bf i,} definiert. Zum Beispiel:

\begin{alltt}
@    b i, ; 
2*  11 i, ; 
+   14 i, ; 
.   1c i, ;
\end{alltt}

Literale brauchen besondere Aufmerksamkeit.

\begin{alltt}
N   defer   8 f@ execute  8 i, 3ffff and , ;
\end{alltt}
 
{\bf Defer} liefert die Adresse des folgenden Codes, der grünen Zahlen zugeordnet wird (alle 18--Bit--Zahlen sind auf einem 32--Bit--Host verkürzt). 
Danach treffen diese auf die Phrase {\bf 8 f@ execute}, die die Zahl aus dem voranalysierten Wort wie eine gelbe kurze Zahl extrahiert. Das liefert das Literal auf dem Stack. Nun wird die Instruktion 8 kompiliert und das Literal abgeschnitten und in das nächste Wort kompiliert. Bis zu 4 Literale können von der gleichen Instruktion referenziert werden, die von den tatsächlichen Literalen gefolgt wird.

Sprung--Instruktionen benutzen {\bf adr}, das den Instruktions--Code kompiliert und die Adresse der Instruktion liefert. {\bf Break} zwingt die folgende Instruktion in ein neues Instruktions--Wort:
\end{comment}




\begin{alltt}
BREAK      4 {\bf slot} ! ; 
ADR n-a   {\bf slot} @ -2 and drop if . adr ; then 
           s3? if . adr ; then  i, {\bf ip} @ break ; 
CALL       defer a   ff and 2 adr +! ; 
IF -a      6 adr ; 
-IF -a     7 adr ; 
THEN a    {\bf h} @ ff and swap +! 0 {\bf call?} ! break ; 
UNTIL a    ff and 7 adr +! ; 
\end{alltt}

{\bf Adr} kompiliert {\bf .} bis Slot 0 oder 1 verfügbar ist. {\bf Until} 
springt zurück bis der Stack negativ wird.

\begin{alltt}
;  {\bf call?} @ dup 4000 or drop if dup 200 or drop if 
   0 and i, ; then then 2/ {\bf ip} @ +! ; 
\end{alltt}   
   
{\bf ;} überprüft, ob die letzte Instruktion ein Unterprogrammaufruf war. Wenn ja, so wird der Aufruf in einen Sprung geändert. Das ist \emph{Endrekursion} und spart eine Return--Instruktion, ohne Syntax für einen Sprung zu benötigen.
{\bf call?} ist eine Variable, die die letzte Instruktion enthält. Ihr Wert ist geshiftet und drückt so aus, welcher Slot belegt ist. 

\section{Target code}
Hier ist ein Beispiel für Zielcode. Der c18 wird gebeten seinen Flash--Speicher zu lesen und in internes RAM zu legen.

\begin{alltt}
{\bf 90 org} 
 BIT --1  -1 64 begin  over   + until drop ; 
 @F  --1n    36 begin  bit b! + until b@ ; 
 WORDS na    a! begin  @f !+  + until drop ; 
{\bf 80 org}  102 b!  63 0 words 
\end{alltt}

Dieser Code nutzt den Vorteil aus, dass {\bf until} bei einer -1 auf dem Stack endet. {\bf Bit} zählt eine halbe Clock--Periode. {\bf @f} erzeugt 18 Clock--Impulse und liest das Eingabe--Shift--Register. {\bf B!} schaltet in diesem Fall die Clock--Leitung um. {\bf Words} liest und speichert die Eingabe--Worte.

Bestimmter Code wird standardmäßig im ROM stehen. Zum Beispiel Schiebe-- und Multiplizier--Routinen:

\begin{alltt}
*7 nn-nn   *+ *+ *+ *+ 
           *+ *+ *+ ; 
2*15 n-n   2* 2* 2* 
2*12       2* 2* 2* 
2*9        2* 2* 2* 
2*6        2* 2* 2* 
           2* 2* 2* ; 
2/15 n-n   2/ 2/ 2/ 
2/12       2/ 2/ 2/ 
2/9        2/ 2/ 2/ 
2/6        2/ 2/ 2/ 
           2/ 2/ 2/ ; 
\end{alltt}

Diese vordefinierten Routinen aufzurufen, tauscht Zeit gegen Platz ein. Ein Unterprogrammaufruf braucht 3--4 Slots und 3 Zyklen. Ein in-line 2*15 würde die Zyklen sparen, aber braucht so 19 Slots, weil 2+ nicht in Slot 3 sein kann. Natürlich, um 8 Stellen nach links zu Schieben:

\begin{alltt}
2* 2* 2*6 
2* 2*6 2* 
2*6 2* 2* 
\end{alltt}

was immer auch am besten packt.\smallskip

\hfill Übersetzt von Ulrich Hoffmann
\end{multicols}
[1] Englisches Original unter \url{http://www.complang.tuwien.ac.at/anton/euroforth/ef01/moore01a.pdf}
\end{document}