DurexFORTH [Tutorial]

  • DurexFORTH [Tutorial]

    Tutorial Thread - Bitte hier keine Fragen posten!

    Nutzt dazu diesen Thread hier: DurexFORTH - Diskussion zum Tutorial



    __________________________________
    DurexForth - Ein Selbstversuch, Teil 1

    So, nach eingehender Prüfung habe ich mich entschlossen, meine ersten Gehversuche in Forth mit DurexForth zu unternehmen.

    Sieht man einmal von dem zugegebenermaßen etwas pubertären Namen dieses Forth Compilers ab (namensgebend war hier die schwedische Retro-Hacker Gruppe "DUREX"), so kann sich das Forth System durchaus sehen lassen: DurexForth ist Open-Source (MIT License) und als Basis dient der Forth 2012 Standard. Den bei Forth Compilern üblichen Editor hat man hier in Form eines minimalen "vi" Editors (bekannt aus der UNIX und Linux Welt) umgesetzt. Mir persönlich kommt das sehr entgegen, da ich ohnehin beruflich wie privat Linux einsetze und den "vi" Editor daher täglich nutze.

    Zudem werden Quelltexte bei DurexForth nicht, wie bei älteren Compilern üblich, in einem Block basierten Dateisystem abgelegt, sondern als einfache Textdateien auf der Diskette gespeichert. Das wiederum hat u.a. den Vorteil das man diese Dateien mit Hilfe des "c1541" Programs, welches mit dem Vice Emulator daher kommt, bequem auf den PC übertragen kann.

    Last but not least sollte ich erwähnen, das DurexForth das Einbinden von zusätzlichen Sprachmodulen erlaubt. Diese Module erweitern den Compiler um Befehle zur Erzeugung von Tönen, der Darstellung von Sprites oder dem Zeichnen von Grafiken. Entsprechende Demos stehen als "Appetizer" auf dem .D64 Image bereit.
    Eine kompakte aber vollständige (englischsprachige) Dokumentation im PDF Format rundet den guten Eindruck dieses Forth Systems ab. Ich benutze bewusst den Begriff "System", denn klassische Forth Compiler haben neben Compiler und Editor oftmals auch eine art minimalistisches Betriebssystem bereitgestellt. Diesen Ansatz verfolgt auch DurexForth.

    Doch genug des Lobes, schauen wir uns das alles mal etwas genauer an. Unter (github.com/jkotlinski/durexforth/releases) kann die jeweils aktuelle Version der Dokumentation und des .D64 Images heruntergeladen werden. Im Verlaufe meines Selbstversuchs werde ich aus Gründen der Bequemlichkeit auf den VICE Emulator zurückgreifen, u.a. weil ich so ggf. Screenshots bereitstellen kann. Den Einsatz auf original Hardware kann das aber selbstverständlich nicht ersetzen. Wahre Coolness ist halt immer noch Keil- oder Brotkastenförmig. ;)

    Nachdem man nun die Diskette bzw. das Image eingelegt hat, lädt man das System mittels der bekannten Zauberformel LOAD"*",8,1 und startet es dann mit RUN.

    Beim allerersten Start lädt DurexForth einige Module nach und compiliert die darin enthaltenen Worte (Befehle). Dies dauert einen kleinen Augenblick, anschließend wird das um die soeben geladenen Module erweiterte Forth System wieder auf der Diskette gespeichert. Damit sind wir startklar und DurexForth begrüsst uns mit dem für Forth typischen Prompt:

    ok

    Bevor wir nun aber beginnen, unsere ersten Versuche mit Forth zu starten, schauen wir uns erstmal unsere Entwicklungsumgebung an, denn diese ist Umfangreicher als es das simple "ok" Prompt erahnen lässt. Daher schauen wir uns erstmal an, was neben dem dem eigentlichen Forth System noch so auf der Diskette enthalten ist. Dazu bedienen wir uns in bester UNIX / Linux Manier dem Befehl "ls" und geben diesen am Prompt ein:

    Quellcode

    1. ls


    Nun sollten wir das Directory der Diskette aufgelistet bekommen. Um einen ersten Eindruck über die Möglichkeiten dieser Sprache zu erhalten, schauen wir uns eines der Demos an. Dazu geben wir zunächst am Prompt folgenden Befehl ein:

    Quellcode

    1. require sprite

    Mit dem Befehlswort "require" werden unter DurexForth zusätzliche Sprachmodule mit weiteren Befehlsworten nachgeladen. Diese bleiben bis zum reset der Maschine im Wörterbuch unserer Forth Umgebung gespeichert. Im obigen Fall haben wir die Spracherweiterung zur Darstellung von Sprites nachgeladen. Lassen wir es ein wenig bunt werden indem wir nun das beiliegende Sprite Demo laden. Dazu geben wir am Prompt folgendes ein:

    Quellcode

    1. include spritedemo

    Nachdem das Sprite Demo geladen wurde, sollten auf unserem Blidschirm ein paar bunte "DurexForth" Sprites herumtanzen. Zeit, sich kurz zurück zu lehnen und das Spektakel zu geniessen. Das Demo kann durch drücken des bekannten "Any Key" beendet werden, woraufhin sich wieder das Forth Prompt meldet:

    ok

    Um das Demo erneut ablaufen zu lassen, geben wir am Prompt einfach den folgenden Befehl ein:

    Quellcode

    1. demo

    In gleicher Weise kann durch laden des Grafikmoduls "gfx" und des entsprechenden Demoprogramms auch das Grafikdemo aufgerufen werden. Dazu geben wir am Prompt ein:

    Quellcode

    1. require gfx

    ok

    Danach laden wir das Grafikdemo:

    Quellcode

    1. include gfxdemo

    Diese beiden kleinen Demoprogramme zeigen sehr gut die umfangreichen Möglichkeiten und die beachtliche Performance von DurexForth auf.

    Und damit wir zum Ende dieses ersten Teils auch noch etwas konstruktives leisten, wollen wir ein erstes "Hello World!" Programm schreiben. Dazu geben wir am Prompt folgendes ein (auf die Leerzeichen achten!):


    Quellcode

    1. : hello ." Hello World!" ;

    Was haben wir nun genau gemacht? In Forth werden anstelle von Funktionen, wie man sie z.B. von Pascal oder C her kennt, sogenannte "Wörter" (words) definiert welche in einem sogenanntem Wörterbuch (dictionary) abgelegt werden. Man srpicht bei Forth daher auch von einem Vokabular (vocabulary). Die Definition eines solchen Wortes wird in Forth mit einem Doppelpunkt : eingeleitet. Danach folgt, durch ein Leerzeichen getrennt, das eigentliche, zu definierende Wort - in unserem Fall hello. Als nächstes folgen nun, ebenfalls durch Leerzeichen getrennt, weitere Befehle aus dem Forth Wörterbuch. In unserem Fall ist dies der Befehl ." Hello World!" .

    Ihr ahnt es schon, ." ist das Forth Wort, um einen Text auszugeben. Beachtet bitte, das zwischen dem ersten Anführungszeichen und dem auszugebenden Text ein Leerzeichen zu setzen ist, da der Forth Compiler ansonsten das Wort ." nicht interpretieren kann. Also nicht: ."Hello World!" sondern: ." Hello World!" . Das Leerzeichen wird bei der Ausgabe des Textes nicht angezeigt, es dient lediglich zur Trennug von Befehlswort und auszugebenden Text. Am Ende des auszugebenden Textes folgt dann ein abschliessendes Anführunszeichen. Man kann also sagen das ." Hello World!" in Forth einem PRINT "Hello World!" in BASIC entspricht.

    Zu guter letzt wird unsere Definition mit einem durch ein weiteres Leerzeichen separierten Semikolon ; abgeschlossen. Nach betätigen der Enter Taste wird nun die Definition unseres Wortes hello kompiliert und dem Wörterbuch von Forth hinzugefügt. Wenn wir nun am Prompt hello eingeben,


    Quellcode

    1. hello

    dann sehen wir folgendes:

    Hello World!ok

    ... und damit sind wir am Ende von Teil 1 meiner kleinen Reise die Welt von Forth angelangt. Wie es weitergeht, seht Ihr in der nächsten Folge von "obsoltees Nerd-Wissen, das kaum einen interessiert" - stay tuned.
    -
    Gesucht:
    Hewlett Packard HP-9826 (1981)
    Jupiter ACE (1982)

    Dieser Beitrag wurde bereits 12 mal editiert, zuletzt von syshack () aus folgendem Grund: Interpunktion, Typos, Ergänzug etc.pp.

  • DurexForth - Ein Selbstversuch, Teil 2

    DurexForth - Ein Selbstversuch, Teil 2

    Im ersten Teil meines Selbstversuchs haben wir gesehen, welche Möglichkeiten in DurexForth stecken und wir konnten ein erstes "Hello World!" Programm schreiben.

    Im zweiten Teil werden wir uns mit einem wichtigen Baustein der Programmiersprache Forth beschäftigen, dem "Stack" (engl. für Stapel). Was es damit auf sich hat, versuche ich nun so gut wie möglich zu erklären. Dennoch ist das Thema besonders für Einsteiger oftmals sehr komplex (ich weiß, wovon ich rede) und daher empfehle ich Euch wärmstens, parallel zu diesem Mini-Tutorial weiterführende Literatur zur Hand zu nehmen. Leo Brodie hat das Thema "Stack" in seinem Buch "Starting Forth" (deutscher Titel: "Programmieren in Forth") wirklich hervorragend erklärt und sehr anschaulich illustirert. Die englischsprachige Ausgabe dieses Buches kann bei Forth Inc. kostenlos online gelesen werden (Leo Brodie, Starting Forth (online ed.)). Alternativ habe ich unter (Forth Kurs von H-Peter Recktenwald) einen kompakten Forth Kurs in Deutsch gefunden. Zudem sind in dem Handbuch von DurexForth die meisten der hier von mir erklärten Worte in einer Referenz aufgelistet. Das ist sehr praktisch, wenn man noch einmal nachschlagen will was ein bestimmtes Wort tut und welche Parameter es auf dem Stack erwartet und am Ende dort wieder ablegt.

    Nun aber zurück zum Thema Stack: viele Programmiersprachen wie z.B. Pascal, C, PHP oder auch PureBasic erlauben es dem Programmierer, Funktionen zu definieren die aus einer Vielzahl von Befehlen bestehen und diesen Funktionen dann Parameter zu übergeben. Soweit, so gut.

    In Forth definieren wir Wörter, welche aus einer Vielzahl von Forth Befehlen bestehen. In gewisser Weise ist ein "Wort" in Forth das Gegenstück zu einer Funktion in C oder Pascal etc.
    Nur, wie kann ich einem Forth Wort nun einen oder mehrere Parameter übergeben?

    Das ist der Moment, an dem bei Forth der Stack ins Spiel kommt. Auf dem Stack werden in Forth Ganzzahlen oder Fließkommawerte abgelegt. Ein Beispiel zur Eingabe am Forth Prompt:

    Quellcode

    1. 1 2 + .
    3 ok

    Glückwunsch! Wir haben gerade zum ersten mal den Stack genutzt um in Forth die Summe aus 1 + 2 zu errechnen. Und dabei sind wir auch gleich über eine weitere Besonderheit der Sprache Forth gestolpert: Forth nutzt bei Operationen auf dem Stack die sogenannte "postfix notation" auch RPN oder UPN (umgekehrte polnische Notation) genannt. D.h. anstatt wie in vielen anderen Programmiersprachen 1 + 2 zu schreiben, wird in Forth diese Rechnung wir folgt notiert: 1 2 +

    Ihr fragt Euch jetzt sicher: Wie bitte?! Sehen wir uns das o.g. Beispiel daher noch einmal im Detail an. Zunächst legen wir die Zahl 1 auf dem Stapel ab, dann legen wir die Zahl 2 auf dem Stapel ab. Nun sähe der Stapel, grafisch dargestellt, so aus:

    [2]
    [1]

    an oberster Stelle liegt auf dem Stapel die Zahl 2, da wir diese als letzte auf dem Stapel abgelegt haben. Darunter liegt auf dem Stapel die Zahl 1, welche wir als erstes eigegeben haben.

    als nächstes rufen wir den + Operator auf. Dieser ist ebenfalls ein Forth Wort, das als Parameter auf dem Stack zwei Zahlen erwartet, diese vom Stapel nimmt und miteinander addiert. Das Ergebnis dieser Addition wird dann wieder auf dem Stapel abgelegt. Da wir ja bereits die Zahlen 1 und 2 auf dem Stapel liegen haben, nimmt + nun diese beiden Werte vom Stapel und legt das Resultat der Addition wieder auf dem Stack ab. Damit sieht unser Stack nun so aus:

    [3]

    Da alles findet natürlich im Hintergrund statt. Damit wir uns das Ergebnis dieser Addition ausgeben lassen können, nutzen wir den Befehl . (Punkt), denn der . Befehl nimmt sich den obersten Wert vom Stack und gibt ihn aus. Hier noch ein Beispiel zur Eingabe am Prompt, um die Angelegenheit zu verdeutlichen:

    Quellcode

    1. 1
    ok

    Quellcode

    1. 2
    ok

    Quellcode

    1. .
    2 ok

    Quellcode

    1. .
    1 ok

    Da die Zahl 2 als letzte auf dem Stack abgelegt wurde, wird sie auch als erste wieder durch den . Befehl vom Stapel genommen und ausgegeben. Dann folgt der erneute Aufruf von . und nun folgt die Zahl 1, da diese nun an die oberste Stelle gerutscht ist als die Zahl 2 durch den ersten . Befehl vom Stapel genommen wurde. Dieses Prinzip nennt man "last in, first out (LIFO)" also "zuletzt rein, zuerst wieder raus".


    !! WICHTIG: per Default ist der Stack in DurexForth auf die hexadezimale Zahlenbasis eingestellt.

    D.h. wenn wir folgende Rechnung am Prompt durchführen:

    Quellcode

    1. 9 1 + .
    erhalten wir folgende Ausgabe:

    a ok

    Das ist völlig korrekt, denn a ist die hexadezimale Darstellung der Zahl 10. Um eine dezimale Darstellung zu erhalten, geben wir nach dem Start unserer Forth Umgenung am Prompt das Wort

    decimal

    ein. Dadurch wird der Stack auf die dezimale Zahlenbasis umgestellt. Um den Stack wieder auf die Verarbeitung von hexadezimalen Zahlen umzustellen, geben wir am Prompt

    hex

    ein.

    Kleiner Tip am Rande: durch das Wechseln von decimal nach hex (und umgekehrt) bleiben die Werte auf dem Stack erhalten. Das kann man prima nutzen um sich Werte von hexadezimal nach dezimal umrechnen zu lassen. Ein Beispiel, wechseln wir in den hex Modus und geben wir eine hexadezimale Zahl ein:

    Quellcode

    1. hex

    ok

    Quellcode

    1. F8
    ok

    danach wechseln wir in den decimal Modus und lassen uns den Wert auf dem Stack ausgeben:

    Quellcode

    1. decimal
    2. .

    248 ok

    Das ist völlig korrekt, denn F8 in hexadezimaler Schreibweise entsprict 248 in dezimaler Schreibweise.

    HINWEIS: Ich gehe in allen folgenden Beispielen nun davon aus, das wir unseren Stack durch Eingabe des Wortes decimal auf die dezimale Zahlenbasis umgestellt haben.

    Hier noch ein paar weitere Beispiele, wie sich das mit der RPN verhält. Links von dem -- (Doppelstrich) steht die uns bekannte Schreibweise, rechts die Schreibweise wie sie in Forth verwendet wird. Wenn Ihr die Beispiele aus der rechten Spalte ausprobieren wollt, vergesst nicht vorher auf Dezimadarstellung umzustellen (Befehl: decimal)!

    Klassisch Forth-RPN

    3 * 5 = 15 -- 3 5 * .
    (4 + 3) * 2 -- 4 3 + 2 * .


    Für die Arbeit mit dem Stack gibt es in Forth diverse Worte, mit denen die Werte welche auf auf dem Stack liegen rotiert, verdoppelt, gelöscht usw. werden können. Diese speziell für die Stack-Manipulation vorgesehenen Worte nennt man auch Stack Operatoren (stack operators). Ein für Anfänger besonders nützlicher Stack Operator ist das Wort .S welches den aktuellen Inhalt des Stack (von links, unterste Position auf dem Stack nach rechts, oberste Postiiton auf dem Stack) ausgibt, ohne dabei die Werte vom Stack zu entfernen. Ein Beispiel zur Eingabe am Prompt:

    Quellcode

    1. 1 2 3 .S

    1 2 3 ok

    Wie wir sehen, haben wir zuerst die Zahlen 1, 2, und 3 in dieser Reihenfolge auf dem Stack abgelegt. Danach haben wir uns mit dem .S Befehl den Inhalt des Stapels ausgeben lassen, ohne das die dort gespeicherten Werte verloren gehen (gelesen wird von links nach rechts. In diesem Beispiel ist die 3 die oberste Zahl auf dem Stack).

    Forth Entwickler nutzen eine bestimmte Art, um zu dokumentieren welche Werte ein Wort auf dem Stack als Parameter erwartet und was nach Ausführung des Wortes auf dem Stack abgelegt wird. Wenn wir uns noch einmal den + Operator ins Gedächtnis rufen, dann würde ein Forth Programmier den Stack für diesen Operator wie folgt dokumentieren:

    ( n1 n2 --- sum )

    Auf der linken Seite des -- (Doppelstrich, er dient hier nur als Trennzeichen zum besseren Verstndnis und ist kein Forth Wort) sehen wir, von links nach rechts, das vor den Aufruf auf dem Stack zuerst die Zahl n1 und die Zahl n2 abgelegt werden. Auf der rechten Seite des -- sehen wir, was nach der Ausführung auf dem Stack abgelegt wird, hier die Summe aus n1 + n2. Der Übersicht halber werde ich diese in Forth übliche Schreibweise für die Darstellung des Stack (ich nenne es Stackdiagramm) bei allen weiteren Erklärungen mit angeben. Ich hoffe, das dadurch die Vorgänge auf dem Stack etwas besser nachvollziehbar sind. Zudem dienen die Klammern in Forth als Kommentarzeichen, so das alles was (mit einem Leerzeichen getrennt) in runden Klammern steht vom Compiler ignoriert wird. Forth Entwickler machen häufg gebrauch von dieser Möglichkeit um ihren Code verständlicher zu halten.

    Wir haben also aktuell folgenden Zustand auf unserem Stack:

    ( 1 2 3 )

    da wir ja immer noch die Zahlen 1, 2 und 3 von vorhin dort liegen haben. Zeit, einen weiteren Stack Operator kennen zu lernen, das Wort drop.


    Gebt am Prompt einmal drop ein und lasst Euch dann mit dem .S Befehl den Zustand des Stacks ausgeben:

    Quellcode

    1. drop

    ok

    Quellcode

    1. .S
    1 2 ok

    Wie wir sehen, nimmt drop die oberste Zahl (in unserem Fall die Zahl 3) vom Stack und verwirft sie. Dabei wird, anders als bei dem . Befehl, die Zahl nicht ausgegeben. Drop löscht also immer den jeweils den obersten Wert auf dem Stack. Entsprechend sieht das generische Stackdiagramm für das Wort Drop wie folgt aus:

    ( n1 -- )

    An der obersten Stelle auf dem Stack liegt vor dem Aufruf ein Wert (n1), nach dem Aufruf ist der Stack leer. Das Diagramm oben bezieht sich exemplarisch auf den Fall, das nur eine Zahl auf dem Stack liegt und das nach Ausführung von drop der Stack dann leer ist. In unserem Fall liegen aber nun noch die Zahlen 1 und 2 auf dem Stack, wobei die 2 nach dem Aufruf von drop von der zweiten an die erste (oberste) Stelle auf dem Stack gerutscht ist:

    ( 1 2 )


    Ein weiterer Stack Operator, der sehr nützlich ist, ist das Wort dup. Dieser Befehl dupliziert den obersten Wert auf dem Stack und legt das Duplikat auf dem Stack ab. Das Stackdiagramm für dup sieht wie folgt aus:

    ( n1 -- n1 n1 )

    auf dem Stack sollten nun noch immer die Werte 1 und 2 liegen. Führen wir jetzt den Befehl dup aus und sehen wir uns dann den Zustand des Stacks an:

    Quellcode

    1. dup


    ok

    Quellcode

    1. .S
    1 2 2 ok

    Wie wir sehen, hat dup den obersten Wert auf dem Stack, die Zahl 2 dupliziert und das Duplikat (eine weitere Zahl 2) auf dem Stack und damit an oberster Stelle abgelegt. Leeren wir nun den Stack um Platz für ein weiteres Beispiel zu schaffen:

    Quellcode

    1. drop drop drop
    ok

    Quellcode

    1. .S
    ok

    wie wir anhand des .S Befehls sehen können, liegen nun keine Werte mehr auf dem Stack, da der .S Befehl keine Werte ausgegeben hat.


    Ein Verwandter von dup ist das Wort over, welches den zweiten Wert auf dem Stack dupliziert und das Duplikat an oberster Stelle ablegt. Das Stackdiragramm zu over sieht dann so aus:

    ( n1 n2 -- n1 n2 n1 )

    Probieren wir over doch gleich mal aus:

    Quellcode

    1. 1 2

    ok

    Quellcode

    1. over
    ok

    Quellcode

    1. .S
    1 2 1
    ok

    Wie wir sehen können, hat der Befehl over die Zahl 1, welche an zweiter Stelle auf dem Stack lag dupliziert und das Duplikat an oberster Stelle auf dem Stack abgelegt:

    Aus

    ( 1 2 )

    wurde

    (1 2 1 )

    Bevor es weitergeht, leeren wir wieder den Stack:

    Quellcode

    1. drop drop drop

    ok


    Ein weiter Stack Operator ist das Wort swap. Mit swap werden die obersten zwei Werte auf dem Stack vertauscht, d.h. der oberste Wert wandert an die zweite Stelle auf dem Stack und der ehemals zweite Wert wird an erster (oberster) Stelle auf dem Stack platziert. Das Stackdiagramm für den Befehl swap sieht so aus:

    ( n1 n2 -- n2 n1 )

    und am praktischen Beispiel funktioniert das so:

    Quellcode

    1. 1 2 3

    ok

    Quellcode

    1. .S
    1 2 3

    Quellcode

    1. swap
    ok

    Quellcode

    1. .S
    1 3 2
    ok

    Wie wir nun sehen können, wurden durch den swap Befehl die beiden obersten Werte auf dem Stack (die Zahlen 2 und 3) miteinander vertauscht. Die dritte und unterste Zahl auf dem Stack (die Zahl 1) hat ihren Platz an dritter Position behalten, da swap lediglich auf die obersten beiden Werte auf dem Stack angwendet wird.


    Lernen wir nun den Stack Operator rot kennen. Das Wort rot nimmt immer die Zahl, welche an dritter Stelle auf dem Stack liegt und "rotiert" sie auf die erste Stelle. Im Stackdiagram sieht das dann so aus:

    ( n1 n2 n3 -- n2 n3 n1 )

    und wenn wir mit den o.g. Werten 1 3 2 auf unserem Stack das Wort rot aufrufen und uns dann den Zustand des Stack anzeigen lassen, so können wir folgendes sehen:

    Quellcode

    1. rot

    ok

    Quellcode

    1. .S
    3 2 1
    ok

    durch Aufruf des Befehls rot haben wir die Zahl an dritter (in diesem Fall der untersten) Stelle auf dem Stack, die Zahl 1, an oberster Stelle auf dem Stapel abgelegt. Dadurch ergibt sich nun die Reihenfolge

    ( 3 2 1 )

    auf dem Stack.

    Leeren wir nun wieder den Stack:

    Quellcode

    1. drop drop drop


    ok


    Damit haben wir nun die wichtigsten Stack Operatoren kennen gelernt. Es gibt noch weitere, die wir im Verlauf dieses Tutorials noch kennen lernen werden.


    Mit den beiden Worten swap und rot können wir nun sogar ein keines Programm (Sprich: Wort) definieren, mit dem wir drei Werte auf dem Stapel in umgekehrter Reihenfolge dort sortieren. Analog zu dem o.g Beispiel also aus den Werten

    ( 1 2 3 )

    auf dem Stack die Reihenfolge

    ( 3 2 1 )

    wird. Wir erinnern uns, eine neue Wortdefinition wird immer mit einem : eingeleitet und mit einem ; abgeschlossen. Nennen wir unser neues Wort reverse und nutzen wir das bisher gelernte. Wie ihr seht habe ich mir erlaubt, das Stackdiagramm in Form eines Forth Kommentars vor dem eigentlichen Code einzufügen (Wir erinnern uns: alles was in runden Klammern steht ist in Forth ein Kommentar):

    Quellcode

    1. : reverse ( n1 n2 n3 -- n3 n2 n1 ) swap rot ;

    Voilá, fertig ist der unser erster, eigener Stack Operator. Legen wir nun die Zahlen 1 2 und 3 auf dem Stack ab und rufen wir unser neu definiertes Wort reverse auf. Danach lassen wir uns den Zustand des Stack anzeigen und dann die einzelnen Werte auf dem Stack ausgeben:

    Quellcode

    1. 1 2 3
    ok

    Quellcode

    1. reverse
    ok

    Quellcode

    1. .S
    3 2 1 ok

    Quellcode

    1. .
    1 ok

    Quellcode

    1. .
    2 ok

    Quellcode

    1. .
    3 ok

    Konkret passiert dabei folgendes:

    Zuerst werden die Zahlen 1 2 3 auf dem Stack abgelegt

    ( 1 2 3 )

    dann rufen wir das Wort reverse auf, dieses führt nun den Stack Operator swap aus, woraufhin sich der Stack so darstellt:

    ( 1 3 2 )

    danach wird er Stack Operator rot aufgerufen, womit unser kleines Programm dann auch beendet wird und zum gewünschten Ergebnis führt:

    ( 3 2 1 )


    Wir können das Wort reverse nun immer dann nutzen, wenn wir die drei ersten Werte auf dem Stack in umgekehrter Reihenfolge auf dem Stack liegen haben wollen.

    Abschliessend möchte ich noch erwähnen, das es bei mir einige Zeit gedauert hat, bis ich mit den Stack Operatoren vertraut gewesen bin. Noch viel länger hat es dann allerings gedauert, bis ich mir über den Sinn und Zweck des Stack in Forth im klaren war. Eine kurze Zeit war ich tatsächlich der Ansicht, das man auf dem Stack alle Werte vorzuhalten hat, die man in einem Programm verwenden möchte. Ein völliger Irrglaube, denn selbstverständlich kennt Forth auch Variablen und der Stack dient häufig zur Übergabe von Parametern, welche von Forth Worten verwendet werden. Die Stack Operatoren helfen dann dabei, diese Parameter in die für ein bestimmtes Wort gewünschte Reihenfolge zu bringen. Und so trocken das Thema auch erscheinen mag, die bisherigen Übungen und Erklärungen sind vorallem dazu nötig, die wichtigsten Konzepte und Grundlagen der Sprache zu vermitteln. Im nächsten Teil lernen wir dann Variablen kennen und können damit beginnen, diese Grundlagen für erste, kleine Spielereien zu nutzen. Versprochen!

    ... und damit sind wir am Ende von Teil 2 meiner kleinen Reise die Welt von Forth angelangt. Wie es weitergeht, seht Ihr in der nächsten Folge von "obsoletes Nerd-Wissen, das kaum einen interessiert" - stay tuned.
    -
    Gesucht:
    Hewlett Packard HP-9826 (1981)
    Jupiter ACE (1982)

    Dieser Beitrag wurde bereits 21 mal editiert, zuletzt von lodger () aus folgendem Grund: Interpunktion, Rechtschreibung, Ergänzungen etc.pp.

  • DurexForth - Ein Selbstversuch, Teil 3

    DurexForth - Ein Selbstversuch, Teil 3

    Im zweiten Teil meines Selbstversuchs habe ich Euch mit dem Stack vertraut gemacht, jenem Baustein der Programmiersprache Forth, welcher u.a. für Berechnungen und zur Übergabe von Funktionsparametern dient. In diesem Teil lernen wir nun die Art und Weise kennen, mit der in Forth Variablen und Konstanten definiert werden und wie wir auf die Speicheradressen unseres C64 zugreifen können.

    In Forth wird eine Variable mit dem Befehlswort variable definiert. Dazu habe ich für Euch im folgenden ein kleines Beispiel zur Eingabe am Prompt. Als erstes stellen wir wieder unseren Stack auf den Dezimalmodus um:

    Quellcode

    1. decimal
    ok

    Dann teilen wir Forth mit, das wir eine Variable mit dem Bezeichner myvar deklarieren wollen. Dazu verwenden wir das Wort variable:

    Quellcode

    1. variable myvar
    ok

    Als nächstes weisen wir Forth an, im freien Speicher unseres C64 eine Speicheradresse zu reservieren, an welcher wir einen Wert für myvar ablegen wollen. Der Name myvar referenziert ("verweist") an dieser Stelle auf eben diese nun reservierte Speicheradresse. Das Forth Wort variable wird also dazu verwendet, eine Variable zu deklarieren (d.h. eine Speicheradresse im RAM des C64 für uns zu reservieren).

    DurexForth verwendet für Ganzzahlen (Integer) 16 Bit Werte. 16 Bit? Aber der C64 ist doch ein 8 Bit Rechner!! In der Tat, aber: DurexForth ist (wie auch BASIC) in der Lage 16 Bit Werte, also Zahlen zwischen −32768 und 32767 zu verarbeiten. Dafür werden im Speicher des Computers in der Folge zwei 8 Bit Adressen belegt. Das bedeutet, das eine Ganzzahl Variable in DurexForth immer zwei Byte im RAM des C64 beansprucht.

    Quellcode

    1. 15 myvar !
    ok

    Als nächstes legen wir die Zahl 15 auf den Stack, dann geben wir den Namen unserer Variable an (also: myvar) und letztlich rufen wir das Forth Wort ! (sprich: store, engl. speichern) auf, um die Zahl auf dem Stack in dieser Variable zu speichern. Das Wort ! nimmt einen Wert vom Stack und schreibt ihn an eine ebenfalls auf dem Stack befindliche Speicheradresse. Das Stackdiagramm für das Wort ! sieht also so aus:

    ( n1 addr -- )

    Tatsächlich legen wir zwei Zahlen auf dem Stack ab, den Wert 15 sowie die Adresse, an die wir diesen Wert speichern wollen. Ich höre den ein oder anderen schon sagen: "Wie?! Die Adresse? Aber wir legen doch den Variablennamen auf den Stack!". Das ist nicht korrekt, denn wie wir in Teil 2 erfahren haben, können auf dem Stack nur Ganzzahlen und Fliesskommawerte abgelegt werden.

    Dazu ein Beispiel:

    Quellcode

    1. myvar .
    17420 ok

    Wie wir am obigen Beispiel sehen können, verbirgt sich hinter "myvar" die Speicheradresse 17420 (bei Euch kann es ein anderer Adresswert sein). D.h. wenn wir am Forth Prompt myvar angeben, wird auf dem Stack die Speicheradresse hinterlegt, auf welche die Variable myvar verweist.


    Wir haben nun also unsere Variable myvar deklariert und den Wert 15 dort hinterlegt. Wie können wir nun aber einen Wert aus einer Variablen wieder auslesen und z.B. weiterverarbeiten? Dazu gibt es in Forth das Wort @ (fetch, engl. abholen), welches den Wert einer Variablen aus dem Speicher holt und ihn auf dem Stack ablegt. Ein simples Beispiel:

    Quellcode

    1. myvar @
    ok

    Quellcode

    1. .
    15 ok

    Zunächst haben wir die Adresse der Variablen myvar auf dem Stack abgelegt, dann haben wir den @ (fetch) Oerator aufgerufen, welcher die auf dem Stack abgelegte Adresse nimmt und deren dort gespeicherten Wert auf dem Stack ablegt. Das Stackdiagram für den @ Operator sieht demzufolge so aus:

    ( addr -- n1 )


    Wir haben nun also gesehen, wie wir eine Variable mit Hilfe des Wortes variable deklarieren können. Ausserdem haben wir die Worte ! (store) und @ (fetch) kennen gelernt, mit welchen wir Werte in einer Variablen speichern bzw. diese wieder abrufen können.

    In Forth kann man ganz einfach den Wert einer Variablen erhöhen. Dazu dient folgendes Beispiel:

    Quellcode

    1. 1 myvar +!
    ok

    Quellcode

    1. myvar @
    ok

    Quellcode

    1. .
    16 ok

    Wie Ihr sehen könnt, haben wir zunächst den Wert 1 auf den Stack gelegt, danach die Adresse der Variablen myvar und zum Schluss haben wir den +! Befehl ausgeführt. Der nimmt den Wert 1 vom Stack und addiert diesen auf den Wert (15) in der Speicheradresse der Variablen myvar.

    Natürlich hätten wir anstelle von 1 myvar +! auch folgendes machen können:

    Quellcode

    1. myvar @
    ok

    Quellcode

    1. 1 +
    ok

    Quellcode

    1. myvar !
    ok

    Quellcode

    1. myvar @
    ok

    Quellcode

    1. .
    16 ok

    Das Ergebnis wäre am Ende das Gleiche, aber die erste Variante ist einfach kompakter und erfordert deutlich weniger Tipparbeit.


    Zwei sehr enge Verwandte von ! und @ sind die Worte c! und c@ - diese beiden Worte machen im Prinzip das gleiche wie ! und @ - allerdings verwenden beide 8 Bit Werte im Bereich von -128 bis 127. Das bedeutet, während ! und @ auf zwei Byte im Speicher zugreifen um dort Werte abzulegen oder auszulesen, ist es bei c! und c@ nur ein einzelnes Byte. Man kann c! und c@ in gewisser Weise mit den BASIC Befehlen POKE und PEEK vergleichen.


    Und weil das Ganze auch ein wenig Spass machen soll, hier nun was zum herumspielen am Forth Prompt:

    Quellcode

    1. hex
    ok

    Quellcode

    1. 5 $d021 c!

    Voilá, schon haben wir die Hintergrundfarbe von schwarz nach grün gewechselt. Ganz so, wie wir es in BASIC miittels POKE 53281,5 auch tun würden, haben wir einfach den Wert 5 an die Speicheradresse $d021 (53281) geschrieben.

    Geben wir nun folgendes ein:

    Quellcode

    1. 5 $d020 !

    Nanu? Jetzt ist der Rahmen grün, aber der Hintergrund wieder schwarz. Dabei haben wir doch nur an die Speicheradresse für den Rahmen ($d020 bzw. 53280) geschrieben?!

    Das ist korrekt, aber beim ersten Mal haben wir den c! Befehl verwendet und somit auch nur ein Byte (8 Bit Wert) an die angegebene Adresse geschrieben. Im zweiten Fall haben wir aber anstelle des c! Befehls den ! Befehl verwendet. Und der schreibt zwei Byte ab der angegebenen Adresse, da wir mit ! ja 16 Bit Werte schreiben und diese immer zwei Byte im Speicher belegen. Konkret heißt das, das wir an der Adresse $d020 das erste Byte unserer 16 Bit Zahl ablegen (dort steht die 5 drin), und an der Adresse $d021 das zweite Byte unserer 16 Bit Zahl (dort steht 0 drin). Und deswegen ändert der Rahmen (Adresse $d020) die Farbe nach Grün (Wert: 5) und der Hintergrund (Adresse $d021) die Farbe nach schwarz (Wert: 0).

    HINWEIS: Es ist also wichtig, das Ihr für das sog. Peeken und Poken an Speicheradressen des C64 die Forth Befehle c! und c@ verwendet. Zum Setzen und Abrufen von Variablen solltet Ihr entsprechend die Befehle ! und @ verwenden.


    Neben Variablen, deren Inhalt sich ändern kann, gibt es in Forth auch Konstanten. Deren Wert wird einmalig festgelegt und ändert sich in der Folge nicht mehr. Definieren wir uns also mal eine Konstante und dafür kennt Forth das Wort constant:

    Quellcode

    1. 500 constant limit
    ok

    Quellcode

    1. limit
    ok

    Quellcode

    1. .
    500 ok

    Na, habt Ihr was bemerkt? Genau: anders als bei Variablen müssen wir bei Konstanten keinen @ (fetch) Befehl ausführen. Woran liegt das? Nun, anders als Variablen können Konstanten ihren einmal zugewiesenen Wert nicht ändern. Daher muss beim Zugriff auf die Konstante nicht zwischen ! (store) und @ (fetch) unterschieden werden. Eine Konstante kann nur gelesen werden und daher reicht es, wenn man lediglich den Namen der Konstante schreibt, um dann auf dem Stack deren entsprechenden Wert liegen zu haben.

    Selbstverständlich kennt DurexForth noch andere Datentypen als reine Ganzzahlen. Auf diese gehe ich dann ein, wenn wir sie im Rahmen dieses Tutorials das erste mal verwenden. Beim nächsten mal schauen wir uns dann an, wie man in Forth zählt und was wir mit dem bis dahin gelernten so schon alles machen kann.


    ... und damit sind wir am Ende von Teil 3 meiner kleinen Reise die Welt von Forth angelangt. Wie es weitergeht, seht Ihr in der nächsten Folge von "obsoletes Nerd-Wissen, das kaum einen interessiert" - stay tuned.
    -
    Gesucht:
    Hewlett Packard HP-9826 (1981)
    Jupiter ACE (1982)

    Dieser Beitrag wurde bereits 10 mal editiert, zuletzt von lodger () aus folgendem Grund: Typos, Interpunktion, Ergänzungen etc.pp