% Forth von der Pike auf Teil 1 % Content-encoding: UTF-8 \usepackage{soul} \begin{document} \title{Forth von der Pike auf} \author{Ron Minke} \maketitle \newcommand{\inst}[1]{\texttt{#1}} \newenvironment{asm}{\begin{center}\begin{tabular}{llp{0.5\columnwidth}}}{\end{tabular}\end{center}} \newcommand{\lab}[2]{\multicolumn{2}{l}{\texttt{#1}}} \newcommand{\instr}[2]{ &\inst{#1} & #2} 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. Wir haben im vorliegenden Sonderheft die Übersetzungen aller acht Teile zu einem einzigen Ganzen zusammengefügt. Wir hoffen, dass es uns gelungen ist, Unstimmigkeiten, die einzig und allein auf unser Konto gehen, zu vermeiden (die Redaktion). Und hier der Text des Autors: Die folgenden Zeilen stellen den Versuch dar, ein Forth--System auf die Beine zu stellen, dessen Voraussetzung “überhaupt nix”, oder auf gut Deutsch “from scratch”, lautet. \begin{multicols}{2} Zunächst ein paar Worte über die Vorgeschichte: 1997 bringt die Firma Atmel aus ihrer AVR--Serie den Mikroprozessor AT90S8515 auf den Markt. Laut Atmel ist die AVR--Serie eine Serie von revolutionären Mikroprozessoren, die zwar die Vorteile von ähnlichen Prozessoren auf diesem Marktsegment von 8--Bittern haben, aber nicht deren Nachteile. Überdies haben Chipbäcker und Software--Spezialisten (sprich C--Compilerbauer) beim Entwickeln des Prozessors von Anfang an zusammengearbeitet. Weitere Informationen findet man auf der Atmel--Website. Ein neuer Prozessor! Für den echten Forth--Enthusiasten ist das natürlich eine Herausforderung: Da muss Forth drauf laufen! Und damit begann es ... Erst flugs den Befehlssatz des neuen Prozessors durchstudieren. Und was hat er denn an Timern, Ports, seriellen I/O--Anschlüssen und so weiter schon an Bord? Und wie sieht denn ein Forth--System auf unterster Ebene noch gleich aus? All die verschiedenen Pointer ... Wie baut sich Forth auf? Der virtuelle Forth--Computer ist ein Programm, das im Arbeitsspeicher eines echten Computers, im vorliegenden Fall unseres AVR--Mikroprozessors, läuft. Der vorhandene Speicher ist in Bereiche für verschiedene Aufgaben aufgeteilt und das Ganze sorgt dafür, dass der echte Computer den Forth--Kommandostrom verarbeiten kann. \begin{small} \begin{verbatim} Datenstack Returnstack | - - - - - - |========| | - - - - - - |========| SP RP Wortliste Terminal-Input-Buffer |========| - - - - - - | |========| - - - - - - | HERE >IN \end{verbatim} \end{small} Das ist eine schematische Wiedergabe der Funktionsteile des einfachsten Falles eines virtuellen Forth--Computers. Er besteht aus einer Wortliste (dem Dictionary), zwei Stacks, einem Terminal--Input--Buffer und eventuellem Disk--IO (Input--Output--Anschlüsse). Die virtuelle Forth--Maschine verwendet einen Satz von Registern, um die hauptsächlichsten Informationen über den Verlauf des Programms zu steuern. An Registern treten auf: \begin{tabular}{ll} SP &Datenstack--Pointer\\ RP &Returnstack--Pointer\\ IP &Interpreter--Pointer (wo wird gerade gearbeitet)\\ W &Word--Pointer (zum laufenden Wort)\\ PC &Program--Counter (Machinenprogramm--Zähler)\\ \end{tabular} Sodann beginnt die “echte Arbeit”: Wie wollen wir die Interna unseres Mikroprozessors (Register und dergleichen) zur Implementation der virtuellen Forth--Maschine einsetzen? Um diese Frage gut beantworten zu können, müssen wir erst ausfindig machen, wie die virtuelle Forth--Maschine ihre Register verwendet. Zur Verdeutlichung haben wir einen Pseudobefehlssatz für einen Pseudo--Assembler definiert. Dieser Befehlssatz enthält nur drei Befehle: \begin{asm} \instr{MOV dest,src}{Kopiere das Datenregister src ins Register dest}\\ \instr{INC dest}{Vergrößere den Inhalt des Registers dest um 1}\\ \instr{DEC dest}{Verkleinere den Inhalt des Registers dest um 1}\\ \end{asm} Es existiert auch eine indirekte Form: Das Setzen von dest oder src in Klammern signalisiert, dass es hier “um den Inhalt von” geht, und nicht um das Register selbst. Die Klammern deuten eine Indirektionsebene an. \begin{asm} \instr{MOV dest,(src)}{Kopiere Daten vom INHALT des src--Registers, d.h., wohin src zeigt, ins Register dest.}\\ \end{asm} Die Wortliste von Forth ist eine verkettete Liste von aneinandergereihten Wortdefinitionen. Man erkennt diverse eigenständige Teile (Felder): \begin{tabular}{ll} Namensfeld & Hier steht der Name des Wortes\\ Linkfeld & Ankopplung an das vorherige Wort\\ Codefeld & Zeiger auf ausführbaren Maschinencode\\ Parameterfeld & Was muss dieses Wort tun?\\ \end{tabular} Die oben stehenden Pseudobefehle brauchen wir, um uns in den aneinandergekoppelten Feldern dieser Wortdefinitionen umherbewegen zu können. Das Namensfeld und das Linkfeld sorgen dafür, dass Definitionen zu einer linearen Liste zusammengekoppelt werden können, die dann vom Textinterpreter durchsucht werden kann. Wie das genau geschieht und wie das eine oder andere in den Speicher gesetzt wird, bewahren wir uns für später auf. Das Codefeld enthält die Adresse des Code--Interpreters für diese Definition und das Parameterfeld enthält alle Informationen, die diese Definition benötigt, um ihre Aufgabe auszuführen. Die Suche nach der besten Kombination von Prozessorregistern, um Forth laufen lassen zu können, beginnt beim Wort EXECUTE im Text--Interpreter. Das Wort EXECUTE ruft ein Stückchen Maschinencode auf, um das betreffende Wort auszuführen. Hierbei müssen wir berücksichtigen, dass bei jedem Aufruf von EXECUTE die Adresse des Codefeldes (die CFA, Code Field Address) desjenigen Wortes, das ausgeführt werden soll, vom Text--Interpreter auf den Stack gelegt wird. Es folgt der Code in Pseudo--Assembler--Notation: \begin{asm} \lab{EXECUTE:}{( CFA -- ) Codefeld--Adresse steht auf dem Datenstack}\\ \instr{MOV W,(SP)}{Kopiere den obersten Datenstack--Eintrag, CFA, ins W--Reg.}\\ \instr{INC SP}{Verwerfe den Datenstack--Wert, mache einen Schritt vorbei} \\ \instr{MOV PC,(W)}{Kopiere die Adresse des Code--Interpreters in der Code Field Address in den Program Counter: Indirekter Sprung dahin}\\ \end{asm} EXECUTE kopiert die CFA in das W--Register und springt – indirekt – über den Inhalt eben dieses W--Registers zum Code--Interpreter. Der Machinencode des Code--Interpreters wird nun ausgeführt. Da das W--Register selbst weiterhin auf die CFA des Forth--Wortes zeigt, das gerade ausgeführt wird, kann der Code--Interpreter Informationen darüber einholen, wie das Forth--Wort weiter zu behandeln ist. In unserer Forth--Implementation (FIG--Forth, siehe betreffende Literatur) liegt das Parameterfeld direkt hinter dem Codefeld. Wenn die Parameterinformation benötigt wird, holen wir diese von hier aus über den betreffenden Code--Interpreter ein. Alle Code--Interpreter müssen ihren ausführenden Teil mit einem Stück Code beenden, der “NEXT” genannt wird. Der gibt die Kontrolle an den darüberliegenden Text--Interpreter zurück. Wird der Code--Interpreter von einem Hi--Level--Wort aufgerufen, so wird die Kontrolle an dieses Hi--Level--Wort zurückgegeben. NEXT setzt voraus, dass die Adresse des als nächstes auszuführenden Wortes im IP--Register aufbewahrt wird (der “Wo--bin--ich--geblieben”--Pointer). So kann der Text--Interpreter die Liste der im Parameterfeld einer Hi--Level--Definition gelagerten Adressen scannen. Das Codestück für NEXT sieht in unserem Pseudo--Assembler so aus: \begin{asm} \lab{NEXT:}{IP zeigt auf das als nächstes auszuführende Wort}\\ \instr{MOV W,(IP)}{Kopiere den Inhalt von IP, die CFA des als nächstes auszuführenden Wortes, ins W--Register.}\\ \instr{INC IP}{Lass IP auf das Wort NACH dem momentanen zeigen, um da schnurstracks weiter zu gehen.}\\ \\ \instr{MOV PC,(W)}{Führe den Code--Interpreter aus, dessen Adresse jetzt im W--Register sitzt. Dieser Wert kommt aus dem Codefeld des gerade auszuführenden Wortes (indirekter Sprung).}\\ \end{asm} Alle Worte in der Wortliste können durch EXECUTE ausgeführt werden, wenn ihre CFA auf dem Datenstack steht, oder durch NEXT, wenn die CFA, die sich in der Wortliste befindet, durch IP angezeigt wird. Anzumerken bleibt noch, dass es bei der Ausführung eines Forth--Wortes der Maschinencode des Code--Interpreters ist, der vom “Host--Computer” ausgeführt wird. NEXT und EXECUTE kriegen die Adresse dieses Code--Interpreters aus dem Codefeld der Definition des betreffenden Wortes zugewiesen. Unten werden wir weiter auf die Interna eingehen, und zwar auf das tatsächliche Abbilden der Register des AVR--Prozessors auf die Register der virtuellen Forth--Maschine, und natürlich auch auf den zugehörigen Machinencode. %\end{multicols} %\end{document}