%\documentclass[ngerman]{article} %\usepackage[T1]{fontenc} %\usepackage[latin1]{inputenc} %\setcounter{secnumdepth}{0} %\setcounter{tocdepth}{0} %\usepackage{alltt} % Forth von der Pike auf % Teil 3 %\begin{document} %\title{Forth von der Pike auf --- Teil 3} %\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. %Hier ist der dritte Teil des Versuchs, ein Forth--System auf die Beine zu %stellen, dessen Voraussetzung überhaupt nix, oder auf gut Deutsch \emph{from %scratch}\/, lautet. %\begin{multicols}{2} Wir kommen jetzt zum fünften Teil der Geschichte über das Hochziehen eines AVR--Forth--Systems mit dem Ausgangspunkt from scratch. In der vorigen Folge hatten wir IP und SP je zu je den AVR--Registerpaaren X und Y zugeordnet. Im vorliegenden Teil gehen wir auf den verwendeten Pseudocode zur Umsetzung von High--Level--Worten nach Befehlen in AVR--Maschinensprache ein. Werfen wir nochmal einen Blick auf Teil 4. Legen wir gleich mit dem Code los: \begin{footnotesize} \setlength{\tabcolsep}{5pt} \begin{tabular}{p{1.5cm}rp{1.5cm}p{3.5cm}} \multicolumn{2}{l}{DOCOL--Pseudo} & \multicolumn{2}{l}{DOCOL--Assembler} \\ \\ PUSH IP & (1) & \inst{Push XL} & Bewahre IP auf dem Returnstack auf\\ & (2) & \inst{Push XH} & \\ INC IP & (3) & \inst{Adiw WL,2} & Zeig auf das nächste Wort\\ MOV IP,W & (4) & \inst{Mov XL,WL} & Kopiere Pointer\\ & (5) & \inst{Mov XH,WH} & \\ \\ NEXT & & & Führe den Code für NEXT aus (siehe Teil 4); Fahre mit dem nächsten Wort fort\\ \end{tabular} \end{footnotesize} Zur Aufbewahrung von IP auf dem Returnstack benötigen wir zwei Befehle: Unser Prozessor ist ja nur acht Bit breit. Aus demselben Grund erhöhen wir W um zwei, damit es auf das nächste Wort (= 2 Bytes weiter oben) zeigt. \begin{footnotesize} \setlength{\tabcolsep}{5pt} \begin{tabular}{p{1.5cm}rp{1.5cm}p{3.5cm}} \multicolumn{2}{l}{EXIT--Pseudo} & \multicolumn{2}{l}{EXIT--Assembler} \\ \\ POP IP & (1) & \inst{Pop XH} & Stelle IP vom Returnstack aus wieder her\\ & (2) & \inst{Pop XL} & \\ \\ NEXT & & & Führe den Code für NEXT aus (siehe Teil 4); Weiter dort, wo wir verblieben waren\\ \end{tabular} \end{footnotesize} Auch hier zwei Befehle, um IP wiederherzustellen. Man beachte, dass es nicht nötig ist, W wiederherzustellen. W bekommt in NEXT einen neuen Wert. \subsection{Die richtige Wahl getroffen?} Wir müssen uns noch eine Antwort auf die Frage verschaffen, ob die Wahl des AVR--Registerpaares W für das Forth--Register W die {\bf richtige} Wahl gewesen ist. Das W--Register wird zur Markierung des Speicherplatzes für das nächste Wort verwendet. In diesem AVR--Forth verwenden wir W ausschließlich in High--Level--Definitionen zur Anzeige des nächsten Wortes. Dazu muss W innerhalb des Codes für \inst{NEXT} (siehe Teil 4) einen Wert bekommen und dieser Wert muss beim Verarbeiten von \inst{DOCOL} zur Verfügung stehen. Zur Verfügung stehen muss er, wie wir später sehen werden, auch in \inst{DOCOL}--artigen Konstruktionen wie \inst{DOCON} und \inst{DOVARIABLE}. Außerhalb dieser in Maschinensprache gehaltenen Teile wird weder das Forth--Register W noch das AVR--Registerpaar W benötigt. Wir können also das Registerpaar W als ein ganz \emph{gewöhnliches} Registerpaar verwenden. In den Maschinensprachteilen machen wir von der Möglichkeit Gebrauch, dass wir bei Registerpaaren auf einen Schlag gleich noch einen konstanten Wert (hier 2, die Anzahl von Bytes in einem Wort) hinzuaddieren können, um das nächste Wort zu erreichen. Das brauchen wir also nicht in zwei getrennten Byte--Additionen zu tun. Der verwendete Befehl, \inst{Adiw}, ist auf die Registerpaare W, X, Y und Z und nur auf diese anwendbar. Da wir die Registerpaare X, Y und Z bereits zugeordnet haben, ist das W--Registerpaar das letzte Paar, bei welchem dieser \inst{Adiw}--Befehl möglich ist. (Befehlssatz: Siehe Atmel--Website.) Die Wahl des AVR--Registerpaares W für das Forth--Register W ist sicher eine gute Wahl. {\bf Entscheidung 8:} Das Forth--Register W wird dem AVR--Registerpaar W zugeordnet. So, die Basis ist damit gelegt. Wir zählen die getroffenen Entscheidungen noch einmal auf: \begin{footnotesize} \setlength{\tabcolsep}{3pt} \begin{tabular}{p{3.5cm}llll} Forth--Register & \multicolumn{3}{l}{AVR--Register}\\ \\ RP & SP\\ W & WL & und & WH & (= R24 und R25) \\ IP & XL & und & XH & (= R26 und R27) \\ SP & YL & und & YH & (= R28 und R29) \\ Hilfsregister für indirekte Sprünge & ZL & und & ZH & (= R30 und R31) \\ \end{tabular} \end{footnotesize} \subsection{Struktur hineinbringen} Um dem Ganzen etwas Struktur zu verleihen, treffen wir folgende Vereinbarung über die Verwendung von Registern innerhalb des Datenstacks. Alle Datenstack--Aktionen ordnen wir ab R23 (gleich unter dem Registerpaar W) nach unten zu an, wobei R23 das obere Byte eines Wortes enthält und R22 das untere und so weiter. Benötigt ein Wort beispielsweise zwei Stack--Einträge, dann kommen wir zu folgendem Bild (wir nehmen das Wort \inst{AND} als Beispiel): \begin{asm} \lab{AND ( n2 n1 -{}- n3 )} \end{asm} \begin{footnotesize} \setlength{\tabcolsep}{3pt} \begin{tabular}{lllll} & Input & &Output & AVR--Register\\ \\ & 0 & & 0 & R19\\ & 1 n2 unteres Byte & & 1 n3 unteres Byte & R20\\ & 2 n2 oberes Byte &SP $\rightarrow$& 2 n3 oberes Byte & R21\\ & 3 n1 unteres Byte & & 3 & R22\\ SP $\rightarrow$ & 4 n1 oberes Byte & & 4 & R23\\ & 5 & & 5\\ \end{tabular} \end{footnotesize} Der Maschinencode wird nun: \begin{asm} \lab{Code\_Andd:}{}\\ \instr{Ld R23,Y+}{Hole oberen n1--Teil herein}\\ \instr{Ld R22,Y+}{Hole unteren n1--Teil herein}\\ \instr{Ld R21,Y+}{Hole oberen n2--Teil herein}\\ \instr{Ld R20,Y+}{Hole unteren n2--Teil herein}\\ \\ \instr{And R21,R23}{AND oberer Teil}\\ \instr{And R20,R22}{AND unterer Teil}\\ \\ \instr{St -Y,R20}{\mbox{Speichere unteren n3--Teil ab}}\\ \instr{St -Y,R21}{Speichere oberen n3--Teil ab}\\ \\ \instr{Next}{} \end{asm} Benötigen wir einen Vorgang, bei dem Daten hereingeholt werden müssen (indirekt, also über einen Pointer), dann verwenden wir dafür das Registerpaar Z. Eine Zuordnung dieses einen Stackeintrags zu einem Registerpaar aus der Reihe R23 \ldots entfällt dann. Nehmen wir als Beispiel den Code für das Wort \inst{@} \renewenvironment{asm}{\begin{center}\begin{tabular}{llp{0.55\columnwidth}}}{\end{tabular}\end{center}} \begin{asm} \lab{@ ( adresse -{}- wert )} \\ \end{asm} \begin{asm} \lab{Code\_At:}{}\\ \instr{Ld ZH,Y+}{Hole oberen Adressteil herein}\\ \instr{Ld ZL,Y+}{Hole unteren Adressteil herein}\\ \\ \instr{Ld R21,Z+}{Hole oberen Teil des Wertes von dieser Adresse herein}\\ \instr{Ld R20,Z+}{Hole unteren Teil des Wertes von dieser Adresse herein}\\ \\ \instr{St -Y,R20}{Speichere unteren Teil des Wertes ab}\\ \instr{St -Y,R21}{Speichere oberen Teil des Wertes ab}\\ \\ \instr{Next}{}\\ \end{asm} Wir reservieren vorläufig R23 \ldots R12 für sechs Stack--Einträge. Im Übrigen sind Forth--Worte, die mehr als sechs Stack--Einträge benötigen, nicht mehr einfach zu nennen! Inzwischen sind wir einem guten Teil von AVR--Maschinencode begegnet. Bevor wir jedoch zu einem funktionierenden Forth--System kommen, müssen wir auch die Hardware--Seite noch unter die Lupe nehmen. \begin{figure*} \rotateright{\includegraphics[width=\textwidth,angle=90]{2006-03/indirect}} \caption{Indirekte Fädelung}\label{minke6:indirect} \end{figure*} \subsection{Hardware--Umsetzung} Der AVR--Prozessor von Atmel ist ein Prozessor, der mit der Harvard--Speichereinteilung arbeitet. Das bedeutet, dass der Speicherplatz für das Programm vom Speicherplatz für die Daten ganz und gar getrennt ist (man vergleiche die Datenblätter der verschiedenen Prozessoren auf der Website von Atmel). Das stellt an den Entwurf unseres Forth--Systems spezielle Anforderungen. Die Codierungsmöglichkeiten für Forth sind: \begin{itemize} \item Indirekte Fädelung (klassisches Modell) \item Direkte Fädelung \item Unterprogramm--Fädelung \end{itemize} Die {\bf einzig} mögliche Art der Implementation auf dem AVR--Prozessor ist das klassische Forth--Modell, die indirekt gefädelte (indirect threaded) Version. Warum ist das so? Die indirekt gefädelte Version geht von einer Wortliste aus, die ausschließlich Zeiger (Pointer) enthält, welche auf einen Platz (die CFA) verweisen, der wiederum einen Zeiger auf den Maschinencode (im internen Flash--Speicher) enthält. Neue Worte, die an die Wortliste angefügt werden, bestehen ausschließlich aus Zeigern. Nirgends wird direkt auf Maschinencode verwiesen. Sehen Sie sich den in Teil 4 gebrachten Codeteil für \inst{NEXT} daraufhin noch einmal an. Die Werte, welche im Datenspeicher landen, sind {\bf echte} Daten (die Pointer). Das Ablegen von Daten und das anschließende Ausführen dieser Daten so, als wäre es Maschinencode, ist {\bf nicht} möglich; Maschinencode kann einzig und allein vom internen Flash--Speicher aus ausgeführt werden. % Im folgenden Teil wird auf die Pointer--Struktur der drei Fädelungstypen näher eingegangen. Im folgenden wird auf die Pointer--Struktur der drei Fädelungstypen näher eingegangen. Bei einem Prozessor der Serie 8051, das werden Sie danach begreifen, sind alle drei Typen möglich, indem man nämlich die beiden Speicherbereiche gewissermaßen \emph{aufeinander} legt. Bei der AVR--Prozessorserie geht das ganz bestimmt nicht und wir bleiben an das indirekte Verdrahtungsmodell gebunden. (Das finden wir aber auch nicht sonderlich schlimm. Das ist das wahre \emph{Basismodell}\/.) %\hfill --- Wird fortgesetzt --- %\end{multicols} %\end{document}