% Content-encoding: UTF-8 \documentclass[ngerman]{article} \usepackage[utf8]{inputenc} \usepackage{multicol,babel} \setcounter{secnumdepth}{0} \setcounter{tocdepth}{0} \renewcommand{\reftextbefore}{auf der vorherigen Seite} \renewcommand{\reftextfacebefore}{auf der gegenüberliegenden Seite} \renewcommand{\reftextafter}{auf der nächsten Seite} \renewcommand{\reftextfaceafter}{auf der gegenüberliegenden Seite} %\begin{document} % \renewcommand{\figurename}{Tabelle} \title{c18 colorForth Compiler} \author{Charles Moore} \maketitle \emph{Im folgenden Artikel aus dem Jahr 2001 beschreibt Chuck Moore seinen colorForth--Compiler für den c18--Prozessor, der heute Grundlage der SEAforth--Prozessoren ist.} \section{Zusammenfassung} c18 ist ein kleiner Computer, der auf einem Siliziumdie oft vervielfacht werden kann. Seine Architektur und seine Instruktionen sind für Forth optimiert. colorForth ist ein Forth--Dialekt, der voranalysierten Quellcode und Farbe für die Zeichensetzung verwendet. Es ist ein Compiler in colorForth enstanden, der colorForth--Quellcode in c18--Maschinencode übersetzt. Der Code kann im c18--ROM oder --RAM abgelegt und von einem Simulator ausgeführt werden. Oder in serielles EPROM/Flash, um von einem c18 ausgeführt zu werden, wenn er existiert. Die Betonung liegt hier darauf, wie einfach ein colorForth--Cross--Compiler sein kann. Der c18 ist interessant, weil er extrem schnell bei wenig Leistung arbeitet: 2400 Mips @ 20 mW. \begin{multicols}{2} \section{c18--Instruktionen} Hier sind die momentanen c18--Instruktionen, die übersetzt werden müssen. Während sich das Design weiterentwickelt, sind Änderungen offensichtlich leicht vorzunehmen. \begin{center} \begin{tabular}{|l|l} \hline & Register \\ \hline T & oberstes Stack--Element\\ \hline S & 2. Stack--Element\\ \hline R & oberstes Returnstack--Element\\ \hline A & Adressregister \\ \hline B & Adressregister\\ \hline \end{tabular} \end{center} Beachtet, dass das Laden ein Element auf den Stack legt und das Speichern und die zweistelligen Operatoren ein Element vom Stack entfernen. \begin{center} \begin{scriptsize} \begin{tabular}{|r|l|l|} \hline Code&Op&Aktion\\ \hline 0&;& aus Unterprogramm zurückspringen\\ \hline 1 &&\\ \hline 2 & Wort & Unterprogramm aufrufen\\ \hline 3 &word ; & zum Unterprogramm springen (Endrekursion)\\ \hline 4 &&\\ \hline 5 &&\\ \hline 6 & if & springe zu 'then' wenn T0-T17 alle null sind\\ \hline 7 & -if & springe zu 'then' wenn T17 null ist\\ \hline 8 & n & lade Literal\\ \hline 9 & @+ & lade von Adresse in A; erhöhe A\\ \hline a & @b & lade von Adresse in B\\ \hline b &@ & lade von Adresse in A\\ \hline c &!r & speichere in die Adresse in R; erhöhe R\\ \hline d & !+ & speichere in die Adresse in A; erhöhe A\\ \hline e &!b& speichere in die Adresse in B\\ \hline f &! & speichere in die Adresse in A\\ \hline 10 &+* & addiere S zu T, wenn T0=1; schiebe rechts\\ \hline 11 &2*& schiebe T 1 Bit links\\ \hline 12 &2/& schiebe T 1 Bit rechts; erhalte T17\\ \hline 13 &-& 1er--Komplement von T\\ \hline \end{tabular} \begin{tabular}{|r|l|l|} \hline 14 &+& addiere S zu T\\ \hline 15 &or& exklusiv-oder von S und T nach T\\ \hline 16 &and& und von S und T nach T\\ \hline 17 &drop& bewege S nach T\\ \hline 18 &dup& dupliziere T\\ \hline 19 &over& lade S\\ \hline 1a &pop& lade R\\ \hline 1b &a& lade A\\ \hline 1c &.& tue nichts\\ \hline 1d &b!& speichere in B\\ \hline 1e &push& speichere in R\\ \hline 1f &a!& speichere in A\\ \hline \end{tabular} \end{scriptsize} \end{center} \section{colorForth} Eine vollständige Beschreibung ist unter \url{www.colorforth.com} verfügbar. Die relevante Besonderheit hier ist, dass die Farbe eines Wortes seine Funktion bestimmt: \begin{center} \begin{tabular}{lll} rot &- &definiere ein neues Wort\\ grün &-& übersetze\\ gelb &-& führe aus\\ weiß &-& kommentiere \end{tabular} \end{center} Um Farbe in diesem Artikel zu vermeiden: \begin{center} \begin{tabular}{lll} GESPERRT &-& definiere\\ normal & - & übersetze\\ {\bf fett} & - & führe aus\\ {\it kursiv} & - & kommentiere \end{tabular} \end{center} {\bf Fett} wird auch benutzt, wenn Wörter im Text referenziert werden. \medskip \label{c18-1} \emph{Fortsetzung auf Seite \pageref{c18-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: \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. \hfill Übersetzt von Ulrich Hoffmann \end{multicols} [1] Englisches Original unter \url{http://www.complang.tuwien.ac.at/anton/euroforth/ef01/moore01a.pdf} \end{comment} \end{multicols} \end{document}