% Content-encoding: UTF-8 \documentclass[ngerman]{article} \usepackage[utf8]{inputenc} \usepackage{multicol} % \usepackage{babel} \usepackage{xspace} \setcounter{secnumdepth}{0} \setcounter{tocdepth}{0} %\newcommand{\code}[1]{\texttt{#1}} %\newcommand{\ret}{\textsf{$<$ret$>$}\xspace} %\newcommand{\ret}{$\hookleftarrow$\xspace} \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} \renewcommand{\figurename}{Listing} \begin{document} \title{GO-COLON als Highlevel--Pendant zu GO in Turbo--Forth und ZF} \ifx\shorttitle\undefined\else \shorttitle{GO-COLON} \fi \author{Fred Behringer} \begin{document} \maketitle Angenommen, \texttt{' dup >body} ergibt im konkreten Fall \texttt{3ff}. Dann kann man in Turbo--Forth und in ZF in das Parameterfeld der Code--Definition dup springen und die Eingabe \texttt{4711 3ff go} beispielsweise liefert \texttt{4711 4711} auf dem Stack. Man kann mit \texttt{go} so auch mitten in das Parameterfeld (nicht nur an den Anfang) einer beliebigen Code--Definition springen und den Rest der Code--Sequenz ab da abarbeiten lassen. Mit \texttt{go-colon} aus dem vorliegenden Artikel gelingt das auch bei Colon--Definitionen --- und das sogar auf überraschend einfache Weise. \begin{multicols}{2} Mit \texttt{go} kann man in Turbo--Forth und ZF mitten ins Parameterfeld (PF) einer Code--Definition hineinspringen und den Rest der Maschinenbefehle (bis zum Abschluss per \texttt{next end-code}) abarbeiten lassen. \texttt{go} erwartet eine \emph{vernünftige} Adresse auf dem Stack, d.$\,$h., es muss sich um das Anfangsbyte eines PC--Assemblerbefehls handeln. Zum Aufspüren der gesuchten Adresse steht einem \texttt{dump} zur Verfügung. Das System kann nichts davon wissen, dass es sich um eine (wenn überhaupt existente) schon vorhandene Code--Definition handelt. Also kann man per \texttt{go} auch an den Anfang einer beliebigen (dazu eigens, z.$\,$B.\ per Hexcode und \texttt{c,} konstruierten) Folge von Maschinenbefehlen im System springen --- soweit diese Folge mit dem bei Code--Definitionen üblichen Abschluss \texttt{next end-code} endet. Als Sonderfall kann man die Wirkungsweise von \texttt{execute} betrachten. \texttt{execute} erwartet die CFA eines Forth--Wortes \textsf{} auf dem Stack (die man sich mit Hilfe des Ticks per \texttt{'} \textsf{} beschaffen kann) und führt nach Aufruf der Sequenz \texttt{'} \textsf{} \texttt{execute} das Wort \textsf{} aus. Bei \texttt{execute} ist es egal, ob \textsf{} eine Code-- oder eine Colon--Definition ist. Ist es eine Code--Definition, dann wirkt \texttt{'} \textsf{} \texttt{execute} genau so, als wenn man \texttt{go} auf die PFA (die Anfangsadresse des PFs) von \textsf{} wirken lässt: \texttt{'} \textsf{} \texttt{>body go} $=$ \texttt{'} \textsf{} \texttt{execute} ('$=$' im Sinne der Funktionsgleichheit). Was fehlt, ist die Möglichkeit, mitten in das PF einer Colon--Definition hineinzuspringen und die restlichen mit ihren CFAs im PF abgelegten Worte abarbeiten zu lassen. Das geht in Forth (zumindest in Turbo--Forth und ZF) so einfach, dass man es kaum glauben kann, nämlich mit der folgenden Colon--Definition, die ich \texttt{go-colon} nennen möchte. (Ich verwende \texttt{BSP:} zur Kennzeichnung von Beispielen.) \begin{alltt} BSP: : go-colon ( ad -- ) >r ; \end{alltt} Dieses Wort \texttt{go-colon} erwartet eine \emph{vernünftige} Adresse $ad$ aus dem PF derjenigen Colon--Definition, in die hineingesprungen werden soll, und arbeitet ab dieser Adresse $ad$ den Rest der dort liegenden CFAs (bis zum abschließenden ;) ab. \emph{Vernünftig} soll heißen, dass $ad$ die CFA eines schon definierten Forth--Wortes zum Inhalt hat. Ein Sprung zwischen (\texttt{lit}) und dem von (\texttt{lit}) bestimmten numerischen Wert oder ein Sprung in das Innere eines Strings hinein wären \emph{unvernünftig}\/. Auch hier, d.$\,$h., falls unvernünftige Fälle vermieden werden, weiß das System nicht, dass es sich um die restlichen CFAs in einer Colon--Definition handelt. Also kann man auch mit \texttt{go-colon}, ähnlich wie bei \texttt{go} für Maschinencode--Folgen, an den Anfang einer beliebigen Folge von (durch ihre CFAs repräsentierten) Forth--Worten springen --- soweit diese Folge einen Abschluss mit \texttt{;} aufweist. Die Frage, wie man eine solche \emph{kopflose} Colon--Definition (eine Folge von CFAs mit Abschluss per \texttt{;}) erzeugen soll, lässt sich mit den üblichen Mitteln sofort klären. Ich gebe ein Beispiel. \begin{alltt} BSP: here ] 5 . 6 . 7 . [ !csp ; go-colon [ret] 5 6 7 ok \end{alltt} Hierbei wird über \texttt{: !csp sp@ csp ! ;} der Absicherungs--Mechanismus gegen ungewollte Stackveränderungen --- und dessen Fehlermeldungen --- ausgeschaltet. Dieses Beispiel funktioniert auch bestens in ZF! Mit \texttt{here} wird die momentane Dictionary--Adresse festgehalten (die man sich natürlich auch in einer Konstanten oder in einer Variablen aufbewahren kann). Alles, was zwischen \texttt{]} und \texttt{[} steht, wird ab \texttt{here} ins Dictionary compiliert. Und das Wort \texttt{;} schließt die \emph{kopflose} CFA--Folge \texttt{5 . 6 . 7 .} wie bei \texttt{go-colon} verlangt ab. Die Folge steht ab dann im Dictionary und ist jederzeit per \texttt{go-colon} aufrufbar --- soweit man sich die Anfangs--Adresse der Folge gemerkt hat. Im Beispiel steht sie von \texttt{here} her noch auf dem Stack und \texttt{go-colon} erweckt die kopflose Folge zum Leben: \texttt{5 6 7 ok} . An sich ist nicht einzusehen, warum man nicht auch die Compilation solcher kopflosen CFA--Folgen über die Stackveränderungs--Sicherung von Turbo--Forth und ZF absichern soll. Im folgenden Beispiel wird das gemacht. \begin{alltt} BSP: here !csp ] 5 . 6 . 7 . [ ; go-colon [ret] 5 6 7 ok \end{alltt} Die Auflösung der Absicherung gegen ungewollte Stackveränderungen, nämlich \texttt{?csp} ( \texttt{: ?csp sp@ csp @ ;} ) steckt (nach wie vor) im Semikolon (\texttt{;}). Bei Colon--Definitionen versteckt sich (in Turbo--Forth und ZF) die Einleitung der Absicherung gegen Stackveränderungen, nämlich \texttt{!csp} ( \texttt{: !csp sp@ csp ! ;} ), schon im Colon (\texttt{:}). Natürlich kann man die Erzeugung kopfloser CFA--Folgen der Bequemlichkeit halber noch über ein definierendes Wort automatisieren (Aufgabe!). Zurück zu \texttt{go}! \texttt{go} ähnelt dem BASIC--Befehl \texttt{goto}. Mit \texttt{goto} kann man (in BASIC) glänzend Spaghetti--Code erstellen. Mit \texttt{go} und \texttt{go-colon} in Forth also auch? Nicht hundertprozentig! Spaghetti--Code ist allgemein verpönt. Verpönt heißt nicht verboten. Forthler verhalten sich gegenüber Programmier--Systemen, die ihnen Vorschriften machen, reserviert. Das Streben nach Freiheit beim Programmieren ist sicher einer der Gründe, weshalb man sich mit Forth beschäftigt. (ANS--Forth macht kaum Vorschriften. ANS--Forth liefert lediglich \emph{Empfehlungen}\/.) % Eine der wenigen Ausnahmen ist das Verbot, \texttt{if} \ldots \texttt{then} hinter \verb|does>| zu verwenden.) Zur Veranschaulichung der Wirkungsweise von \texttt{go-colon} möchte ich ein Beispiel bringen, Als Demonstrationsobjekt suche ich dazu eine Colon--Definition, die in ihrem PF nur CFAs enthält --- keine Strings oder dergleichen. Zum problemlosen Hineinspringen (in die betrachtete Colon--Definition) kann ich mir dann die Anspring--Adressen durch einfaches Abzählen aus der PFA (der Anfangsadresse des PFs der Colon--Definition) heraus beschaffen. Ob die dann vom System aufzurufenden CFAs selbst zu Code-- oder zu Colon--Definitionen gehören, ist egal. Ich rekonstruiere mir zu diesem Zweck (umständlich, aber natürlich nur zur Demonstration) das Forth--Wort \texttt{8*} als Colon--Definition. Um die Erklärungen durchsichtiger zu gestalten, konstruiere ich zunächst ein Forth--Wort, das den obersten Stackwert um genau eine Bitstelle nach links (zu höheren Bitstellen hin) verschiebt. Es sei \texttt{1-lshift} genannt (in ANS--Forth entspricht das der Kombination 1 lshift): \begin{alltt} BSP: code 1-lshift ax pop ax shl ax push next end-code \end{alltt} Damit kann ich \texttt{8*} als Colon--Definition fassen zu: \begin{alltt} BSP: : 8* 1-lshift 1-lshift 1-lshift ; \end{alltt} Das ist wirklich umständlich, verdeutlicht aber (hoffentlich) das, was gezeigt werden soll. Mit Hilfe von \texttt{dump} (oder aber über \texttt{' 8* >body} und Abzählen) suche ich mir die benötigten Adressen im PF von \texttt{8*} heraus: PFA, PFA$+2$, PFA$+4$ und PFA$+6$. (Das von mir verwendete Turbo--Forth und auch ZF sind 16--Bit--Systeme.) Ruft man diese Fassung von \texttt{8*} auf (die Warnmeldung darüber, dass \texttt{8*} schon existiert, kann ignoriert werden), dann wird der oberste Stackwert, sagen wir $n$, dreimal um je eine Bitstelle nach links verschoben. Die Zahl $n$, die ja als Binärzahl auf dem Stack liegt (im folgenden BSP ist es die Zahl $1$), wird also insgesamt mit $8$ malgenommen. Es ergibt sich (Adresswerte aus einem von mir durchgeführten Experiment in Hexdarstellung): \begin{alltt} BSP: hex [ret] ok ' 8* u. [ret] 6771 ok 6771 >body u. [ret] 6773 ok 1 6773 go-colon . [ret] 8 ok 1 6775 go-colon . [ret] 4 ok 1 6777 go-colon . [ret] 2 ok 1 6779 go-colon . [ret] 1 ok \end{alltt} Ich habe hier \texttt{u.} statt einfach nur \texttt{.} geschrieben, weil in ZF (in Turbo--Forth nicht) in der Bildschirmanzeige auch negative Adressen auftreten können und weil ich Adressen--Angaben (verständlicherweise) immer nur nicht--negativ lesen möchte. Um \texttt{go-colon} ein weiteres Mal (diesmal in anderer Weise) mit \texttt{execute} zu vergleichen, möge (mit den Adressen des eben gebrachten Beispiels) das folgende Beispiel betrachtet werden: \begin{alltt} BSP: hex [ret] ok 1 6773 @ execute . [ret] 2 ok 1 6775 @ execute . [ret] 2 ok 1 6777 @ execute . [ret] 2 ok \end{alltt} Dass hierbei immer nur $2$ herauskommt, hat seine Richtigkeit, da ja \texttt{execute} in allen drei Fällen immer nur das eine und einzige Wort ausführt, dessen CFA an der angesprungenen Adresse im PF liegt, nämlich \texttt{1-lshift}, nicht etwa den ganzen Rest der im PF noch folgenden CFAs. (Sonst hätte man ja gleich mit \texttt{execute} arbeiten können und die Einführung des Wortes \texttt{go-colon} wäre unnötig gewesen.) Ich weiß nicht mehr genau, wie ich auf \texttt{go-colon} gekommen bin. Es steht bei mir auf der Festplatte im Forth--Verzeichnis \texttt{tools} als Datei--Eintragung aus dem Jahre 1990. \texttt{go-colon} jongliert mit dem Returnstack. Das ist nicht verboten, \texttt{Do}--\texttt{Loop}s machen das auch. Interessant hierzu ist vielleicht noch der Artikel \emph{Lusstructuren maken zonder IMMEDIATE} von Coos Haak [CH] aus dem Vijgeblad 43 (1993), S.10. Dort geht es darum, Unendlich--Schleifen ohne die \texttt{Immediate}--Eigenschaft von \texttt{begin} und \texttt{again}, allein durch Operieren mit den Returnstack--Worten \texttt{r> r@ >r} , zu erzeugen. \texttt{again} entspricht in gewisser Weise dem obigen \texttt{go-colon}. Für den Aussprung lässt sich dabei \texttt{exit} ebenfalls durch direkten Zugriff auf den Returnstack fassen. Coos Haaks Vorschläge finden sich in \figurename\ \vref{Haaks}. \begin{figure*}[t] \begin{center} \begin{minipage}{0.8\textwidth} \begin{alltt} : begin ( -- ) r@ >r ; \textbackslash Kopiert die Adresse nach begin auf den Returnstack : again ( -- ) r> drop \textbackslash Entfernt die Adresse nach again vom Returnstack r@ >r ; \textbackslash Springt an die Adresse nach begin : exit ( -- ) r> drop \textbackslash Entfernt die Adresse nach begin vom Returnstack r> drop ; \textbackslash Das wirkt genau so wie das ursprüngliche exit \end{alltt} \end{minipage} \caption{\label{Haaks}Kontrollstrukturen nach Coos Haak 1993} \end{center} \end{figure*} Und zur Funktions--Überprüfung hier noch (von mir) ein Wort zur Abfrage auf [ret]. Es entspricht in etwa dem beispielsweise in \texttt{dump} sehr nützlichen Wort \texttt{stop?} von Turbo--Forth (in ZF nicht vorhanden), hier aber mit den Returnstack--Fassungen von \texttt{begin}, \texttt{again} und \texttt{exit} von Coos Haak (mit dessen \texttt{begin} und \texttt{again} und dabei aber mit dem üblichen \texttt{exit} von Turbo-Forth und ZF geht es nur nach doppelter Betätigung der Eingabe-Taste): \begin{alltt} BSP: hex : weiter? ( -- ) begin key 0d = if exit then again ; \end{alltt} Nach dem Aufruf von \texttt{weiter?} passiert beim Drücken einer beliebigen anderen Taste als der Eingabetaste nichts, aber auch rein gar nichts (die Eingabe--Schleife wird leer durchlaufen). Genau dann, wenn die Eingabetaste gedrückt wird, wird diese \texttt{begin}--\texttt{again}--Schleife, wie man es von einer solchen Schleife erwartet, verlassen. (\texttt{0d} ist der von \texttt{key} gelieferte Hexcode für die Eingabetaste.) Das System pausiert also so lange, bis die Eingabetaste gedrückt wird. Baut man in diese \texttt{begin}--\texttt{again}--Schleife dann noch ein (sinnvolles) Programm ein (beispielsweise für die Ausgabe einer jeweils weiteren Zeile im Wort \texttt{dump}), dann hat man ein schrittweise arbeitendes \texttt{dump}, das mit der Eingabetaste verlassen werden kann. (Das Ganze nur mal so, zur Übung.) Und noch was: Als Forth--Amateur hat man die Möglichkeit, sich in jedem Einzelfall interaktiv \emph{ganz schnell} zu vergewissern, dass der angewendete Trick (hier \texttt{go-colon}) auch wirklich funktioniert. Andererseits gibt es, wie oben schon gesagt, Fälle, in denen \texttt{go-colon} nicht gehen kann (nicht ohne Vorsichtsmaßnahmen). Man betrachte das Wort: \begin{alltt} BSP: : xxx 4 . 4 . 4 . ; \end{alltt} und versuche, im PF von xxx mit dem Wort \texttt{go-colon} unmittelbar vor einen der drei Werte $4$ (also zwischen \texttt{(lit)} und dem durch \texttt{(lit)} als \emph{Literal} gekennzeichneten Wert $4$) zu springen ;-) , also z.~B.\ \texttt{' xxx >body 8 + go-colon} [ret] ... Das geht selbstverständlich nicht! Mit \begin{alltt} BSP: ' xxx >body 6 + go-colon [ret] \end{alltt} dagegen geht es prima! Die hier gemachten Vorschläge beziehen sich auf Turbo--Forth und ZF. Sie sind natürlich sehr stark vom Aufbau des jeweiligen Forth--Systems abhängig. (Im vorliegenden Artikel geht es nicht um Forth als Sprache, sondern um Forth als System.) In MinForth 1.5 von Andreas Kochenburger [AK] und in 32/FORTH 3.07 von Rick VanNorman [RV] funktioniert das eine oder andere der im Vorliegenden besprochenen Vorschläge nicht. Es wäre sicher interessant zu untersuchen, inwieweit sich die Vorschläge an diese beiden Systeme (beide sind ANS--Forth) anpassen lassen. Diese Frage drängt sich auf, sobald man erkannt hat, dass die Konstruktion von Coos Haak ( \texttt{begin again exit über >r r@ r>} ) ohne Weiteres auch in 32/FORTH von Rick VanNorman funktionieren. (Ich habe das mit dem von mir oben besprochenen Wort \texttt{weiter?} überprüft.) \end{multicols} \section{Quellen} \begin{tabular}{lp{18cm}} {}[AK] & Andreas Kochenburger: MinForth --- Minimalistic Forth in C.\\ & http://home.arcor.de/a.s.kochenburger/minforth.html --- 34k .\\[1ex] {}[CH] & Coos Haak: Lusstructuren maken zonder IMMEDIATE.\\ & http://www.forth.hccnet.nl/vijgebladarchief/ --- 118k .\\[1ex] {}[RV] & Rick VanNorman: 32/Forth.\\ & http://www.taygeta.com/forthcomp.html --- 21k .\\ \end{tabular}\medskip \vfill \begin{center} \includegraphics[width=0.4\textwidth]{2010-04/goto500}\\ \emph{Dann hab ich \texttt{GOTO 500} getippt --- und hier bin ich.}\\ {\tiny Quelle: Thinking Forth} \end{center} \vfill \end{document}