Hallo Besucher, der Thread wurde 32k mal aufgerufen und enthält 170 Antworten

letzter Beitrag von EgonOlsen71 am

BASIC V2 Interpreter/Compiler in/für Java

  • Zitat von JeeK

    Das FOR-NEXT im BASIC gaukelt auch nur ein statische Konstrukt vor, ist aber in Wirklilchkeit mit for()-, oder while()-Konstrukten, wie man sie von sonst üblichen statischen Kontrukten anderer Compiler-Sprachen kennt, nicht vergleichbar. Es ist eigentlich mehr, als es gemeinhin den Anschein hat. Ich würde es eigentlich eher als primitiven Exception-Mechanismus mit Auto-inkrement/Dekrement-Funktion sehen, mit dem man halt zufällig auch auch Schleifen machen kann (was durch die Bezeichnung der Tokens auch so suggeriert wird), aber der für alles gut ist, wenn man an einen definierten Ausgangspunkt zurück will, und das in Verschachtelung. Die Abbildung in eine statische Struktur könnte dann nur durch eine aufwändigere semantische Analyse des Codes, wenn sozusagen, die Verhältnisse am Stack als nicht mehr von Laufzeit abhängig erkennbar wären. Sowas könnte man mit entsprechenden Compileroptionen für einen Compiler leichter machen, also wie genau der Compiler die Analyse betreiben soll. Die Verwendung von nichtanonymen NEXT-Aufrufen könnte einem Compiler da auch helfen.

    Mein Klassiker da:

    Code
    1. 17 [...]:FORS=0TO159:[...]
    2. 18 FORI=1TON:X2=X*X:Y2=Y*Y:IFX2+Y2<4THENXY=X*Y:X=X2-Y2+R:Y=XY+XY+J:NEXT
    3. [...]
    4. 28 NEXTS:[...]

    Auch RETURN im Zusammenhang mit hängenden Schleifen ist da für Überraschungen gut (anderes Programm):

    Code
    1. 17 IF[...]THENI=C:GOSUB41
    2. [...]
    3. 41 FORT=1TO4:I=(I+1)AND3:IFPEEK(G+D(I))=219THENH=I:RETURN
    4. 42 NEXT:RETURN

    Man muß also nicht alle Schleifen schließen, damit einer Unterroutine das RETURN 'erlaubt' wird!


    Dieses Konstrukt ist für BASIC-Compiler besonders schwer verdaulich.

  • Ja, solche Sachen sind hübsch, wenn man einmal den BASIC-Stapel und darauf operierenden Konstrukte FOR-NEXT und GOSUB-RETURN verstanden hat. :)


    Was ich außerdem meine, ist, dass ja das, was ein Compiler macht trotzdem stapelorientiert sein darf. Das muss nichts böses sein. Auch Prozeduren/Funktion, Parameterübergaben, Exception-Handling, etc. verwenden ja auch Stacks und andere Datenstrukturen. Ein Compiler macht ja nicht unbedingt einen langsamen Code deswegen, weil er das FOR-NEXT auch über einen Stack löst, vielmehr gewinnt man ja schon massiv, wenn die Laufvariable nicht notwendigerweise ein Float sein muss (m.E. am meisten). ;)

  • Noch mehr absurdes Zeug...ich habe den Template-Mechanismus so ausgebaut, dass er nun faktisch tatsächlich nutzbar ist. Das hier dürfte eine der wenigen Webseiten sein, deren Template in 6502-Assembler geschrieben wurde (Code hängt unten an der Ausgabe dran):


    http://goo.gl/5vc7cd


    (Eine BASIC-Variante gibt es auch, geschrieben ohne Zeilennummer und mit Labeln stattdessen: https://goo.gl/dcxCtc )

  • Ich werd mal in der 4ma nachfragen ob ich das naechste In-House Projekt mit Weboberflaeche mit 6502 Assembler oder Basic V2 machen darf. Dann hab ich endlich mal nen Grund beides zu lernen *gg*


    Ich glaube das waere eine geniale Ueberraschung kurz vor einem Jobwechsel. Der Nachfolger hat _richtig_ Freude...

  • Ich habe mal wieder ein bisschen Zeit in das Projekt investiert. Das meisten Änderungen betreffen Kleinkram wie die korrekte Verarbeitung von völlig absurden Printanweisungen, die ohnehin niemand wirklich benutzt, die aber eben nicht 100% kompatibel ausgewertet wurden.


    Nützlicher mag sein, dass jetzt auch eine vorkompilierte Version inkl. minimaler GUI zum Laden und Starten von BASIC-Programmen enthalten ist. Um die zu nutzen, muss man nur Java 7 oder höher installiert haben. Dann kann man entweder das gesamte Projekt runterladen und das basicv2.jar in /dist/ starten oder die run_gui.cmd in Wurzelverzeichnis starten...oder man kann das Jar einfach einzeln laden: https://github.com/EgonOlsen71…w/master/dist/basicv2.jar


    Die meisten Browser nölen dann rum, dass Jar-Dateien ganz schlimm sind und man muss explizit bestätigen, dass man die haben will. Es ist aber nichts schlimmes drin. Wer dem trotzdem nicht traut, kann die Datei ja aus den Quellen auch selber neu erzeugen.


    Die GUI hat drei Knöpfe, LOAD, RUN/STOP und PAUSE/RESUME. Das dürfte selbsterklärend sein. Sie öffnet immer eine Konsolen-Emulation, also etwas, dass ein bisschen wie ein C64-Textbildschirm aussieht und sich auch ansatzweise so verhält. Alle PRINTs, POKEs für Farben und Formen und GETs und INPUTs landen in dieser Konsole. Der Zeichensatz ist in der Konsole mittels STRG+SHIFT umschaltbar.


    Die Grafik-Erweiterung ist hier per Default aktiv.


    Vielleicht macht diese Ergänzung die Nutzung für den ein oder anderen einfacher.

  • Die Firefox-Browser mit Ausnahme der Entwicklerversion lassen überhaupt keine Java-Apps in der Weboberfläche mehr zu, ebenso Chrome. (Exakt gesagt, das Java-Plugin wird nicht mehr unterstützt).


    Sofern Du also keine Standalone Java-App kompiliert hast, läuft es nur noch im IE (und evtl. in Nischen-Browsern, die ich nicht kenne).

  • Die meisten Browser nölen dann rum, dass Jar-Dateien ganz schlimm sind und man muss explizit bestätigen, dass man die haben will. Es ist aber nichts schlimmes drin. Wer dem trotzdem nicht traut, kann die Datei ja aus den Quellen auch selber neu erzeugen.

    Nein, das ist nur ein Jar. Das läuft auf dem Desktop, wie eine Exe-Datei. Das hat mit Applets nichts zu tun.


    Sorry, hatte nur das erste Zitat im Augenwinkel gehabt. Nichts für ungut!

  • Ein kleines Update: Das Projekt ist nicht tot! Ich habe angefangen, es so zu erweitern, dass am Ende auch echter 6502-Machinencode aus dem Programm erzeugt werden kann, wenn man das denn will. Dazu kompiliere ich erstmal in eine Art Zwischencode, der wie Assembler aussieht, aber keiner bestehenden Architektur wirklich entspricht. Für den habe ich einen einfachen Interpreter geschrieben, der diese Pseudo-Machinensprache-Programme dann ausführen kann. Wenn das alles funktioniert (momentan ist noch nicht jeder Befehl implementiert), kann man das Programm auf dieser Basis in 6502- oder x86-ML oder sonstwas umwandeln.


    Hier ein Beispiel für Primzahlen. Das BASIC-Programm sieht so aus:


    Code
    1. 10 w=500:dim f(500):p=1:a=3
    2. 20 printa:f(p)=a:p=p+1:if p>w then stop
    3. 30 a=a+2:x=1
    4. 40 s=a/f(x):if s=int(s) goto 30
    5. 50 x=x+1:if x<p and f(x)*f(x)<=a goto 40
    6. 60 goto 20




    Das Kompilat in die Zwischensprache sieht dann so aus (und funktioniert auch):



    Es geht z.B. auch sowas:


    Code
    1. A((1/183)*(B+ASC(A$+"hello")*2+-ASC((MID$("32222",2)+A$)+A$)/2),D%)*A(0,D%+D%+COS(A(0,0)-1.2))+A(1,C*2)*B(B(ASC("A")-96)*4)


    ...das sieht dann so aus:



  • Da ist Trigonometrie drin!
    Kann ich mir auf V2 Basic Basis Assembler Schnipsel machen mit Echtzeit Trigonometrie Berechnungen und diese in einen Cross Assembler wie Acme übernehmen? Ich bräuchte dann auch die Sinus Tabelle die dürfte nicht im System versteckt bleiben jsr Array Access vermute ich mal).


    Geht auch Tangens und Arcus Tangens?


    Ich brauche dann auch kein Python um irgend etwas vor zu berechnen.

  • Kann ich mir auf V2 Basic Basis Assembler Schnipsel machen mit Echtzeit Trigonometrie Berechnungen und diese in einen Cross Assembler wie Acme übernehmen? Ich bräuchte dann auch die Sinus Tabelle die dürfte nicht im System versteckt bleiben jsr Array Access vermute ich mal).

    Ich habe bisher nicht vor, diese Funktionen neu zu bauen. Ich will bei der (bisher noch gar nicht stattfindenden) Umsetzung von Pseudo-Maschinensprache auf 6502-Maschinensprache für den C64 an diesen Stellen einfach die ROM-Routinen aufrufen. Von daher wird dir das vermutlich nicht wirklich helfen.

  • Da ist Trigonometrie drin!
    Kann ich mir auf V2 Basic Basis Assembler Schnipsel machen mit Echtzeit Trigonometrie Berechnungen und diese in einen Cross Assembler wie Acme übernehmen? Ich bräuchte dann auch die Sinus Tabelle die dürfte nicht im System versteckt bleiben jsr Array Access vermute ich mal).


    Geht auch Tangens und Arcus Tangens?


    Ich brauche dann auch kein Python um irgend etwas vor zu berechnen.

    Sinustabellen kannst Du auch direkt in ACME bauen, da gibt es die Funktionen sin(), cos(), tan(), arcsin(), arccos() und arctan().

  • Sinus Tabelle

    Welche Sinustabelle meinst Du? Die Werte für SIN() werden berechnet (mittels Taylorreihe beim C64).

    Tangens

    Du verwendest wirklich die Tangensfunktion? :/

    das Programm auf dieser Basis in 6502- oder x86-ML oder sonstwas umwandeln

    Ich befürchte so einfach ist das nicht.
    Zunächst ist das Verhalten der NEXT-Anweisung weiterhin ungeklärt. Ich nehme an, es existiert dafür ein eigener Zwischencode-Befehl und ein eigener Next-Stapel?
    Der hauptsächliche Grund dürfte aber sein: Du lädst Fließkommazahlen und sogar Strings in Register.
    x86: Die Standardregister EAX.. sind Integerregister (I32). Theoretisch könnte man ein Float (f32) in solch ein Register laden, aber nicht damit rechnen. Rechnen kann man nur mit der x87-FPU sowie den XMM-Registern. Außerdem müßte man für Fließkommazahlen double (f64) verwenden, um den kompletten Wertebereich des C64 abzudecken. Auf jeden Fall wäre hier eine genaue Registerallokation notwendig. Am einfachsten erscheint es da noch, virtuelle Register A, X, Y etc zu definieren, die dann die verschiedenen Werte aufnehmen können. Eine vollständige Übersetzung nach x86 wäre das allerdings nicht, sondern eher eine halbe Interpretation des Zwischencodes.
    6502: Die Register des 6502 sind bekanntlich nur 8 Bit breit und können weder Fließkommazahlen, noch Strings und noch nicht einmal einfache 16-Bit-Integer aufnehmen. Zur Berechnung von Fließkommazahlen verwendet der Basic-Interpreter mindestens zwei virtuelle Fließkommaregister á 6 Bytes auf der Zeropage. Das bedeutet aber auch, daß eine direkte Umsetzung eines Zwischencodebefehls in 6502-Maschinensprache sehr viel Programmcode benötigt, z. B. PUSH X:

    Code
    1. lda X-facc
    2. pha
    3. lda X-facc + 1
    4. pha
    5. ...
    6. lda X-facc + 5
    7. pha

    Dies könnte man kürzen, indem man diese Routine per JSR aufrufen läßt (und dabei aufpaßt, daß man die Returnadresse vorher sichert). Das eigentliche Assemblerprogramm mutiert dadurch zu einer Ansammlung von JSR-Befehlen in eine runtime (threaded code). Das gilt dann wohl auch für weitere Befehle, z. B. Integeroperationen wie ADD und SUB, sicherlich für MUL und DIV, da der 6502 hierfür keine eigenen Befehle hat. Das jedoch nur, sofern Integeroperationen überhaupt vorgesehen sind und nicht alle Rechenoperationen auf Fließkommabasis ablaufen sollen. Darauf deuten aber

    Code
    1. CMP X,#0{REAL}
    2. JE SKIP4

    sowie das Fehlen von expliziten Konvertierungsbefehlen (Integer -> Fließkomma, Fließkomma -> Integer) hin.
    Problematisch wird es dann bei Stringoperationen. Strings können nicht so einfach auf den Stack gepusht werden, schon gar nicht auf den Prozessorstack. Spätestens hier bräuchte man (am besten) einen virtuellen Stapel, der ebenfalls von der runtime verwaltet werden muß, andernfalls eine aufwendigere Heapverwaltung mitsamt Garbage Collection.
    Kurz gefaßt: Die von Dir im Zwischencode verwendeten Register A, X, Y sind für die Übersetzung in 6502- oder x86-Maschinencode leider keine brauchbare Vorlage. In beiden Fällen müßte eine neue Registerallokation vorgenommen werden.
    Meine Empfehlung:
    1.) Trenne beim Zwischencode zwischen Integer- und Fließkommazahlen, d. h. Du hast Befehle wie MUL_F und MUL_I. Solange auf dem C64 auch einfachste Rechenoperationen mit Fließkommazahlen durchgeführt werden anstelle von Integer, wird das Kompilat nur sehr langsam laufen.
    2.) Überlege Dir die Einführung des Datentyps Boolean, auch wenn dieser in Basic selbst nicht verwendet wird. Bei

    Code
    1. EQ X,Y
    2. CMP X,#0{REAL}
    3. JE SKIP5

    sträuben sich mir die Nackenhaare. Kürzer wäre

    Code
    1. CMPEQ X, Y ; hinterläßt true oder false
    2. BF skip5 ; Branch if false

    3.) Die Verwendung von virtuellen Registern A, X und Y bringt leider für die Übersetzung auf den 6502 nichts. Einfacher (und schneller) wäre es, einen Zwischencode für eine virtuelle Stackmaschine zu generieren, wobei der Datenstack (Variablen, Rücksprungadresse) vom Evaluationsstack (Ausdruckberechnung) getrennt sein sollte.


    Und noch kurz eine Anmerkung am Rande und eine Nachfrage:
    Gehe ich recht in der Annahme, daß Du zur Berechnung von SIN(), COS() etc auf die Javaroutinen zurückgreifst und desweiteren mit dem Datentyp double rechnest? In dem Falle hast Du eine höhere Genauigkeit als auf dem C64. Im (unwahrscheinlichen) Extremfall könnte es bedeuten, daß ein Programm auf Deinem System sauber läuft, aber auf einem C64 falsch rechnet.
    Wie funktioniert bei Dir die Routine REALOUT? Greifst Du dabei auch auf eine eingebaute Routine von Java zurück oder hast Du da was eigenes kreiert?

  • Ich denke schon, dass dieser Zwischencode so taugt. Natürlich kann ich die "Register" so nicht beibehalten, das ist mir schon klar. Der Zwischencode ist auch noch eine Stufe abstrakter als "normaler" Assemblercode wäre. Ich lade keine Strings in die Register, sondern Zeiger auf die Strings im Speicher dieser Pseudomaschine. Das sieht man im Code nur nicht. An sich stehen die Strings momentan im Speicher der Pseudomaschine, eine Garbage Collection darauf führt sie auch durch (das sind momentan diese im Code verteilten COMPACT-Aufrufe).
    Der Mix von Integer und Float ist momentan in der Tat eher wild. Da ich aber das Verhalten des BASIC-Interpreters nachbilden muss (der immer mit REAL rechnet), denke ich momentan, ich komme nicht umhin, so ziemlich alles mit Float zu machen, auch wenn da jetzt im Pseudocode noch INTEGER steht. Das ist mehr als Hint zu verstehen, weswegen ich momentan auch keine Trennung nach INTEGER- und FLOAT-Befehlen habe. Kann sein, dass die noch kommt.
    Das die Genauigkeit bei den Berechnungen nicht passt ist klar. Aber das ist beim Rest des Projektes ja auch nicht anders....damit kann ich leben.


    Mit der EQ/CMP-Kombination bin ich auch nicht so richtig zufrieden. Der Hintergrund ist momentan, dass logische Ausdrücke nicht zwangsläufig nur in IFs vorkommen. Deswegen berechnen EQ, NEQ usw momentan einfach entweder -1 oder 0 und schreiben das Ergebnis zurück nach X. Sie setzen sonst keine Flags oder ähnliches. Der CMP sorgt dann für den eigentlichen Vergleich von irgendwas mit dem, was in X steht. Man würde keine CPU so bauen, aber das ist ja auch keine...von daher...mal gucken, wie weit ich damit komme.


    REALOUT ist normaler Java-Code, leicht erweitert, um das Verhalten der C64-Ausgabe zu emulieren.


    Ach ja, FOR-NEXT...dafür habe ich noch nichts gebaut. Wird vermutlich auf einen extra Stack hinauslaufen...

  • Vielen Dank für die Antwort und Erklärungen!

    Zeiger auf die Strings

    Leider macht das keinen Unterschied, denn auch die Zeiger (16-Bit-Adressen) können nicht in ein 6502-Register geladen werden (anders als z. B. beim Z80). Im Grunde genommen ist bei der Übertragung einer Hochsprache in 6502-Assembler eine direkte Umsetzung unter Beteiligung der 6502-Register nicht oder nur mit großem Aufwand möglich. Guckt man sich z. B. das Kompilat an, das gcc erzeugt, so sieht man, wie aufgebläht und umständlich der Code ist. Daher meine Empfehlung, sich von vornherein nicht am 6502 (A, X, Y) zu orientieren und auf virtuelle Register zu setzen, die bei Fließkommaberechnungen sowieso verwendet werden müssen.
    Am einfachsten dafür geeignet ist die Registerallokation einer virtuellen Stackmaschine mit maximaler (Evaluations-)Stacktiefe (je nach dem reicht 8 bis 12 meistens schon aus). Der Vorteil bei einer virtuellen Stackmaschine ist, daß sich deren Allokationsschema sehr leicht auf verschiedene CPUs abbilden läßt (s. Java). Ansonsten kann man es noch mit einer 3-Adress-Registermaschine versuchen, wobei der Aufwand dabei aber wesentlich größer ist.
    So oder so steht man vor einigen blöden Problemen. Zum einen greifen die Fließkommaroutinen für z. B. SIN im Basic-Interpreter selbst auf die Fließkommaakkumulatoren zu, so daß deren Inhalte vorher irgendwie gerettet werden müssen, es sei denn man trennt die eigenen virtuellen Register von den Akkumulatoren und befüllt letztere für jede Berechnung neu und kopiert anschließend das Ergebnis zurück in die eigenen Register.
    Zum anderen können aber auch Ausdrücke trotz der Begrenzung ihrer Verschachtelungstiefe trotzdem theoretisch eine beliebige Verschachtelungstiefe erreichen, und zwar dann, wenn selbstdefinierte Funktionen (DEF) innerhalb eines Ausdrucks aufgerufen werden. (Dabei muß man auch das Problem der Rekursion im Auge haben.)
    Das bedeutet konkret: Wenn Du eine Anzahl virtueller Register definierst (z. B. A, X, Y), deren Inhalte für Berechnungen in die Basic-Fließkommaakkumulatoren übertragen und anschließend zurückkopiert werden, bleibt die Notwendigkeit, vor dem Aufruf einer selbstdefinierten Funktion auch die virtuellen Register irgendwo zu sichern (register spilling). Das kann geschehen in Abhängigkeit zu den verwendeten Registern in der aufgerufenen Funktion (wobei man sich die Menge dieser Register beim Kompilieren jeweils merken muß), oder man sichert sämtliche virtuelle Register auf einen Schlag. Am Ende muß noch definiert werden, in welchem Register der Rückgabewert übergeben und anschließend weiterverarbeitet wird. Verwendet man als Rückgaberegister z. B. Register A, darf A vorher keinen anderen Wert enthalten, da dieser sonst überschrieben würde.


    Tja, es gibt noch einige Fallstricke beim Übersetzen von Basic nach Assembler. :( Viel Erfolg beim Lösen der Schwierigkeiten. :thumbup: Wäre nett, wenn Du uns weiterhin über den Stand der Entwicklung auf dem Laufenden halten könntest. Es ist auf jeden Fall ein sehr interessantes Projekt.

  • Hmm...aber ich orientiere mich doch gar nicht an den 6502 Registern!? Die sind mir eigentlich noch ziemlich egal, deswegen habe ich ja diese Zwischensprache eingeführt. Ich beabsichtige auch in keiner Weise, meine X,Y,A,B usw. direkt auf irgendwelche CPU-Register zu übertragen, weder 6502 noch X86 noch sonstige. Ich betrachte sie als Platzhalter (die hier aber schon wie Register aussehen...), die letztendlich einen Ort im Speicher darstellen. So wie auch mein Stack gedanklich nicht der CPU-Stack ist, sondern irgendein Speicherbereich. Ich gehe davon aus, dass ich die Inhalte im Wesentlichen immer hin- und herkopieren muss, so wie du das schon beschrieben hast. U.U. kann man an ein paar Stellen Abkürzungen nehmen, aber das Grundprinzip wird so sein. Der aktuell erzeugte Code ist so generiert, dass jede Teiloperation (also z.B. SIN(X) oder A*B) in sich abgeschlossen ist. Ich muss mich nicht darum kümmern, irgendwelche Register über die einzelnen Operationen hinweg zu retten. Im Prinzip legt jede Operation ihr Ergebnis am Ende auf dem "Stack" ab und die folgende holt es von dort wieder runter. Diese PUSH ?/POP ?-Kommandos sind aber nur wenig im Code zu finden, weil sie teilweise bei der Erzeugung schon wegoptimiert werden (PUSH X; POP X macht eben keinen Sinn...). Super effizient und schlank wird das vermutlich in der Tat nicht sein...aber schauen wir mal....ich werde berichten... :D

  • aber ich orientiere mich doch gar nicht an den 6502 Registern

    Ah, okay, ein Mißverständnis meinerseits. Deine virtuellen Register A, X, Y (und B) heißen nur zufällig so wie die Register des 6502. ^^
    Im Prinzip verwendest Du also bereits eine Stackmaschine.

    Super effizient und schlank wird das vermutlich in der Tat nicht sein

    Nun, es wird auf jeden Fall schon mal schneller sein als das normale interpretierte Basic. So viel ist sicher. Beim Kompilieren hast Du ja bereits wesentliche, zeitraubende Vorgänge durchgeführt: DIe Umwandlung der Infix- in eine Postfixnotation, Auflösung der Variablenbezeichner in feste Adressen sowie Zuordnung der Sprünge zu festen Adressen. Diese übliche Vorgehensweise von Basic-Compilern hat stets für eine deutliche Beschleunigung des Basiccodes gereicht.
    Nachteilig bleibt natürlich weiterhin die Verwendung von Fließkommazahlen anstelle von Integer. Man könnte theoretisch mit der Kompatibilität brechen und definieren, daß in einer Operation, bei der beide Argumente vom Typ Integer sind, das Ergebnis auch vom Typ Integer ist, d. h. "d% + 1234" ergibt stets einen Integerwert, auch wenn d% den Wert 32767 hat und damit das Ergebnis wegen des nicht abgefangenen Überlaufs falsch ist. Oder man könnte zu jedem Element auf dem Stack in Echtzeit festhalten, von welchem Datentyp es ist, um so in die verschiedenen Rechenoperationenen verzweigen zu können. Das wäre nicht ganz so schnell, aber bei häufiger Verwendung von Integerzahlen immer noch schneller als reine Fließkommaarithmetik. So könnte man oftmals ansonsten unnötige Konvertierungen (z. B. bei Arrayindizes) einsparen.
    Wenn Du, so wie Du es bereits andeutest, die Befehle nach Datentypen entsprechend ausdifferenzierst, hast Du schnell einen Bytecode, den man relativ einfach auf dem C64 ausführen kann. Ein simpler Interpreter zum Ausführen solchen Bytecodes wäre meines Erachtens der erste Schritt, bevor man sich an eine vollständige Übersetzung nach Assembler macht (, die u. U. gar nicht so viel schneller, aber dafür von der Codegröße viel länger wäre).


    Was den Stack anbelangt, so gibt es drei Möglichkeiten, diesen auf dem C64 zu gestalten:
    1.) Du definierst eine maximale Tiefe von 8-12, was meiner Erfahrung nach für die meisten Berechnungen ausreicht. Dafür legst Du im Speicher 6 kleine Tabellen an, die Du mit dem X-Register (des 6502) direkt adressieren kannst. X enthält dabei den Zeiger auf den Stack (TOS = Top of Stack). Diese Vorgehensweise macht es allerdings nötig, bei Aufrufen von selbstdefinierten Funktionen den aktuellen Inhalt des Stacks irgendwo zwischenzuspeichern.
    2.) Du definierst eine maximale Tiefe von 256 entsprechend 6 Tabellen á 256 Bytes im Speicher adressiert mit dem X-Register und sagst: Wenn dieser Stack überläuft, hat der Programmierer halt Pech gehabt. Eine Tiefe von 256 dürfte in der Tat für die allermeisten Programme ausreichen und ist sogar mehr als damals[tm] UCSD-Pascal angeboten hat.
    3.) Du realisierst den Stack über indirekte Adressierung in Verbindung mit dem Y-Register (6502). Damit ließen sich noch viel mehr als 256 Elemente auf den Stack bringen. Nachteil: Sehr langsamer Zugriff. Würde ich daher eher nicht empfehlen, sondern entweder auf 1) oder 2) zurückgreifen.


    Nebenbei: Solltest Du jemanden brauchen bei der Entwicklung des Interpreters auf dem C64, so würde ich gerne dabei helfen.

    ich werde berichten

    Vielen Dank.


    Noch eine kleine Nachfrage: Wie gehst Du mit DATA-Anweisungen um? Sammelst Du die konstanten Daten in einem gesonderten Datensegment?