hex \ Alle Wertangaben sind hexadezimal zu verstehen. \ Die drei Worte lit branch ?branch koennen aus dem Satz von Primitives aus \ [WS09] herausgenommen werden. Ich kann sie (unter alleiniger Verwendung der \ uebrigen Primitives aus [WS09]) als Colon-Definitionen fassen: : lit ( -- n ) \ Dieses Wort ist das uebliche Wort aus F83 [RZ87] r@ @ r> 2 + >r ; \ oder TF83 oder anderen Forth-Systemen zur Behandlung \ von Konstantwerten im Quelltext, die auf den Stack \ gelegt werden sollen. Es braucht nicht als Primitive \ gefuehrt zu werden. Um ein Beispiel zu nennen: In \ der Colon-Definition : xxx lit 4711 ; wird bei \ Aufruf von xxx der Wert 4711 auf den Stack gelegt \ und danach wird an der Adresse nach der Adresse \ fortgefahren, an welcher 4711 enthalten ist. (In \ Turbo-Forth wird fuer lit die Bezeichnung (lit) \ verwendet. lit habe ich in der klammerlosen \ Schreibweise von [WS09] uebernommen.) : branch ( -- ) \ Auch dieses Wort ist ueblich und auch dieses Wort r> @ >r ; \ braucht nicht als Primitive gefasst zu werden. Es \ Es ist fuer konstant vorgegebene (unbedingte) \ Spruenge in einer Colon-Definition zustaendig. \ Bei Aufruf des nachfolgenden Beispiels aa2 wird an \ der Adresse jener Zelle innerhalb aa2 fortgefahren, \ in der aa1 enthalten ist (Sprung nach aa1). Die \ Zelle (in aa2) hinter branch (das Wort dup wird hier \ als Platzhalter verwendet) muss vor dem Sprung mit \ der Adresse jener Zelle (in aa2) belegt worden \ sein, die das anzuspringende Ziel (hier mit Inhalt \ aa1) enthaelt. \ --------------------------------------------------- \ Selbstverstaendlich kann das Sprungziel mitten aus \ dem Geschehen heraus durch Umbelegung des aa1-Wertes \ (an der Adresse hinter branch im Wort aa2) jederzeit \ beliebig veraendert werden! \ --------------------------------------------------- \ Vergleiche das Wort then in if-then-Strukturen fuer \ diese Art von Spruengen. (Fuer die Auszaehlung im \ Dictionary: Der Wert 4 im Beispiel aa2 wird vom \ Colon-Compiler als lit 4 compiliert und benoetigt \ also 4 Bytes - und nicht etwa nur 2 Bytes!) \ Verwendung nur innerhalb von Colon-Definitionen! : aa1 ." Alles klar!" ; : aa2 branch dup 4 . aa1 ; ' aa2 >body 0a + ' aa2 >body 2 + ! \ Das Wort ?branch stellt, anders als branch, eine Moeglichkeit zur Verzweigung \ (zu bedingten Spruengen) dar. Um auch das ohne Assembler und ohne die \ Immediate-Eigenschaft bewaeltigen zu koennen, mache ich von der Idee aus \ [FB11] Gebrauch, das Wort d+ als Primitive im Austausch gegen +c aus [WS09] \ als Primitive zu verwenden. Ausserdem benoetige ich fuer ?branch noch einige \ andere Dinge, die ich zunaechst einmal alle dem Wort ?branch vorziehen darf. \ In Willi Strickers VD-Arbeit [WS09] wird +c als Primitive verwendet, um dem \ Umstand Rechnung zu tragen, dass es in Forth keine High-Level-Flags gibt. \ Das Wort +c ist in Forth-Systemen unueblich. In [FB11] konnte ich zeigen, \ dass bei Verwendung des ANS-Forth-Wortes d+ als Primitive ein vollgueltiger \ Ersatz fuer +c geschaffen werden kann. Den Trick mit d+ zur Herausschaelung \ von einfach-genauen Abfragen auf Gleichheit mit 0 habe ich aus einer \ Bemerkung von Albert Nijhof gelernt [AN06]. \ Im vorliegenden Artikel leistet mir d+ gute Dienste, um bedingte Spruenge in \ High-Level-Forth darzustellen. Fuer Nachpruefzwecke ist es nicht unbedingt \ noetig, d+ als Primitive (d.h. als Code-Definition) gefasst zu haben. Ich \ brauche hier aber d+ zum Nachweis dessen, dass beim Minimal-Primitive-Satz \ von Willi Stricker und dessen 'Kernel' [WS09] das Primitive ?branch einfach \ weggelassen werden kann. \ Ich darf die TF83-Code-Definition fuer d+ aus [FB11] wiederholen und ich \ verwende im weiteren Verlauf d+ anstelle des Strickerschen +c als Primitive. \ Das +c (mit den Teilaspekten + und c) sei wie in [FB11] aus d+ hergeleitet. \ Die folgenden Stackparameter d1, d2 und d1+d2 fuer das unter TF83 gefasste \ d+ sind Punktzahlen, also 32 Bit breit (doppelt-genau). code d+ ( d1 d2 -- d1+d2 ) ax pop bx pop cx pop dx pop cx push dx push ax push bx push 66 c, ax pop 66 c, bx pop 66 c, ax bx add 66 c, bx push ax pop bx pop ax push bx push next end-code \ Das Forth-Wort d+ hat gegenueber +c aus [WS09] den Vorteil, dass es sich \ hier um ein (ganz normales) ANS-Forth-Wort (aus dem Double-Word-Set) [AF94] \ handelt. (In [WS09], wie in vielen anderen Systemen auch, wurde das Wort d+ \ aus dem Grundsystem von Primitives hergeleitet.) \ Neben den Primitives lit und branch enthaelt [WS09] noch das (fuer bedingte \ Vorwaertsspruenge (if-then etc.) wichtige) Wort ?branch. Auch das kann als \ Colon-Definition geschrieben werden - und kommt dabei ohne die Eigenschaft \ 'immediate' aus. Es kann also im Satz von Primitives in [WS09] weggelassen \ werden. Schwierigkeiten hat mir (zunaechst) der Umstand gemacht, dass ich \ nicht ohne zwei unterschiedliche Ruecksprung-Ebenen (fuer Sprung oder \ Nicht-Sprung) auskam und dafuer ein Hilfswort einfuehren musste, fuer das \ es auf den ersten Blick keine rechte Rechtfertigung zu geben scheint. \ Auf die Frage, die mich eigentlich interessierte, naemlich, ob es moeglich \ ist, den vollen if-then-Konstrukt mit seinen Derivaten als Colon-Definition \ zu schreiben, habe ich bisher leider noch keine zufriedenstellende Antwort \ gefunden: Wie soll man mit einem Einpass-Compiler beim Compilieren (mit dem \ Colon-Compiler) ein Sprungziel an den Anfang des if-then-Konstrukts in die \ diesen enthaltende Colon-Definition setzen, das erst nach der Compilation \ von if-then - nach dem Abschluss durch then - zur Verfuegung steht (erst \ dann zur Verfuegung stehen kann)? Ich darf diese Frage fuer spaeter \ zurueckstellen. \ Als Vorarbeit darf ich das Hilfswort ?branch2 (zur Einleitung von bedingten \ Vorwaertsspruengen mit fester Sprungweite 2 in Colon-Definitionen) \ vorschlagen. (Mir geht es hier in erster Linie um einen minimalen Satz von \ Primitives, nicht so sehr um die Minimierung der Anzahl von benoetigten \ Colon-Definitionen fuer den Kernel aus [WS09]). Die Bedingung (fuer den \ Sprung) holt sich ?branch2 vom Stack: Sprung erfolgt bei fl=-1 (-1=true). \ Das fuer ?branch eigentlich benoetigte Hilfswort ist (hier bei mir) nicht so \ sehr ?branch2, sondern vielmehr (branch) - die Klammern gehoeren absichtlich \ zum Wort. Andererseits hilft mir ?branch2 beim Aufbau von ?branch (also \ Hilfswort fuers Hilfswort). Und zudem entwickelt ?branch2 ein Eigenleben \ bei der Konstruktion von ?dup min max dabs lshift rshift ohne 'immediate' \ und ohne Assembler (als Colon-Definitionen - siehe weiter unten). \ Vorher erst noch das Wort 0= aus [WS09] als Colon-Definition. Es benoetigt \ neben dem Primitive d+ aus [FB11] das Primitive drop und die Kernel-Worte \ swap und - (minus) aus [WS09]. : 0= ( n1 -- n2 ) \ n1=0 -> n2=-1 ; sonst n2=0 0 -1 0 d+ swap drop 1 - ; \ 0= hat die uebliche Wirkung: Will man generell aus \ dem nicht eindeutig gegebenen Logik-Stackwert '<>0' \ den fuer viele Zwecke genehmeren Wert -1 (fuer true) \ machen, dann erreicht man das, wie ueblich, auch \ hier per 0= 0= . Das ist ein Beispiel, bei welchem \ d+ in einer Verzweigung hilft, Assembler-Code zu \ umgehen. : ?branch2 ( fl -- ) \ Dieses Wort sorgt in einer Colon-Definition dafuer, 0= 0 -1 0 d+ swap \ dass das Wort, welches unmittelbar auf eben dieses drop dup + \ ?branch2 folgt, uebersprungen wird, wenn fl=0. Ist r> + >r ; \ dagegen fl<>0, dann wird nichts uebersprungen. Bei \ Anwendung dieser Ueberlegung auf das unten stehende \ Wort ?branch beachte man unbedingt, dass das dem \ ?branch2 vorangestellte Wort 0= die Stackwirkung und \ damit die Wirkung von ?branch2 umkehrt. Im unten \ aufgefuehrten Wort ?branch wird also (branch) \ ausgefuehrt, wenn fl=0, und aber uebersprungen, wenn \ fl<>0. \ Achtung: Im Gegensatz zum ueblichen Wort ?branch \ wird hinter ?branch2 kein Sprungziel aufbewahrt: \ Die Sprungweite ist bei ?branch2 zu 2 vorgegeben. \ Eine Erweiterung zu ?branchc, wobei c eine fest \ vorgegebene Zweierpotenz ist, bietet sich sofort an. : (branch) ( -- ) \ Springt an eine (vorgegebene) Ziel-Adresse. r> r> @ >r >r ; \ Zusammenwirken wie im folgenden Beispiel bb1, mit \ dem in bb1 enthaltenen ?branch, welches wiederum \ eben dieses (branch) enthaelt. Man kann sagen, \ (branch) wirkt wie branch, aber mit einer weiteren \ (mit einer dazwischengeschobenen) Ruecksprung-Ebene. : ?branch ( fl -- ) \ In der aufrufenden Colon-Definition (als Beispiel 0= dup \ nehme ich bb1) wird der Speicherplatz hinter ?branch ?branch2 (branch) \ (den ich durch dup fuer die Belegung mit dem dup ?branch2 dup \ Sprungziel frei gehalten habe) uebersprungen, wenn drop drop ; \ fl<>0. Ist dagegen fl=0, dann nicht. \ Die Erklaerung von ?branch darf ich zur Uebung \ stellen. Geht es mit weniger dups und weniger drops? \ Beispiel: : bb1 ( fl -- ) ?branch dup \ dup = Platzhalter zur Aufbewahrung des Sprungziels 4 . 5 . 6 . ; \ fl=0 -> 6 ok fl=-1 -> 4 5 6 ok \ An Stelle von dup laesst sich auch mit anderen 'Platzhaltern' arbeiten. Ich \ habe das mit swap und drop nachgeprueft. Ich konnte so die Verwendung des \ Compilier-Kommas als Primitive vermeiden. Letzteres ist nicht Bestandteil \ des Satzes von Primitives aus [WS09]. \ Im Moment noch (als Notmassnahme der Vorbereitung zur manuellen Einbringung \ des Sprungziels in den Platzhalter im Beispiel bb1): ' bb1 >body 10 + ' bb1 >body 2 + ! ( [ret] ) \ Auf dem Weg zum Thema des Titels \ -------------------------------- \ Es folgen die von Coos Haak vorgeschlagenen Kontrollstruktur-Worte, die als \ Colon-Definitionen gefasst und nicht immediate sind [CH93]. Eben diese Worte \ haben mich dazu veranlasst, ueber 'High-Level-Forth' und den nach aussen hin \ nicht erkennbaren Unterschschied zwischen immediate und nicht-immediate \ nachzudenken. : begin ( -- ) r@ >r ; \ Kopiert die Adresse nach begin auf den Returnstack. : again ( -- ) r> drop \ Entfernt die Adresse nach again vom Returnstack. r@ >r ; \ Springt an die Adresse nach begin. : exit ( -- ) r> drop \ Entfernt die Adresse nach begin vom Returnstack. r> drop ; \ Das wirkt genau so wie das urspruengliche exit. \ Eine Schattenseite tut sich insofern auf, als dieses exit nicht ganz das von \ TF83 und anderswoher gelaeufige exit ist. \ Fortfuehrung der Idee von Coos Haak \ ----------------------------------- \ Das folgende Beispiel zeigt eine Moeglichkeit eines (zugegeben 'primitiven') \ begin-until-Konstrukts, das ohne Assembler und ohne Immediate-Worte auskommt. : cc1 4 begin 1 + dup . dup 47 = ?branch2 exit again ; \ Dieses Beispiel bildet die Zahlen 5-47 auf dem Bildschirm ab. Es zeigt die \ Brauchbarkeit des oben eingefuehrten Wortes ?branch2 und die Brauchbarkeit \ der Haakschen Worte begin exit again. Erwarten wuerde man hier eine \ Zusammenfassung der Wortfolge ?branch2 exit again zum ueblichen until (im \ vorliegenden Zusammenhang ohne Assembler und ohne Immediate). Doch dazu \ fehlen mir noch ein paar Ueberlegungen. Ich verschiebe das auf spaeter. \ Eine Unsauberkeit besteht darin, dass beispielsweise im leicht erweiterten \ Beispiel : cc2 4 begin 1 + dup . dup 47 = ?branch2 exit again drop ; \ immer noch der Abfrage-Ueberhang 47 auf dem Stack zurueckbleibt. Das drop \ kommt nach Aussprung aus begin-until nicht mehr zur Wirkung. Ganz so leicht \ ist es mit until also nicht! \ Weitere Worte aus [WS09], hier ohne if und then \ ----------------------------------------------- \ In Willi Strickers Kernel aus [WS09] war ?dup (mit der Nummer 11:) das erste \ Wort, das mich nachdenklich stimmte: Wo kommen if und then her? In den \ Primitives von [WS09] sind sie nicht enthalten. Man muesste sie ueber die \ zugelassenen Primitives oder/und die schon definierten Colon-Definitionen \ erst einmal definieren. Zugelassen sein sollte zwar ein funktionsfaehiger \ Colon-Compiler (irgendeiner), aber noch nicht unbedingt ein vollstaendiges \ Forth-System. Und was, wenn der Colon-Compiler 'nur' als Primitive zur \ Verfuegung steht (ein Ein-Wort-Compiler [JF09], irgendwie in Form einer \ ausfuehrbaren DOS-Datei)? \ Ich gebe im Folgenden ein paar Worte aus dem Kernel von [WS09] an, bei \ welchen ich ohne if und then (und natuerlich auch ohne Assembler) auskomme. \ Vollstaendigkeit wird (hier noch) nicht angestrebt. Man beachte, dass ich \ dabei kein Immediate-Wort) benoetige. : compile ( -- ) r> \ Das ist genau die Definition von compile aus TF83. dup 2 + >r @ , ; \ Sie nutzt die Ruecksprung-Adressen-Manipulation. \ Fuer dieses Wort, das bestechend einfach aussieht, \ hier aber eigentlich sonst gar nicht verwendet wird, \ muesste man den Strickerschen Minimal-Primitive-Satz \ um das Wort , (oder zumindest um c,) erweitern. \ Die folgenden Worte aus [WS09], die ich hier ohne if-then darstelle, koennen \ als weitere Beispiele fuer eine Anwendung von ?branch2 dienen. : ?dup ( n -- n n ) dup ?branch2 dup ; : min ( n1 n2 -- ) over over > ?branch2 swap drop ; : max ( n1 n2 -- ) over over < ?branch2 swap drop ; : abs ( n -- !n! ) dup negate max ; : dabs ( d -- !d! ) dup 0< ?branch2 dnegate ; \ Und abschliessend noch lshift und rshift. In TF83 gibt es weder lshift noch \ rshift. Es handelt sich dabei aber um ANS-Core-Worte [AF94], die im Kernel \ von [WS09] auf jeden Fall ihre Berechtigung haben. : (lshift) begin dup 0= ?branch2 exit 1 - swap dup + swap again ; : lshift ( n1 u -- n2 ) (lshift) drop ; : (rshift) begin dup 0= ?branch2 exit 1 - swap u2/ swap again ; : rshift ( n1 u -- n2 ) (rshift) drop ; \ Ich benoetige hier die Hilfsworte (lshift) und (rshift). Geht es auch ohne \ diese Hilfsworte? Die Schwierigkeiten liegen im Wort exit. exit springt \ nicht etwa nur hinter again, sondern hinter das Semikolon, es verlaesst \ also gleich die ganze Colon-Definition. Und das liegt nicht speziell am \ exit aus [CH93]: Mit dem exit aus F83 oder TF83 oder aus ANS-Forth \ bestuende dasselbe Dilemma. Schade, denn das Ueberspringen per ?branch2 \ waere eine einpraegsame Methode! Aber nicht so schlimm, denn Assembler-Code \ wird auch hier immer noch nicht benoetigt. Und darauf kam es mir an. \ ------------------------------------------------------------------------- \ Bei einer Klausur (liegt schon weit zurueck) kommen einem die besten \ Einfaelle, wenn es Zeit ist abzugeben. Beim Schreiben eines VD-Artikels \ ist es aehnlich...: \ Schleifen, die sich nur auf schon durchlaufene Stellen im Quelltext \ beziehen, muessten eigentlich alle nach dem hier besprochenen Schema \ behandelbar sein. Dazu gehoeren (neben dem oben erwaehnten begin-again \ aus [CH93]) auch begin-until und begin-while-repeat. Bei begin-until \ haette ich es beinah schon geschafft. Aber nur beinahe! Die weitere \ Beschaeftigungsrichtung ist damit jedoch bereits vorgegeben. \ Turbo-Forth-Feinheiten: TF83 ist eigentlich auf ein Zusammenarbeiten mit \ ansi.sys in der config.sys eingestellt. Unter XP und hoeher sind diese \ Begriffe nicht mehr bekannt. Was tun? Aus dem Internet ansi.com holen und \ unter XP aufrufen, geht nicht. XP weigert sich. Man kann aber nach unter \ XP aufgerufenem Turbo-Forth die 'Attribute' auch einfach per ctrl-F1 \ off-schalten. Dadurch geht nichts Wesentliches verloren - aber der \ anfaengliche Bildschirmsalat verschwindet.