Posts by JeeK

    Muss mir mal bei Gelegenheit das "1-Pixel-Problem" auf dem 1801 anschauen

    Denk dran unterschiedliche Farben zu benutzen.. Blau und Gelb oder Rot und Grün. Letzteres ist schon bei 2 Pixel hintereinander ziemlich heftig.

    Jup. Farben sind so oder so entscheidend.

    Ich hatte damals SpeedDOS vom Bruder geschenkt bekommen und der Verkäufer/Einbauer hatte die Textfarbe auf Schwarz gesetzt. Schwarz auf Blau sah auf dem Montior auch blöd aus.

    Bei meiner gepimpte SpeedDOS+ Version war ich mit schwarzen Rahmen und schwarzen Hintergrund mit hellgrüner Zeichenfarbe. Ich hatte so ein Faible für den PET-Look. Ist das eigentlich tendenziell auch dahingehend problematisch oder eher nicht? Hab ihn lange nicht mehr an einem TV in Betrieb gehabt, aber meine Erinnerung ist dahingehend recht positiv. Natürlich war das "Farbenübersprechen" bei einem vertikalen Kammmuster am HiRes-Schirm unübersehbar ...

    Ja, nett. Der Bug ist ein Klassiker (nicht im Sinne der Bekanntheit), sondern von der Art her. Das kommt nicht selten vor, dass man sich mit dem Carry-Flag verhaspelt. Und diese Fehler fallen mitunter auch nicht sofort auf, weil von der "Geschichte" davor zufälligerweise das Flag richtig gesetzt sein könnte. Manchmal kommt man vielleicht auch drauf, dass man sich das explizite Setzen oder Löschen des Carry-Flag sparen kann. Aber dann baut man da oder dort im Code um und plötzlich halten die Vorbedingungen nicht mehr und Optimierung ist hinfällig und so ein Bug hat sich dann eingeschlichen. :S

    Sowas kommt mir nur allzu bekannt vor und ich nehme an, das werden wohl die meisten 6502-Coder auch mal erlebt haben ...

    Achtung: Dies gilt nur für den 8 Bit-6502, aber nicht für den 16 Bit-65816.

    Wobei es hier drauf ankommt, in welchem Modus sich die CPU befindet (Emulation oder Native) und welche Adressierungsarten bzw. Opcodes (neue/alte) und ob das Direct-Page-Register Low-Byte 0 ist (wenn nein, wir auch im Emulation-Mode kein Wrapping gemacht). Alles rund um Stack-Zugriffe wrappt hingegen in der Bank 0.
    Das musste ich mal erwähnen, auch wenn's für die ursprüngliche Fragestellung nichts beträgt. Ich find es einfach nur kurios, dass man Wochen braucht, um sich das Wissen um die Adressierungsspitzfindigkeiten des 816ers einzuarbeiten, sodass man halbwegs sattelfest ist, was dann ärgerlicherweise nach nur wenigen Tagen Nicht-daran-denken wieder wie wegblasen ist, so verwinkelt sind die Gegebenheiten. ;)

    Erstmal an allen Diskussionsteilnehmern einen riesen Dank für das geballte know-how.
    Damit ich mein bisheriges Programm nicht komplett umkrempeln muss, habe ich mich für die Lösung "HEX$(16384,H$)" entschieden.

    Das passt auch irgendwie in mein bisheriges "Erweiterungskonzept". Die letztere Lösung ist zwar die sauberster, bedeuted aber für mich zuviel Aufwand, da ich den Tokenizer, De-Tokenizer, Parser und Executer komplett neu schreiben müsste.

    Sobald es meine Zeit wieder zulässt werde ich zur Tat schreiten und mein Ergebnis hier veröffentlichen

    Gut so. Wie gesagt, die Funktions-Implementierung war (hauptsächlich auch für mich) eine Studie, wie aufwändig das wirklich ist. Ich würde für kleine Erweiterungen auch einen großen Bogen darum machen. Wenn man eine Erweiterung in der Größenordnung von TSB, Simons' Basic etc. macht, dann ist diese Art natürlich obligatorisch. ;)

    jsr $b7f7 ; FAC#1 nach 16 Bit in Y/A bzw. $14/$15

    Vorsicht, da können $schlimmeDinge passieren: In $14/15 bewahren POKE/WAIT die gewünschte Speicheradresse auf, während die restlichen Argumente ausgewertet werden. Jede Basic-Funktion, die ihrerseits diese Speicherstellen benutzt, muss daher ihre Inhalte vorher retten und zum Schluss wieder restaurieren (siehe den Code der Funktion PEEK).

    ...und da Basic-Funktionen beliebig verschachtelt werden können, muss dieses Retten/Restaurieren unbedingt über den Stack erfolgen, nicht über Selbstmodifikationen.

    In der Tat, an das hab ich freilich nicht gedacht. Eindeutig nicht sauber, aber andererseits in der Praxis eher selten vorkommend (dass bei der Auswertung des 2. POKE-Parameters ausgerechnet Stringfunktionen, und dann auch noch HEX$ auftritt). Aber konstruieren kann man so einen Fall alle mal:

    Code
    1. 10 fori=1024to2048
    2. 20 poke i,63andasc(right$(hex$(i),1))
    3. 30 next

    Dazu also folgende aktualisierte Variante:

    function-ext.asm

    function.zip

    Reicht für TOK64 nicht die Doku TOK64.TXT? Oder ist das unvollständig?

    Vielen Dank für das Feedback. Das ist ein Anfang, aber ich hätte gerne Bestätigung, ob sich dies dann auch so manifestiert in der aktuellen Version. Auch möchte ich gerne überprüfen, wie das Tool mit den anderen Zeichen umgeht, die nicht in dieser Liste sind. Wenn also jemand liebenswürdigerweise eine 64-Bit Version des TOK64 Tools (https://github.com/thezerobit/tok64) kompilieren und mir zukommen lassen könnte? Das würde mir sehr helfen. :emojiSmiley-106::emojiSmiley-08:


    Ich für Windows keine Entwicklungsumgebung. Da mögen bitte andere hier im Forum aushelfen (sind ja offenbar schon dabei hier Abhilfe zu schaffen).

    Für mich ist Linux interessant und war jemand schon so nett, eine Anpassung vorzustellen:

    https://github.com/0cjs/tok64 - ist zwar noch ein Pullrequest, der vom Originalautor noch nicht aufgenommen wurde, aber das kommt hoffentlich noch

    (wir warten schon 2 Wochen ... :/)

    Ich kann also nur ein Linux-Compilat anbieten. Oder, wenn es ein Test-PRG gibt, gerne dazu das Listing anfertigen und zur Verfügung stellen.

    Das hab ich ja in meinen vorigen Post ja beschrieben. Der Bad Subscript Error kommt daher, weil du nicht die Zeile mit dem Tokenizer abarbeitest, sondern nur die ersten Zeichen einer Zeile abfragst. Da A$= nicht mit HEX$ übereinstimmt und du weiter an den ROM-Tokenizer gibst, ist für den dann HEX$() eine Array-Variable, die nicht entsprechend dimensioniert ist.

    Ich hab bislang noch nicht eine Funktion als neues Token für meine Erweiterungen gebraucht. Die Fragestellung hat mich aber schon immer interessiert. Da der Aufwand bislang von mir immer als recht beträchtlich eingeschätzt wurde, hab ich immer einen Bogen darum gemacht. Aber jetzt hat mich doch mal interessiert, was man dazu alles braucht und es real ausprobiert.
    Ich möchte keinesfalls sagen, dass ich hier die optimale Lösung gefunden hätte. Ich hab mal ohne einschlägige Literatur zu konsultieren mal meine Vorstellung umgesetzt. ;)

    Exemplarisch gezeigt am Funktionen-Keyword HEX$. Ich verwendet dafür das Token $CC (das nächste freie bei BASIC V2). Nebeneffekt: es gilt damit automatisch als String-Funktion und man muss nicht bei der Ausdrucksauswertung bei der Typfestlegung nicht eingreifen.


    Für die Funktionserweiterung braucht man 3 Teile, den Tokenizer (der irgendwo in einer Zeile befindlichen Keywords in ein Token wandelt), die Auswertung, die das Token erkennt und die Funktion abhandelt und zu guter letzt auch den "Lister", der beim LIST-Befehl das Token auch wieder mit Keyword darstellt.


    Der aufwändige Teil ist der Tokenizer, der hier im Grunde faktisch nochmal in Kopie der aus dem ROM ist, nur bei der Keyword-Suche ist ein neuer Teil eingeschoben. Abgesehen von den anderen Hooks, ersetzt in diesem Fall der "neue" Tokenizer den alten. Etwaige bestehende Hooks von Erweiterungs- oder Alternativ-ROMs (z.B. SpeedDOS+) fallen so raus und deren Befehle werden nicht mehr erkannt.


    Das alles ist eher eine Studie, die genau auf ein Keyword hingearbeitet ist. Wenn man mehrere Funktionen bedienen möchte, muss man ein bisschen mehr dazubauen, also die Keyword-Suche durch eine Liste führen, den Aufruf der Tokens etwa über eine Sprungtabelle machen usw.


    Um gegenüber anderen Erweiterungen und Alternativ-ROMs neutral zu bleiben, hätte ich vielleicht noch einen anderen Ansatz, nämlich zuerst den "originalen" Tokenizer aufrufen und dann erst den eigenen Tokenizer in einer wirklich aufs Notwendigste reduzierten Variante (Strings, DATA und REM muss man aber trotzdem berücksichtigen) über die Token-Zeile in einer Art 2. Pass drüberlaufen lassen, um dann die neuen Tokens einzupflanzen ... kommt vielleicht dann auch noch. :D


    Für Try-Outs bitte einfach dieses Image probieren ... :)

    function.zip

    Es wäre natürlich toll, wenn es eine Unterstützung der ZOOMFloppy in VICE gäbe. Dort ist - wenn auch nur optional - ein IEEE-488 Stecker vorgesehen (wenn man ihn mitbestellt oder ihn selbst nachkauft und drauflötet). Wie komplex das Unterfangen wäre, eine solche Integration hinzukriegen, kann ich leider nicht abschätzen. Weiß jemand mehr dazu?

    Hinsichtlich Timing ist das dann eigentlich nur eine Frage, wie gut z. B. auf VICE-Seite darüber hinweggetäuscht wird, das gewisse, eventuell nicht korrekte zeitliche Verhältnisse per USB in der Emulationszeit "richtig" ankommen ...

    Sehr viel besser und das Beispiel für Stringverarbeitung ist super! Ich habe noch ein paar winzige Typos korrigiert. Vielen Dank (im Namen aller Basicprogrammierer :thumbup:)!

    Freut mich und danke auch für den Feinschliff. Ich schaff es einfach nicht auch nur die kleinsten Passagen zu erstellen, ohne dass mir da irgendwelche Flüchtigkeitsfehler unterlaufen. :S Da bin ich wirklich froh, dass immer viele andere Augenpaare im Wiki "mitschauen".

    Ah, verstehe! Dann finde ich den C64-Wiki-Artikel ein wenig zu vereinfacht formuliert, denn das hört sich so an als ob USR quasi out-of-the-box String-Unterstützung hätte. Aber der Artikel hat eh so einige Schwächen, vielleicht fixe ich zumindest mal die wirklich offensichtlichen...

    Die "out--of-the-box"-Unterstützung passt so nicht ganz. Die USR-Funktion ist so ja nicht vorhanden und besteht nur aus einem JMP über die Vektoradresse. Die kann so gesehen nichs vorbereiten oder im Nachgang etwas tun. Den Umgang mit Strings als Rückgabewert machen auch Funktionen wie CHR$ oder STR$, die dabei den Aufrufkontext explizit festlegen (eher als eine Art "Override-Mechanismus" zu verstehen). Wenn USR sich ebenso verhalten soll, dann muss sie sich auch an dieselben Hausregeln des Interpreters halten.


    Ich hab den C64-Wiki-Artikel dahingehend angepasst und erweitert. Ist das so besser bzw. hilfreicher?

    Dann aber auch richtig den String-Heap-Speicher nicht nur anfordern, sondern auch dann belegen, wie in Assembler: VAR$=MEINE_FUNC$(INTEGER) in BASIC Erweiterung. Aber wie? erläutert. Bei USR muss dann im FAC der Descriptor (der am String-Descriptor-Stack liegt) liegen, der auf den Hex-String zeigt und so in der Ausdrucksauswertung weiter verarbeitet werden kann.

    Dazu muss man mit JSR $B47D (in A die Länge), den Platz anfordern und dann mit TXA; JSR $B487 (mit Stringadresse des angeforderten Strings in A/Y) den Descriptor am String-Descriptor-Stack aufbauen und der Ausdrucksauswertung rückmelden, dass das Ergebnis (das in $61/$62/$63 steht) ein String ist, der am Top of SDS bereit liegt.

    Das ist jetzt etwas knapp angeführt. Ich hab jetzt meine Routinen heraus gekramt, und da bieten sich 2 Möglichkeiten an. Die STR$()-Methode und die CHR$()-Methode.

    Bei STR$() wird der String im Input-Puffer aufgebaut, ab $00FF und der muss 0-terminiert sein. Dabei wird der String automatisch am String-Heap angelegt und rüberkopiert.

    Beim Ansatz wie bei CHR$() wird der String am Heap selbst angelegt und dann nur noch der Descriptor am SDS gelegt.

    Hier ist die STR$()-Variante (Label STR) die Vorgabe. Wahlweise mit JMP CHR die andere Variante wählen.

    Wie auch im ROM wird mit dem Trick gearbeitet, dass die beiden PLA den Aufrufer (der einen nummerischen Wert erwartet!) entfernt wird. Dann ist der Weg frei für einen String als Rückgabewert.


    Ein BASIC-Test-Programm könnte dann so aussehen:

    Code
    1. 10 f=f+1:if f=1 then load "usr-string",8,1
    2. 20 poke785,0:poke786,192
    3. 30 def fn f(x)=40960-peek(51)-peek(52)*256
    4. 100 print fnf(.)
    5. 110 for i=0to100:print usr(i);:next
    6. 200 print fnf(.)

    Das Image mit den Programmen: usr.zip :)

    Ich muss gestehen, dass ich USR noch nie verwendet habe, aber kann der String-Descriptor und der zugehörige String nicht einfach irgendwo im Speicher stehen? Ich hätte gedacht, dass man da nicht groß mit Heap und Stack rumfuhrwerkt, sondern einfach einen Puffer im Assemblerprogramm vorhält (z.B. irgendwo ab $C000). Bei der Zuweisung A$=USR(1234) wird dann doch A$ ordnungsgemäß im Heap angelegt und die Stringdaten dorthin kopiert, oder?

    Die String-Adresse kann auch irgendwo im Speicher stehen, wenn oberhalb des String-Heaps liegend (z. B. ab $c000), dann wird der String auch von der GC in Ruhe gelassen. Nur der Descriptor kann nur in einer Variablen (bzw. einem Array-Element) oder eben am temporären String-Descriptor-Stack liegen. Dafür ist der ja da, sonst funktioniert eine Ausdrucksauswertung nicht mehr, wie etwa H$="$"+USR(X) oder B$ = LEFT$(USR(X),2) ...


    Nein, bei der Zusweisung A$=USR(1234) wird der Descriptor(!) vom Descriptor-Stack in die Variable A$ kopiert und dann entfernt, siehe ROM (LET-Befehl).

    Wo der String im Descriptor hinzeigt ist dann eigentlich egal. Man muss immer unterscheiden zwischen dem Descriptor, der nur auf die String-Daten zeigt und dem String selbst. Man kann nicht sagen "A$ ordnungsgemäß im Heap angelegt", denn genau genommen ist nur der String am Heap und der Descriptor am temporären String-Descriptor-Stack oder in einer Variable (Array-Element).


    Man muss nicht "groß herumfuhrwerken", es sind ja alle Routinen im ROM, man muss sie nur (zugegebenermaßen) richtig aufrufen. Die zwei JSRs mit einem TXA halte ich für zumutbar ... ;)

    Ich hatte die meisten alten Supercpu+Ramcard Anpassungen mit dem Dreamon gemacht. Leider ist daraus in den meisten Fällen ein ganz übler Spaghetticode geworden.8)

    Deshalb suche ich einen Disassembler für den 65816 Code. Ich weiß um die Schwiergkeit des disassemblieren, aufgrund der fast nicht feststellbarkeit, ob bestimmte Register

    aktiv sind oder nicht aktiv sind. Das sind z.B. u.a. gesetzte Bankbytes oder die 8 und 16 Bit Register. Vielleicht kennt ja einer so eine Software. Selbst wenn die Bedienung des

    Programmes sehr umständlich ist, würde ich es nehmen.:)

    Wie wahr! Ich kenn jetzt zwar keinen Cross-Disassembler (Dank Snoopy haben wir ja was), aber im nativ laufenden Monitor Jamaica Monitor plagt sich der Disassembler gehörig. Der führt ein Schattenregister für das Status-Register mit (SEP und REP mithört), um den jeweilig aktuellen 8-Bit- oder 16-Bit-Modus zu erfassen. Da muss man dann auch schauen, dass man mit dem richtigen Startstatus mit dem Disassemblieren anfängt ... Mit weiteren Komplikation, wenn das Ding im Trace-Modus ist und nebenbei disassembliert (dann sollte das Schattenregister nicht das wirkliche Statusregister sein, wie es bei der originalen Version des Monitors war).

    Ob das Data Bank Register auch so mit abgehört wird, war mir noch gar nicht bewusst (sollte jedenfalls auch so sein). Aber Nebenwirkungen durch PLP sind dann schon schwieriger zu erfassen.

    Wär ja super, wenn das 65816disasm das alles halbwegs hinkriegt. ;)

    Ja, verdammt. Ich sollte mal richtig lesen. Du hast natürlich recht. :platsch:
    Dann könnte ich das damit machen. Werds direkt mal testen.


    Vielen Dank :respect:

    Dann aber auch richtig den String-Heap-Speicher nicht nur anfordern, sondern auch dann belegen, wie in Assembler: VAR$=MEINE_FUNC$(INTEGER) in BASIC Erweiterung. Aber wie? erläutert. Bei USR muss dann im FAC der Descriptor (der am String-Descriptor-Stack liegt) liegen, der auf den Hex-String zeigt und so in der Ausdrucksauswertung weiter verarbeitet werden kann.

    Dazu muss man mit JSR $B47D (in A die Länge), den Platz anfordern und dann mit TXA; JSR $B487 (mit Stringadresse des angeforderten Strings in A/Y) den Descriptor am String-Descriptor-Stack aufbauen und der Ausdrucksauswertung rückmelden, dass das Ergebnis (das in $61/$62/$63 steht) ein String ist, der am Top of SDS bereit liegt.

    Der HEX$-Befehl gehört dann in etwa so gepatcht (ab Zeile 54):

    Im ursprünglichen Code ist der Aufruf $B4F4 unnötig ... der angeforderte Speicher wird gar nicht verwendet, sondern in die bestehende Variable geschrieben, wobei nicht einmal die notwendige Mindestlänge von 4 Zeichen überprüft wird (geschweige denn richtig auf 4 gesetzt wird). Ein B$=STR$(1):HEX$(32768) korrumpiert den Speicher am String-Heap (B$ ist nur 2 Zeichen lang!) nach dem B$-String Richtung höherer Adressen.

    Du kannst auch die Variable einfach suchen, statt davon auszugehen, dass die zuletzt verwendete sein muss oder so.

    Oder sogar den Variablennamen als Argument übergeben). Z.B. so

    HEX$(16384,H)

    Das Parsing ist ja da unter deiner Kontrolle, mit CHRGET nach dem Komma die nächsten max. 2 Zeichen als Variablenamen interpretieren und dann (hier jetzt fix mit HE ausformuliert) lokalisieren.


    Code
    1. LDA #"H"
    2. STA $45
    3. LDA #("E" | $80) ; String-Typ, also HE$ wird gesucht
    4. STA $46
    5. JSR $B0E7 ; Variable suchen und gegebenenfalls auch anlegen.
    6. ; Adresse der Variable in A/Y

    Der Hinweis oben bezüglich B$="....." hätte eigentlich keine Relevanz, wenn der Hex-String wirklich in den Stringspace - der angefordert wird! - kopiert werden würde. Dann gehört nur dessen Descriptor in die Variable übertragen. Wenn der B$-Descriptor auf einen String im Programmcode zeigte, dann wird der String nicht mehr referenziert, andernfalls wird der alte String im Stringspace eben zu "Garbage".
    Dann könnte man für obiges Beispiel auch B$="": HEX$(3000): PRINT B$ schreiben. Dann wäre es so, dass nur auf die zuletzt "gesuchte" Variable zugegriffen wird und die Zuweisung ist nur eine Dummy-Zuweisung.

    Inzwischen bin ich zum Ergebnis gekommen, das es so nicht machbar ist. Mein eigenes Kommando "HEX$" wird vom CBM Parser als indizierte Variable gehandhabt.
    Der Aufruf A$=HEX$(16348) kommt daher immer zum "Bad subscription Error". Was auch klar ist. Dazu müßte ich das BASIC kopieren und abändern, das HEX$ als Basic Befehl angesehen wird und nicht als indizierte Variable. Ähnlich wie mit der STR$() Funktion.
    Daher habe ich mich vorerst für einen anderen Weg entschieden, so zusagenein Workaround. Vor dem Befehl definiere ich eine beliebige STRING Variable mit 4 Bytes Länge. Rufe den Befehl auf und dort wird dann das Ergebnis in diese Variable gelegt. Das sieht dann so aus:

    Das hab ich ja in meinen vorigen Post ja beschrieben. Der Bad Subscript Error kommt daher, weil du nicht die Zeile mit dem Tokenizer abarbeitest, sondern nur die ersten Zeichen einer Zeile abfragst. Da A$= nicht mit HEX$ übereinstimmt und du weiter an den ROM-Tokenizer gibst, ist für den dann HEX$() eine Array-Variable, die nicht entsprechend dimensioniert ist.


    Als Workaround, wenn man sich den Tokenizer-Varianteaufwand nicht antun möchte, ist die Übergabe an eine fixe Variable natürlich auch gut. Da aber nicht darauf spekulieren, dass die Variable schon befüllt ist, sondern wie es sich eigentlich sauber gehört, den String-Speicher anfordern und in den Descriptor der String-Variable den neu angelegten String eintragen. Der eventuell alte String ist dann "Garbage", um den sich die Garbage-Collection kümmert (kein Angst, diese Funktion wird das Kraut auch nicht fett machen, wenn man Angst hat, dass einem die GC widerfährt ... dann halt eine lineare Implementierung nehmen).
    Für deinen Fall ginge das etwa so

    Gibt es den Sourcecode von Elite irgendwo als Download (idealerweise von der C64-Version, aber gerne auch andere Fassungen) oder stehen da immer noch rechtliche Hürden im Weg?

    Zum Bleistift hier und hier:

    http://www.elitehomepage.org/archive/

    https://github.com/kieranhj/elite-beebasm

    Ich hab's glaub ich hier schon erwähnt, das Github-Repo von Kroc, wobei M.J. die Qualität nicht sonderlich überragend findet (was die Durchdringung bei den Erklärungen der Routinen, speziell der mathematischen angeht - mangels Vergleich kann ich das nicht beurteilen). Dennoch eine nette Stöbervariante, allerdings aber eben "nur" ein Disassembly im neuen Gewand, nicht etwa der Original-Sourcecode ...