Hallo Besucher, der Thread wurde 22k mal aufgerufen und enthält 89 Antworten

letzter Beitrag von polluks am

DurexFORTH - Diskussion zum Tutorial

  • Was mich interessieren würde, wie ist denn die Leistung bzw Geschwindigkeit von Forth in Bezug zu Basic? Welche (kommerzielle ) Anwendungen und Spiele wurden denn in Forth programmiert?

    Also, ich habe jetzt keine Performance Tests oder Benchmarks durchgeführt, aber man kann schon sagen das Forth im Vergleich zu Basic erheblich performanter ist. Nicht zuletzt, weil Forth eine Compiler Sprache ist die Wortdefinitionen direkt in Maschinensprache compiliert. Laut der Dokumentation ist DurexForth aber nicht ganz so performant wie optimierter cc65 C code oder reiner Assembler Code.


    Zu der Frage nach Anwendungen und Spielen fallen mir jetzt spontan das Spiel "Starflight" von EA aus dem Jahre 1986 und die Textverarbeitung EasyWriter ein. Zudem wurde ein Teil der Steuersoftware der Raumsonde Philae in Forth realisiert. Die Firma FederalExpress setzt Handheld Scanner ein, deren Firmware in Forth realisiert wurde. Zudem ist Forth die Basis der sog. "Open Firmwarwe" welche u.a. von Apple (ältere Mac Modelle) und IBM (pSeries) verwendet wird.

  • Ah ok, das ist interessant. Werde mir auch das Spiel mal anschauen.
    Basic kann man aber doch auch in Maschinensprache compilieren? Welche Vorteile bietet denn da Forth? Ist wahrscheinlich übersichtlicher und man muss nicht ständig Sachen poken. Ich kann mich jedenfallls an ein Rollenspiel erinnern, so etwa 1990 bis 92, welches in Basic programmmiert und compiliert wurde. Das war war dafür schon echt beachtlich.

  • Nun, das BASIC des C64 kann erstmal gar nichts kompilieren, da es sich um ein Interpreter BASIC handelt und nicht (wie z.B. auf dem PC mit BlitzMax oder PureBasic) um einen Compiler. Natürlich kann ich mittels der Peek / Poke Befehle auch in BASIC maschinensprache Opcodes in den Speicher schreiben, das wird ja auch oft genug gemacht: jeder der schon einmal ein "Listing des Monats" eingetippt hat, kennt die endlos langen DATA Zeilen welche letztlich Maschinensprache Opcodes enthalten.


    Forth, ist vom "feeling" her sehr nah an der Hardware der Maschine, tatsächlich bietet DurexForth sogar die Möglichkeit inline Assembler code in den Forth Code zu integrieren. Den direkten Zugriff auf den Speicher des C64 wie er in BASIC mittels Pokes realisiert wird, gibt es natürlich auch in Forth. Ein Bespiel zur Eingabe in DurexForth wäre z.B.:


    4 $d020 c!


    Damit schreibst Du den Wert 4 an die Speicheradresse 53280

  • Nun, das BASIC des C64 kann erstmal gar nichts kompilieren, da es sich um ein Interpreter BASIC handelt und nicht (wie z.B. auf dem PC mit BlitzMax oder PureBasic) um einen Compiler.

    Sicher, das eingebaute Basic wird interpretiert, aber deswegen kann man das fertige Programm ja trotzdem durch einen Compiler umwandeln, z. B. mit Basic Boss?

  • Sicher, das eingebaute Basic wird interpretiert, aber deswegen kann man das fertige Programm ja trotzdem durch einen Compiler umwandeln, z. B. mit Basic Boss?

    Das stimmt natürlich. Ich denke, es hängt in diesem Fall davon ab, wie gut der BASIC Compiler den Code optimiert.

  • Also, ich habe jetzt keine Performance Tests oder Benchmarks durchgeführt, aber man kann schon sagen das Forth im Vergleich zu Basic erheblich performanter ist. Nicht zuletzt, weil Forth eine Compiler Sprache ist die Wortdefinitionen direkt in Maschinensprache compiliert. Laut der Dokumentation ist DurexForth aber nicht ganz so performant wie optimierter cc65 C code oder reiner Assembler Code.
    Zu der Frage nach Anwendungen und Spielen fallen mir jetzt spontan das Spiel "Starflight" von EA aus dem Jahre 1986 und die Textverarbeitung EasyWriter ein. Zudem wurde ein Teil der Steuersoftware der Raumsonde Philae in Forth realisiert. Die Firma FederalExpress setzt Handheld Scanner ein, deren Firmware in Forth realisiert wurde. Zudem ist Forth die Basis der sog. "Open Firmwarwe" welche u.a. von Apple (ältere Mac Modelle) und IBM (pSeries) verwendet wird.

    Also bei den gängigen für C64 Forth-Implementierungen (gilt faktisch für alle 6502-basierten Forth-Varianten) und den Benchmarks, die ich damals machte, kann man zwischen BASIC und Forth mit einem Faktor 10 und von Forth zu Assembler ebenso mit einem Faktor 10 rechnen - nur grob, als Daumenregel. Es gibt ganz gefinkelte Implementierungen, die noch gegenüber einer Standard-FIG-Forth-Implementierung paar Prozente gut machen. Aber es hängt auch generell von der Implementierung insgesamt ab, also welche Forth-Words sind wie implementiert (als Primitive, also in Maschinensprache oder als High-Level-Words, also mit anderen Forth-Words). Das ist dann gewissermaßen der Kompromiss zwischen Portierbarkeit und Geschwindigkeit.
    Der Vergleich bei gängigen Sachen Aufgaben zwischen BASIC und Forth hinkt oft, denn BASIC rechnet und hantiert ständig mit Fließkommawerten. Selbst für jede zählende Schleifenvariable. Forth rechnet grundsätzlich nur mit 16-Bit-Integers oder mit Double-Precision, also 32-Bit-Integers (mit oder ohne Vorzeichen). Es gibt natürlich auch Float-Librarys, die die Float-Routinen des BASIC-ROMs anzapfen oder einen eigene Implementierung mitbringen. Das ist aber nicht das klassische Forth (obgleich der ANSI-Forth-Standard Floating-Point-Words definiert). Forth-Programmierer machen soweit es geht einen kräftigen Bogen um Float-Geschichten, zumal ja viele Dinge, speziell auch für jene Dinge, für die Forth steht und eingesetzt wird, auch mit Fixpunktarithmetik auskommen.


    Korrektur zum obigen ZItat: Forth compiliert, aber nicht direkt in Maschinensprache! Vielmehr wird einfach eine Liste von Forth-Word-Referenzen "compiliert", nichts als eine Aneinanderreihung von Adressen, die ein sogenannter "innerer Interpreter" abarbeitet bei Aufruf eines Forth-Words. Dieser ist der aber klein, kompakt und schnell (jetzt nicht vergleichbar, was ein BASIC-Interpreter macht).

  • Es gab mal auf VCFe einen Benchmark-Wettbewerb in Forth. Hier gibts die Benchmarks und Vergleiche mit vielen verschiedenen Computern und Forth-Systemen:


    https://atariwiki.strotmanin die Richtungn.de/wiki/Wiki.jsp?page=Forth%20Benchmarks


    (Ich kriege da eine Sicherheitswarnung, die ich aber mal ignoriert habe...)

    Die URI heißt eigentlich https://atariwiki.org/wiki/Wiki.jsp?page=Forth%20Benchmarks
    weil das Zertifikat auf atariwiki.org ausgestellt ist, passt obiger Hostname nicht mit dem Zertifikat zusammen. ;)

  • Korrektur zum obigen ZItat: Forth compiliert, aber nicht direkt in Maschinensprache! Vielmehr wird einfach eine Liste von Forth-Word-Referenzen "compiliert", nichts als eine Aneinanderreihung von Adressen, die ein sogenannter "innerer Interpreter" abarbeitet bei Aufruf eines Forth-Words. Dieser ist der aber klein, kompakt und schnell (jetzt nicht vergleichbar, was ein BASIC-Interpreter macht).

    Danke für die Info. Ich habe da wahrscheinlich die Aussage "DurexForth compiles to machine code" (https://github.com/jkotlinski/durexforth) mißverstanden?!

  • Forth, ist vom "feeling" her sehr nah an der Hardware der Maschine, tatsächlich bietet DurexForth sogar die Möglichkeit inline Assembler code in den Forth Code zu integrieren. Den direkten Zugriff auf den Speicher des C64 wie er in BASIC mittels Pokes realisiert wird, gibt es natürlich auch in Forth. Ein Bespiel zur Eingabe in DurexForth wäre z.B.:


    4 $d020 c!


    Damit schreibst Du den Wert 4 an die Speicheradresse 53280

    Das "inline" gehört eigentlich zu Forth dazu (kein besonderer Verdienst von DurexForth), dass man auch Forth-Words in Maschinensprache deklarieren kann. So gesehen ist im Forth immer alles Inline, weil ja die Sprache sich selbst erweitert - also alle Neudefinitionen haben den gleichen Status wie die bereits bestehenden (es gibt keine reservierten "Keywords"). Dazu gibt es auch bei den meisten Umsetzungen einen Forth-Assembler (oftmals auf Ragsdale-Quellen basierend, siehe auch 6502 Forth-Assembler). Dann kann man so Sachen machen wie



    Code
    1. CODE 2* 0 ,X ASL, 1 ,X ROL, END-CODE

    Was ein schnelles "2 *" mittels Shift/Rotate-Operationen macht.


    Als High-Level-Definition entspricht ungefähr (obige Variante berücksichtigt nicht das Vorzeichen bzw. gilt nur vorzeichenlose Ganzzahlen):


    Code
    1. : 2* DUP + ;

    Der am Stack liegende Wert wird dupliziert und dann addiert (was auch noch immer flotter ist, als eine wirkliche 16-Bit-Multiplikation, wenn man nur "2 *" schriebe).

  • Danke für die Info. Ich habe da wahrscheinlich die Aussage "DurexForth compiles to machine code" (https://github.com/jkotlinski/durexforth) mißverstanden?!

    Naja, das trifft in diesem Fall insofern zu, weil das hier kein indirekt verkettetes Implementierungsmodell verwendet wird (wie bei klassischen, FIG-Forth-orientierten Varianten), wo Words nur Adressreferenzen bestehen, sondern hier Words direkt mit "JSR adresse" aufgerufen werden. Das kann die CPU "native" abarbeiten und man spart sich den "inneren Interpreter", allerdings auf Kosten von rund 50 % mehr Speicherplatzverbrauch für High-Level-Words. Wenn man den Platz hat und von der Geschwindigkeit profitieren möchte, ist das ja sicher ein sinnvoller Ansatz. Auch wenn dann dadurch eigentlich reiner Assembler-Code entsteht, wenn man die JSR-Ketten als solches betrachtet, so beschränkt sich die Maschinensprachegenerierung doch auf ein simples Aneinanderreihen von JSRs. Also weit davon entfernt, was etwa C-Compiler machen (selbst wenn diese "nur" P-Code erzeugen würden).

  • Hallo zusammen,
    in den 1980er Jahren habe ich mich, als blutjunger computer user, eine Zeit lang für die Programmiersprache "FORTH" interessiert, habe diese aber nie wirklich erlernt. Unlängst bin ich, einer nostalgischen Laune folgend, wieder auf FORTH gestossen und habe mich in diesem Zusammenhang mal umgeschaut, welche FORTH compiler es aktuell noch für den C64 gibt. Dabei bin ich über das Open-Source Projekt "DurexFORTH" (https://github.com/jkotlinski/durexforth) gestolpert, welches sogar eine Grafik Library und eine Library für das erzeugen von Tönen mitbringt. Nun meine Frage: gibt es hier ggf ein paar alte Recken, die sich ebenfalls für FORTH erwärmen könne und u.U. schon Erfahrung mit DurexFORTH gemacht haben? Irgendwie habe ich den Ehrgeiz, es doch nochmal mit FORTH zu probieren und hoffe daher, das es diesmal klappt.

    Bin erst neulich auf diesen Thread gestoßen (daher jetzt meine etwas geballten Posts hier). Ich wollte auch die Erfahrungen aus meiner Sicht mitteilen. Ich bin ja da noch in den 80ern verhaftet und damals hab ich mich auf "C64 Forth/79 von Greg Harris, 1983 Performance Micro Products" eingearbeitet und dieses dann mit Grafik-Bibliothek, Assembler, Decompiler, und einiges mehr erweitert. Die Variante geht nach dem Forth-79-Standard und es damit nicht so verstaubt wie die FIG-Forth-Varianten der damaligen Zeit. Hat zwar ein bisschen Drumherum, aber eigentlich (zumindest in der mir vorliegenden Version) sonst nichts Besonderes zusätzlich dabei, das die Fähigkeiten des C64 ausreizt. Das, was ich dazu gemacht habe ist zwar (noch) nicht veröffentlicht (tw. schon), wer Interesse hat, kann den Vorgang beschleunigen. ^^


    Wie auch immer, DurexForth ist für heute Verhältnisse sich eine ausgezeichnete Wahl, speziell im C64-Kontext.


    Es wird zwar immer gerne das Einsteigerbuch von Leo Brodie zu Forth genannt. Aber ich kann auch jenes von Ronald Zech, Die Programmiersprache FORTH : eine kompakte Darstellung der Programmiersprache mit vielen Beispielen, 2. Auflage, Franzis Verlag empfehlen. Vermutlich nicht so ohne weitere käuflich zu bekommen, aber (Uni-)Bibliotheken und öffentliche Büchereien werden schon ein Exemplar haben. Ob's einen Scan davon gibt, weiß ich jetzt nicht. Sollte ich vielleicht mal machen. :D
    Richtet sich sowohl an Einsteiger und geht bis ganz ins Innerste. Für mich das deutschsprachige Referenzwerk, wenn es um Forth geht.

  • Nun, in Forth ist nicht grundsätzlich alles RPN notiert, das require Wort ist hier ein gutes Beispiel, aber es gibt durchaus noch weitere Wörter. Zudem bedeutet RPN ja nicht, das man die Kommandos einfach rückwärts schreibt ;) Relevant ist die RPN vorallem bei Operationen mit dem Stack, dazu im nächsten Teil mehr. Bei dem ." Wort ist zu bedenken, das der Text in den Anführungszeichen nicht auf dem Stack abgelegt wird sondern im Speicher des Rechners hinterlegt ist.

    Um da vielleicht da Ganze noch etwas anders zu formulieren bzw. zu erläutern (für Interessierte Newcomer, alte Forth-Hasen werden das natürlich kennen):


    Es gibt in Forth einen "inner interpreter", der wie eine Pseudo-CPU den Forth-Code abarbeitet und den "outer interpreter", der für das textuelle Parsing zuständig ist. Wenn man also ." irgend ein text" schreibt oder als Source-Code "verarbeitet" wird (es ist im Prinzip egal, ob das auf der Tastatur getippt wird oder etwa von Diskette herein kommt), dann wird der Datenstrom von links nach rechts abgearbeitet. Das hat vorerst mit RPN nicht viel zu tun, das ist nur einfaches Text-Parsing. Es kommt dann nur darauf an, ob das dann innerhalb einer Definition (eines neuen Words) vorkommt, also "compiliert" wird (="compile time") oder direkt ausgeführt werden soll. Zum Vergleich: Man gibt etwa eine Zahlenkonstante 1234 auch nicht als 4321 an. RPN heißt nicht, dass alles in der Reihenfolge umgedreht wird! ;)


    Das klassische Forth (ohne irgendwelche Erweiterungen) kennt keine Strings im Sinne eines Datentyps. Deswegen gibt es in Wirklichkeit keine ausgeprägten Operation mit und für Strings. Für Forth sind Strings nur eine gewisse Byte-Folge, die an irgendeiner Adresse steht.
    Um beim obigen Beispiel zu bleiben: Das Word ." nimmt alles bis zum nächsten "-Zeichen, gibt es in einen Puffer, der dann im interaktiven Fall gleich ausgeben wird oder innerhalb einer Definition im Programm abgelegt wird und erst beim Aufruf den so eingebetteten String anzeigt.
    Der "outer Interpreter" (Parser) könnte mit einem "Hallo world!" somit syntaxtisch nichts anfangen, weil weder "Hallo noch world!" als ein Forth-Word existieren. Das überladen der Operatoren (hier ".") geht auch nicht so ohne weiteres (wenn man mal Vocabolarys beseite lässt).


    Mehr als String-Konstanten ausgeben und formatierte Zahlen bilden ist im Grundumfang nicht vorgesehen. Die Speicherkonvention für im Speicher abgelegte Strings folgt lediglich der Konvention <Längen-Byte> gefolgt den String-Bytes. Der String wird dann als Adresse, zeigend auf das Längen-Byte, repräsentiert, z.B. auch als Parameter übergebbar.
    Beispiel: Ausgabe eines Strings im oben erwähnten Format:

    Code
    1. : PRINT ( ADDR -- ) COUNT TYPE ;


    COUNT wandelt die Adresse in Adresse + Längen-Byte ("ermittelt den Count des Strings"), was TYPE dann tatsächlich ausgibt.
    Z.B. einen String "AB" im freien Speicher (HERE legt die Adresse dazu auf den Stack) anlegen:

    Code
    1. HERE 2 C, 65 C, 66 C,

    Die Adresse liegt noch immer am Stack, jetzt kann man PRINT verwenden (wobei DUP die Adresse am Stack lässt, sonst ist die Adresse weg und wir wissen nicht mehr wo der String ist):

    Code
    1. DUP PRINT
  • Also gibt es z.B. kein Wort, welches die Länge eines Strings ermittelt?

    Nein, weil der String ja über keine Endmarkierung verfügt (wie etwas bei C üblich mit einem 0-Byte am Ende). Die Länge steht ja bereits als 1. Byte der "String-Struktur", genannt auch "counted string". Damit kann man so auch nur mit Strings bis zu einer maximalen Länge von 255 umgehen (also in Verbindung mit Words wie .", COUNT). Aber das ist recht "traditionell", neuere Standards, wie etwa ein auf ANSI94 basierendes Forth mit weitere Befehlen für die String-Behandlung gehen von den "counted strings" am Stack weg und verwendet nahezu generell Adresse+Länge, also 2 Werte am Stack (statt nur der Adresse auf einen im Speicher liegenden "Counted-String"). Aber: Im Speicher liegender Text, etwa Ausgabetext, typischerweise String-Konstanten im Programmtext, verwendet aber nach wie vor das Counted-String-Schema.
    Wenn man es genau betrachtet, dann konvertiert ja Word COUNT einen Counted-String in die Adresse+Länge-Form am Stack, welche z.B. mit TYPE ausgegeben werden kann. :D

  • Zunächst einmal möchte ich mich bei @JeeK für dessen ergänzende Kommentare bedanken. Da ich selber ja ein Forth "Noob" bin, freue ich mich über jede zusätzliche Information zu diesem Thema. Danke Dir, @JeeK !! :)


    Ich hatte Euch ja versprochen, in den "nächsten Tagen" den dritten Teil meines kleinen mini-Tutorials zu präsentieren. Nun, da war ich insofern etwas zu optimistisch, als das ich diese Woche auf Schulung (F5 Systems Viprion Load Balancer) fernab meiner rheinischen Heimat bin. Und obgleich ich einen Laptop mit VICE Emulator und DurexForth im Reisegepäck habe, so fehlt mir nach 9 Stunden intensiven Zuhörens dann doch die nötige Konzentration um ein nachvollziehbares und verständliches Tutorial zu schreiben. Habt also bitte etwas Geduld und Nachsicht mit mir, der nächste Teil kommt auf jeden Fall!!


    Zudem möchte ich Euch wissen lassen, das ich sehr erfreut über den Zuspruch zu diesem Tutorial bin. Nichts motiviert einen mehr, als das Wissen um andere "gleichgesinnte" da draussen.

  • Code
    1. hex
    2. 5 $d021 c!

    Braucht DurexForth wirklich das $-Zeichen? Ich kenne das eigentlich nur ohne (ich probier das grad nicht live mit). Sonst hätte man mit "hex" ja nicht explizit die Basis auf Hexadezimal umstellen müssen, wenn der Outer-Interpreter das $-Zeichen als spezielles Zeichen erkennen sollte (und dabei implizit einen Hex-Wert "einliest"). Sonst wär's halt nur ein Doppelgemoppel. ;)

  • Es ist in der Tat Doppelgemoppel meinerseits, denn in der Tat reicht es aus, wenn man einfach d020 (bei Hexadezimaler Basis) eingibt. Laut Handbuch kennt DurexForth diverse Prefixe, $ für Hexadezimalzahlen, # für Dezimalzahlen und % for Binärzahlen. Ich habe das $ eigentlich nur verwendet, um hervorzuheben das ich eine Hexadezimalzahl meine.