Hallo Besucher, der Thread wurde 58k mal aufgerufen und enthält 397 Antworten

letzter Beitrag von Boulderdash64 am

BASIC 4.5 für den C64 (BASIC 3.5 + EIGENE BEFEHLE)

  • Den Vorschlag von JeeK


    fand ich gut und hab ihn mal umgesetzt. Dazu habe ich diese Routine hinzugefügt, deren Quelle stammt aus codebase64.org.

    Aufgefallen ist mir die Zeile "lda #$6F ;secondary address 15 (error channel)"

    Wiese kommt da kein 0F (15) hin? Wenn ich das tue, wird der Fehlerkanal NICHT gelöscht. Bei der Sekundären Adresse $6F blinkt die LED nicht mehr. Warum?


    Weiter hänge ich die aktuelle Version von BASIC45 an. Dort sind einige Fehler ausgemerzt.

  • Aufgefallen ist mir die Zeile "lda #$6F ;secondary address 15 (error channel)"

    Wiese kommt da kein 0F (15) hin? Wenn ich das tue, wird der Fehlerkanal NICHT gelöscht. Bei der Sekundären Adresse $6F blinkt die LED nicht mehr. Warum?

    Das über den Bus gesendete Sekundäradressen-Byte enthält in der unteren Hälfte die eigentliche Sekundäradresse (hier 15) und in der oberen Hälfte einen Kommandocode, der angibt, was jetzt mit dieser Adresse gemacht werden soll (Kanal öffnen, Kanal schließen, oder einfach auf den Kanal zugreifen). $6 ist meines Wissens der Code für "einfach nur darauf zugreifen".


    EDIT: Tatsächlich befinden sich die TALK/LISTEN-Kommandos mit der Geräteadresse und die OPEN/CLOSE/ACCESS-Kommandos mit der Sekundäradresse im gleichen Namensraum: Ob ein mit ATN übertragenes Byte zur ersten oder zweiten Gruppe gehört, wird theoretisch nur an den oberen Bits erkannt, man müsste die beiden Bytes also auch anders herum senden können. Praktisch würde ich mich aber nicht darauf verlassen wollen, dass das mit allen Geräten funktioniert.

  • Aufgefallen ist mir die Zeile "lda #$6F ;secondary address 15 (error channel)"

    Wiese kommt da kein 0F (15) hin? Wenn ich das tue, wird der Fehlerkanal NICHT gelöscht. Bei der Sekundären Adresse $6F blinkt die LED nicht mehr. Warum?

    Das über den Bus gesendete Sekundäradressen-Byte enthält in der unteren Hälfte die eigentliche Sekundäradresse (hier 15) und in der oberen Hälfte einen Kommandocode, der angibt, was jetzt mit dieser Adresse gemacht werden soll (Kanal öffnen, Kanal schließen, oder einfach auf den Kanal zugreifen). $6 ist meines Wissens der Code für "einfach nur darauf zugreifen".


    EDIT: Tatsächlich befinden sich die TALK/LISTEN-Kommandos mit der Geräteadresse und die OPEN/CLOSE/ACCESS-Kommandos mit der Sekundäradresse im gleichen Namensraum: Ob ein mit ATN übertragenes Byte zur ersten oder zweiten Gruppe gehört, wird theoretisch nur an den oberen Bits erkannt, man müsste die beiden Bytes also auch anders herum senden können. Praktisch würde ich mich aber nicht darauf verlassen wollen, dass das mit allen Geräten funktioniert.

    Gibt es da irgenwo ein Auflisung der "Kommandos" mit deren Nibbles im im Kommandokanal?

  • Gibt es da irgenwo ein Auflisung der "Kommandos" mit deren Nibbles im im Kommandokanal?

    ROM-Listing sagt:

    Code
    1. %001ddddd LISTEN (ddddd = device)
    2. %00111111 UNLISTEN
    3. %010ddddd TALK (ddddd = device)
    4. %01011111 UNTALK
    5. %0110cccc "einfach nur darauf zugreifen" (cccc = channel)
    6. %1110cccc CLOSE (cccc = channel)
    7. %1111cccc OPEN (cccc = channel)

    UNTALK und UNLISTEN sind also nichts anderes als TALK und LISTEN für das Gerät mit der Adresse 31, weshalb diese Adresse nicht vergeben werden kann.

  • Mir stechen im Assemblercode ständig diese cmp #0 an etlichen Stellen ins Auge ... nämlich solche, die gar nicht notwendig sind. Das erklärt zu einem gewissen Grad auch etwas die recht beachtliche Größe der Erweiterung. Die folgenden Sachen sammeln sich natürlich über das gesamte Projekt ...

    Bitte das jetzt nicht falsch verstehen, ich möchte hier die Leistung in keiner Weise schmälern, aber das Projekt ist schon soweit, dass es eigentlich schon schön rund ist und rollt. Da kann man sich auch schon mal an die Unebenheiten der Oberfläche wagen, um sie zu begradigen, damit es noch gediegener läuft.


    Oft im Gefüge vorkommend:

    jsr CHRGET 

    cmp #0

    beq label

    was durch

    jsr CHRGET

    beq +

    ersetzt werden kann. CHRGET liefert mit Zero-Flag aber bereits den richtigen Status nämlich, Zeroflag gesetzt, wenn das Anweisungsende erreicht ist. Das ist übrigens auch dann erreicht, wenn ein ":" kommt, nicht nur am Zeilenende (mit 0)!
    Ich nehme an, da werden etliche neue Kommandos, wenn in einer Zeile durch ":" getrennt, Syntaxfehler produzieren, weil sie zwingend nur ein Zeilenende und nicht ein Anweisungsende erwarten ...


    Oder in free.asm:

    lda $0100,y

    cmp #0

    beq +

    Auch hier setzt LDA bereits die Flags entsprechend und das CMP #0 kann entfallen.


    JMP für Sprünge in unmittelbarer Nähe vermeidet man liebend gern, in dem man ein Bxx nimmt, wo eine Bedingung immer erfüllt ist

    Also für obige Teil geht es weiter mit

    iny

    jmp -

    was man üblicherweise schreibt

    iny

    bne -

    (y beginnt bei 0 und wird immer > 0 bleiben und der Input-Puffer wird auch nie einen Rollover auf 0 haben!)


    Dann auch der Klassiker:

    JSR $xxxx

    RTS

    ->

    JMP $xxxx


    Wenn wir schon bei free.asm sind (zufällig):

    FREE hat aber auch syntaktisch einige Fälle offen gelassen ...


    FREE() -> liefert nichts (dem Code nach wird nach Variable ")" gesucht), sollte sich vielleicht wie FREE ohne Parameter verhalten?

    FREE(ABS) -> ABS ist ein Token, auch hier wird vermutlich nach der Variablen mit dem Tokencode gesucht, die es freilich nicht geben kann.


    In diesen Fällen wird die Variablensuche, die nicht erfolgreich sein kann, gar nicht auf Erfolg überprüft und es wird immer in die Variable kopiert. Ich hoffe, das Kopieren macht nicht sonst etwas kaputt. ;)


    FREE() ist keine Funktion mehr, kann also nicht wie FRE() verwendet werden. Die Klammernsyntax suggeriert aber eine Funktion (sie wird nur bei Funktionen verlangt bzw. klammert Ausdrücke bei
    Parametern, dann aber optional. Also Anweisung würde ich die Klammern nicht zwingend voraussetzt. Das spießt sich mit der sonstigen Konvention, wie in den BASIC-Varianten sonst vorzufinden ist.


    Die Variablenangabe erlaubt nicht die Langsyntax für Variablennamen.

    FREE(VAR) -> Syntax Error, ein Variablenname darf beliebig lang sein, signifikant für die Suche sind aber nur die ersten beiden Zeichen. Die anderen Zeichen müssten überlesen werden ...

    FREE(V A) -> ok (es dürfen beliebige Leerzeichen zwischen den Zeichen sein)


    Uhi, ich komme mir jetzt schon vor wie ein Haarspalter ... :S

  • fand ich gut und hab ihn mal umgesetzt. Dazu habe ich diese Routine hinzugefügt, deren Quelle stammt aus codebase64.org.

    Was ich nicht ganz verstanden habe, warum da eine Routine hinzugefügt werden muss. Der Code muss ja schon da sein. Man braucht doch nur zur Auswertung der Variablen DS$ gehen, die ja den Fehlerkanal ausliest und in der Pseudovariablen DS$ hinterlegt. Das macht BASIC 7.0 ja auch so. Man hat dann so nebenbei auch gleich den Fehler gespeichert (statt ihn zu verwerfen).

    Der Code wäre ja dann sonst doppelt in der Erweiterung, oder?

  • Das macht BASIC 7.0 ja auch so. Man hat dann so nebenbei auch gleich den Fehler gespeichert (statt ihn zu verwerfen).

    Wobei man da aufpassen muss. Basic 7.0 macht das nicht immer. Zweimal direkt hintereinader "DS$" abfragen liest den Fehlerkanal nur einmal aus.

    Macht man dazwischen aber einen "DCLOSE #2" (dabei stört es nicht, dass Kanal 2 gar nicht offen ist), so wird wieder abgefragt.

  • Das macht BASIC 7.0 ja auch so. Man hat dann so nebenbei auch gleich den Fehler gespeichert (statt ihn zu verwerfen).

    Wobei man da aufpassen muss. Basic 7.0 macht das nicht immer. Zweimal direkt hintereinader "DS$" abfragen liest den Fehlerkanal nur einmal aus.

    Macht man dazwischen aber einen "DCLOSE #2" (dabei stört es nicht, dass Kanal 2 gar nicht offen ist), so wird wieder abgefragt.

    Richtig, aber das ist unerheblich für die Funktion den Error-Channel erstmalig nach dem impliziten BOOT-Kommando zu bereinigen.

  • Was ich nicht ganz verstanden habe, warum da eine Routine hinzugefügt werden muss. Der Code muss ja schon da sein. Man braucht doch nur zur Auswertung der Variablen DS$ gehen, die ja den Fehlerkanal ausliest und in der Pseudovariablen DS$ hinterlegt. Das macht BASIC 7.0 ja auch so. Man hat dann so nebenbei auch gleich den Fehler gespeichert (statt ihn zu verwerfen).

    Der Code wäre ja dann sonst doppelt in der Erweiterung, oder?

    Habe die Stelle gefunden, die es aufzurufen reicht: (sofern das bei unterschiedlichen BASIC45-Varianten von der Position unverändert bleibt)


    jsr $ce8e


    Das ist kompakter und auch anders gelöst, als das BASIC 3.5 original macht. Statt Open/Close wird direkt mit den IEC-Routinen gesprochen (und dabei sichergestellt, dass wirklich nur Gerätenummern >=8 dran kommen. Der Text aus dem Error-Channel kommt in einen fest angelegten Puffer nach $451 und folgend, in $450 steht die Länge der Meldung. Das Original BASIC 3.5 fordert den Puffer (40 Zeichen) als String vom String-Heap dynamisch an und nach dem Befüllen mit der Fehlermeldung wird der Rest immer mit Nullen aufgefüllt, am Ende hat man dann immer eine 0. Vergleiche BASIC 3.5 ROM-Listing.

  • Habe die Stelle gefunden, die es aufzurufen reicht: (sofern das bei unterschiedlichen BASIC45-Varianten von der Position unverändert bleibt)


    jsr $ce8e

    Super. Vielen dank! Mir war dieser Einsprung nicht bekannt, bzw ich dachte nicht das der Autor von BASIC 3.5 da seine eigene Routine gemacht hat. Habe meine zusätzliche nun durch diesen Einsprung ersetzt.

    Wieder ein paar Bytes eingespart.


    Weiter habe ich auf deine Empfehlung hin

    pasted-from-clipboard.png

    meinen kompletten Code durchforstet und einige Stelle geändert.


    Labelname oder +/-?

    Zu beginn meiner Programmierung kannte ich die Label Option von +, ++, .. oder -,--,... noch nicht. Später habe ich davon gebrauch gemacht. Allerdings leidet auch manchmal die Lesbarkeit darunter und ich nutze wieder sprechende Label.


    pasted-from-clipboard.png

    Ja, das stimmt. Da habe ich nicht zu Ende gedacht. Damit mein RTS genutzt wird MUSS in der Angesprungenen Systemroutine ja schon ein RTS stehen. Das kann ich auch gleich für mich nutzer :rolleyes:

  • FREE hat aber auch syntaktisch einige Fälle offen gelassen ...


    FREE() -> liefert nichts (dem Code nach wird nach Variable ")" gesucht), sollte sich vielleicht wie FREE ohne Parameter verhalten?

    Habe ich geändert

    FREE (return)

    liefert das gleiche wie FREE() (return)

    :saint:


    Spaces innerhalb der Klammer ergeben einen Syntax Error und die Variablenlänge ist beliebig, jedoch werden nur maximal die ersten beiden Zeichen gewertet.


    Kommen wir nun zu einem grundsätzlichen Problem der Funktionsauswertungen eigener "Befehls-"Funktionen.

    Nur zu gerne würde ich diesen Ausdruck VAR=FREE() realisieren. Jedoch habe ich in diesem Thread schonmal diese Problematik angesprochen. Es ist die Schwierigkeit das der Ausdruck FREE() els Variablen Array angesehen wird, noch bevor ich mit meinem eigen Tokenizer, bzw Dispatcher auf meine eigene Routine verweisen kann.

    Darauf hin ist als Workaround diese Möglichkeit entstanden.


    Gerne nehme ich Vorschläge, wie ich eigene Funktionen, wie X=FUNCTION(WERT) oder X$=FUNCTION$(WERT,WERT$) schreiben kann.

  • Kommen wir nun zu einem grundsätzlichen Problem der Funktionsauswertungen eigener "Befehls-"Funktionen.

    Nur zu gerne würde ich diesen Ausdruck VAR=FREE() realisieren. Jedoch habe ich in diesem Thread schonmal diese Problematik angesprochen. Es ist die Schwierigkeit das der Ausdruck FREE() els Variablen Array angesehen wird, noch bevor ich mit meinem eigen Tokenizer, bzw Dispatcher auf meine eigene Routine verweisen kann.

    Darauf hin ist als Workaround diese Möglichkeit entstanden.


    Gerne nehme ich Vorschläge, wie ich eigene Funktionen, wie X=FUNCTION(WERT) oder X$=FUNCTION$(WERT,WERT$) schreiben kann.

    Das hab ich ja schon in Assembler: VAR$=MEINE_FUNC$(INTEGER) in BASIC Erweiterung. Aber wie? beantwortet und gelöst.
    Es ist halt nicht schön, weil eine Erweiterung einen eigenen Tokenizer mitbringen muss. Nun, die BASIC 3.5 Erweiterung muss aber bereits einen haben. Das heißt, nach dem das Ding im RAM ist, muss man diese Routine Patchen können um eigene Funktionen unterschieben zu können (und man die Routine nicht noch einmal komplett dabei hat). Die Stelle müsste man noch suchen ...


    Vielleicht noch einmal zusammen gefasst: Dass in deinem Fall die Funktion als "Array"-Variable gesehen wird, liegt schlichtweg daran, dass der Tokenizer, der vorher die Zeile nicht FREE als Schlüsselwort in ein Tokencode umgesetzt hat, das beim Interpretieren nur als Variable ansehen kann. In der Ausdrucksauswertung müsste dann auch der neue Token entsprechend erkannt und zur neuen Funktion verzweigen.
    Ein bisschen russisch wäre der Ansatz, wenn man die Funktion ohne Token abbildet und sich nur in der Ausdrucksauswertung reinhängt (also nicht nur in der Interpreterschleife) und im Klartext nach "FREE(" Ausschau hält und behandelt.

  • Hier eine aktuallisierte Version meine BASIC 4.5 Erweiterung.

    Ich habe den Befehl HELP durch INFO ersetzt, somit steht der eigentliche HELP Befehl von BASIC 3.5 wieder zur Verfügung.

    Den Befehl FREE(VAR) ist durch FREE [VER] verändert worden. Damit habe ich ihn mehr an den BASIC Like Standard angepasst

    Ein neue Befehl zum Einlesen eines Directory in String Arrays sit hinzu gekommen.

    Danke an User "JeeK" für die klasse Vorlage :thumbup:

    Syntax: FILES V$(0),I,U8|U9 ... V$(0) ist eine beliebige Stringvariable, I eine beliebiger Integer Variable und U8 ist das Drive (U8 U9 U10....)

    Dabei werden die Einträge in V$() gespeichert und I enthält die Anzahl der Einträge. Ist aber noch was buggy, wenn ein Laufwerk nicht "present" ist oder keine Disk eingelegt ist.

  • Hier eine aktuallisierte Version meine BASIC 4.5 Erweiterung.

    Vielen Dank für deine BASIC-Erweiterung. Ich lade mir die auch alle runter. :thumbup:


    Allerdings habe ich nun schon die dritte "basic45.prg" auf der Platte. Wäre eventuell hilfreich, die verschiedenen Releases mit Versionsnummern oder entsprechenden Zusätzen zu versehen?


    Würde später einmal auch die Fehlersuche erleichtern, falls in sechs Monaten mal jemand nachfragt, dass der Befehl XY bei ihm nicht geht. Auf die Frage: Welche Version? kommt dann die Antwort: Ja, die basic45.prg eben! ;)

  • Den Befehl FREE(VAR) ist durch FREE [VER] verändert worden. Damit habe ich ihn mehr an den BASIC Like Standard angepasst

    Nur keinen Stress, aber das FREE() ist noch keine echte Funktion, die auch durch den Tokenizer und der Ausdruckauswertung "verstanden" wird, oder?

    Ich kann ja mal schauen wegen dem Hook im bestehenden Tokenizer von der Kern der BASIC3.5-Erweiterung. Nur wegen dem blöden FEE() extra einen weiteren Tokenizer einzupflanzen hielte ich für übertrieben. ;)

  • Genau so habe ich es auch gesehen und mich entschieden. Ich hätte den meine Tokenizer und den Dispatcher komplett umschreiben können/müssen. Und (und das ist viel entscheidender) den richtigen Einstieg im Tokenizer von Michaeal Schimeks BASIC 3.5 finden müssen. Ich glaube es zwar gefunden zu habn, wollte aber weiter an meinem Konzept "glauben" und dran festhalten. Daher den "taktischen" umweg.


    Dir Jeek, danke für jedwege unterstützung und Idee.

  • Hier eine aktuallisierte Version meine BASIC 4.5 Erweiterung.

    Vielen Dank für deine BASIC-Erweiterung. Ich lade mir die auch alle runter. :thumbup:


    Allerdings habe ich nun schon die dritte "basic45.prg" auf der Platte. Wäre eventuell hilfreich, die verschiedenen Releases mit Versionsnummern oder entsprechenden Zusätzen zu versehen?

    Gibt es. Wenn du den Befehl VER aufrufst, dann bekommst du einen SOFTWARE REVISION. Die setzt sich aus JJJJ.MM.TT zusammn. Also sollte da von heute 2020.02.18 stehen.

  • Gibt es. Wenn du den Befehl VER aufrufst, dann bekommst du einen SOFTWARE REVISION. Die setzt sich aus JJJJ.MM.TT zusammn. Also sollte da von heute 2020.02.18 stehen.

    Ich glaube gemeint ist eher, dass das Release-File, zumindest wenn man es hier postet, nicht immer den gleichen Namen bekommt, sondern eine Release-Kennung enthält, die sich gleich im Dateinamen widerspiegelt und nicht den Aufruf der Erweiterung erfordert.

    Wenn ich in 3 Verzeichnissen basic45.prg herumliegen haben, hilft mir Datum und Größe auch nicht unbedingt einzuordnen, welches das neuere oder ältere

    ist. Daher würde ich das .prg mit einer Datumskomponente oder fortlaufender Nummer posten. ;)

    Ist ja nur ein Vorschlag. Dann müssen nicht alle, die das herunterladen mit einem Datumszusatz versehen (erzeugt X-mal Arbeit, statt 1x beim Releaser) ... und viele machen es erst gar nicht und sind dann erst recht in der Falle von gleichnamigen Exemplaren. :S

  • Daher würde ich das .prg mit einer Datumskomponente oder fortlaufender Nummer posten. ;)

    Das würde ich auch empfehlen, vor allem, weil sich der Aufwand dafür - denke ich - in sehr überschaubaren Grenzen halten dürfte. ;)


    Die Anzeige mit dem VER-Befehl ist auf jeden Fall eine sehr gute Idee und bei eventueller Hilfe bei Fehlerrückmeldungen ganz hilfreich. :thumbup:


    Ich persönlich würde eine Versionsanzeige direkt in der Titelzeile (also z.B. "BASIC 4.5 E" oder "BASIC 4.5 R20" ...) bevorzugen, dann wäre das gleich beim Start sichtbar. Aber das ist einfach Geschmackssache und bleibt schon dem Entwickler überlassen. :)


    Insgesamt gefälllt mir die Erweiterung wirklich sehr gut! Tolle Arbeit! :thumbup: