%\documentclass[ngerman]{article} %\usepackage[T1]{fontenc} %\usepackage[latin1]{inputenc} %\setcounter{secnumdepth}{0} %\setcounter{tocdepth}{0} %\usepackage{alltt} % Forth von der Pike auf % Teil 6 %\begin{document} %\title{Forth von der Pike auf --- Teil 6} %\author{Ron Minke} %\maketitle %\newcommand{\inst}[1]{\texttt{#1}} %\newenvironment{asm}{\begin{center}\begin{tabular}{llp{0.6\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 \emph{Vijgeblaadje} unserer niederländischen Forth--%Freunde.\\ Übersetzung: Fred Behringer. %Dieses ist die Folge sechs der Geschichte über den Versuch, ein Forth--System mit der Voraussetzung \emph{from %scratch}\/, zu erstellen. %\begin{multicols}{2} % In dieser Folge sehen wir uns die Hardware--Ausstattung des Systems an. Wir sehen uns nun (im ursprünglichen Teil 6 dieser Artikelserie) die Hardware--Ausstattung des Systems an. Das System hat eine Standard--Ausführung und besteht aus einem AVR--Prozessor, einem Adress--Latch und einem RAM--Speicher. Man beachte, dass das übliche EPROM fehlt. Das sitzt als Flash--Speicher im Prozessor selbst. Und in diesem Flash--Speicher sitzt nun wiederum unser Vorhaben, das AVR--Forth. Wir gehen davon aus, dass ein serieller Anschluss in Form eines im Prozessor--Chip vorhandenen UARTs zur Verfügung steht. Auch müssen wir natürlich einen AVR--Prozessortyp wählen, der externes RAM ansteuern kann (das können sie durchaus nicht alle). \subsection{Indirekt gefädelt, direkt gefädelt, unterprogrammgefädelt???} Bevor wir die verschiedenen Fädelungsarten (threads) besprechen, brauchen wir noch eine Methode, \mbox{\inst{:}--Definitionen} in Forth ins RAM zu setzen. Wie wir das tun, ist im Augenblick nicht so wichtig. Wir gehen davon aus, dass es ganz \emph{normal} möglich ist. Ausgangspunkt ist die Definition \begin{verbatim} : QUADRAT DUP * ; \end{verbatim} Irgendwo in unserem Forth--System verwenden wir das Wort \inst{QUADRAT}. Um das zu Sagende besser ins Bild zu bringen, nehmen wir ein paar fiktive Speicheradressen an, wo die verwendeten Worte abgelegt sind. Den {\bf Aufruf} von \inst{QUADRAT} finden wir beispielsweise an der Hex--Adresse 9812. Die {\bf Definition} von \inst{QUADRAT} finden wir an Adresse 834F. Das Wort {\tt DUP} ist ein Low--Level--Codewort an Adresse 0426 im Flash--Speicher und \inst{*} ist eine High--Level-- (eine \inst{:}--) Definition an Adresse 1278 im Flash--Speicher. Für ein traditionelles indirekt gefädeltes Forth können wir jetzt den folgenden Speicherauszug zeichnen. Abbildung \ref{minke6:indirect} zeigt ein Beispiel bei dem je zwei Bytes eine Zelle bilden. %\begin{figure*} %\includegraphics[width=\textwidth]{2006-03/indirect} %\caption{Indirekte Fädelung}\label{minke6:indirect} %\end{figure*} Wir nehmen an, dass der IP jetzt auf die Stelle 9812 zeigt, so dass also bei Aufruf von \inst{NEXT} die Definition \inst{QUADRAT} ausgeführt wird. Was geschieht nun genau? Wir verwenden den einfachen Befehlssatz, den wir in Teil 1 eingeführt hatten (man achte auf die Zahlenbeispiele): \begin{asm} \lab{NEXT:}{IP zeigt auf das als nächstes auszuführende Wort an Adresse 9812}\\ \\ \instr{MOV W,(IP)}{Kopiere den Inhalt von IP (=8359), die CFA des als nächstes auszuführenden Wortes, ins Register W.} \end{asm} \begin{asm} \instr{INC IP}{Setze den IP so, dass er auf das Wort NACH dem momentan bearbeiteten zeigt, um da unmittelbar weiterzuarbeiten (=9814).}\\ \instr{MOV Z,(W)}{Führe den Maschinencode aus, dessen Adresse jetzt im W--Register (=0734) sitzt. Verwende Z als Zwischenregister.}\\ \instr{JMP (Z)}{Gehe über einen indirekten Sprung in den Maschinencode.} \end{asm} \begin{figure*} \rotateright{\includegraphics[width=\textwidth,angle=90]{2006-03/direct}} \caption{Direkte Fädelung}\label{minke6:direct} \end{figure*} Wäre \inst{QUADRAT} eine Maschinencode--Definition gewesen, wären wir nun fertig. Das Stückchen Maschinencode wird ausgeführt und wir springen auf das nächste \inst{NEXT} zurück, das uns zu Platz 9814 leitet. \inst{QUADRAT} ist jedoch ein High--Level--Wort. Es enthält keinen Maschinencode, sondern einen thread, eine Liste von Adressen. Um diese Definition ausführen zu können, muss der Interpreter an Adresse 835B, dem Parameterfeld von \inst{QUADRAT}, aufs Neue gestartet werden. Wir müssen aber auch den alten Wert von IP sichern, um da dann fortfahren zu können, wenn das Wort \inst{QUADRAT} abgearbeitet ist. Da wir gerade einen indirekten Sprung ausgeführt haben, müssen wir hierzu auf ein Stückchen Maschinencode stoßen. Das ist der Code für \inst{DOCOL} (sehen Sie noch einmal kurz bei Teil 5 dieser Serie nach). Dieses Stückchen Maschinencode ist für jede High--Level--Definition dasselbe. Wir wiederholen den Pseudocode: \begin{asm} \lab{DOCOL:}{}\\ \\ \instr{PUSH IP}{Sichere den momentanen IP auf dem Returnstack (=9814)}\\ \instr{INC W}{Zeige auf das nächste Wort (=835B)}\\ \instr{MOV IP,W}{Kopiere diesen Pointer als neuen IP}\\ \\ \instr{NEXT}{Fahre beim nächsten Wort fort} \end{asm} Wir sind nun bei der Ausführung der Definition \inst{QUADRAT} eine Ebene tiefer gerutscht. Die Situation, in der wir uns jetzt befinden, ist eigentlich dieselbe wie an unserem Ausgangspunkt, das Ausführen einer Reihe von Definitionen (Worten). Wir sind auf indirekte Weise hierher gelangt, über Zeiger (Pointer). Diese Vorgehensweise ist die traditionelle altmodische Art von Forth. Das ist die indirekt gefädelte Methode. Es ist klar, dass wir auch noch zurück müssen. Haben wir das Wort \inst{*} auf dieselbe Weise ausgeführt, so stoßen wir auf das Wort \inst{EXIT}. Dieses Wort ist der Returnbefehl, der in den Speicher gesetzt wird, wenn der Forth--Compiler auf das Wort \inst{;} (Ende der Definition) trifft. \inst{EXIT} macht das Umgekehrte von \inst{DOCOL}. In Pseudocode sieht das so aus: \begin{asm} \lab{EXIT:}{Die Rückkehradresse steht auf dem Returnstack}\\ \\ \instr{POP IP}{Stell die Adresse des als nächstes auszuführenden Wortes wieder her (=9814)}\\ \instr{NEXT}{Fahre dort fort, wo wir nach dem Ausführen des Wortes QUADRAT verblieben waren} \end{asm} Die charakteristischen Merkmale eines indirekt gefädelten Forths: Jedes Forth--Wort hat ein Codefeld von genau einer Zelle (hier 2 Bytes). High--Level-- (\inst{:}--) Definitionen compilieren für jedes Wort genau eine Zelle in die Definition. Der Forth--Interpreter muss doppelt indirekt arbeiten, um die Adresse des schließlich auszuführenden Maschinencodes zu finden, erst über IP, danach dann über W. \begin{figure*} \rotateright{\includegraphics[width=\textwidth,angle=90]{2006-03/subroutine}} \caption{Unterprogramm--Fädelung}\label{minke6:subroutine} \end{figure*} \subsection{Direkt gefädelter Code} Der Unterschied ist klein: Beim direkt gefädelten Code steht im Codefeld keine Adresse, sondern Maschinencode. Oft hat der Code die Form von \inst{JMP} adresse oder \inst{JSR} adresse, aber es kann natürlich auch eine vollständig ausgeschriebene Routine sein, die auf \inst{NEXT} endet (siehe Abbildung \ref{minke6:direct}). %\begin{figure*} %\includegraphics[width=\textwidth]{2006-03/direct} %\caption{Direkte Fädelung}\label{minke6:direct} %\end{figure*} Da \inst{NEXT} nun eine Indirektionsstufe weniger auszuführen braucht, wird es ein Stückchen einfacher: \begin{asm} \lab{NEXT:}{IP zeigt auf das als nächstes auszuführende Wort auf Adresse 9812}\\ \\ \instr{MOV W,(IP)}{Kopiere den Inhalt von IP (=8359), die CFA des als nächstes auszuführenden Wortes, ins W--Reg.}\\ \instr{INC IP}{Setze den IP so, dass er auf das Wort nach dem momentan bearbeiteten zeigt (=9814), um da unmittelbar weiterarbeiten zu können.}\\ \instr{JMP (W)}{Springe direkt zum Maschinencode (=0734)} \end{asm} Der letzte Befehl in diesem Beispiel ist ein \inst{JMP}. Und hier liegt zugleich das Problem unseres AVR--Forth--Systems: Der Prozessor wurde so entworfen, dass im RAM--Speicher nichts anderes als Daten liegen können. Der auszuführende Maschinencode findet sich einzig und allein im FLASH--Programmspeicherbereich des Prozessors. Und sollten Sie die Maschinencodebefehle ins RAM setzen, dann könnten diese auf keine einzige Art ausgeführt werden. Der Trick des Aufeinanderlegens der beiden Speicherplatzarten (Programmbereich und Datenbereich), so wie er bei der Prozessorserie 8052 üblich ist, greift hier nicht. Schade, mit dem AVR--Prozessor ist keine direkt gefädelte Forth--Version möglich! \subsection{Unterprogrammgefädelter Code} Eigentlich können wir es nun schon erraten: Ein unterprogrammgefädeltes Forth besteht aus einer Anzahl von hintereinander platzierten Unterprogrammaufrufen (Calls) der verwendeten Worte. Aber wenn wir die Calls im RAM unterbringen, können wir auch hier leider keinen Maschinencode ausführen. Abbildung \ref{minke6:subroutine} zeigt der Deutlichkeit halber einen Speicherauszug. %\begin{figure*} %\includegraphics[width=\textwidth]{2006-03/subroutine} %\caption{Unterprogramm--Fädelung}\label{minke6:subroutine} %\end{figure*} Man beachte, dass sich durch die Verwendung von Unterprogrammaufrufen die Adressen, an denen die Calls stehen, verändert haben! Der \inst{CALL}--Befehl selbst nimmt einen gewissen Platz ein. Unsere Wahl ist einfach: {\bf Entscheidung 9:} Wir wählen ein indirekt gefädeltes AVR--Forth! %\hfill --- Wird fortgesetzt --- %\end{multicols} %\end{document}