Hallo Besucher, der Thread wurde 11k mal aufgerufen und enthält 69 Antworten

letzter Beitrag von syshack am

Zusätzlichen Basic Befehl erstellen. Wie?

  • Ein sehr erhellendes Beispiel wodurch mir die Funktionsweise der Konstrukte DEF FN (..) klarer geworden ist, danke.


    Die Beispiele 1..3 zeigen m.E., dass man den Cursor auch separat positionieren kann ohne den User-Sprung innerhalb des Print-Statements verwenden zu müssen. Das scheint praktisch, wenn vor der eigentlichen Ausgabe noch eine Verzweigung stattfindet; so kann gemeinsamer Code vorgezogen (oder aus einer Schleife an den Anfang herausgezogen) werden.


    Richtig clever im Sinn von übersichtlich finde ich die X,Y-Koordinaten in einer einzigen REAL-Zahl als Vorkomma-Nachkomma-Anteil zu übergeben! :)


    Zwei Dinge sind mir noch aufgefallen bzw. unklar:
    Wozu dient die Gestaltung re=59736, hat das mit dem USR-Pointer was zu tun? Auch, was das nachgestellte Sys re bewirkt.


    Dass nach dem USR-Aufruf ";if.then:return" steht soll nur demonstrieren, dass die Maschinenroutine diesen Teil gar nicht mehr auswertet und zur nächsten Basic-Zeile gesprungen wird?


    Mir ist auch nicht ganz klar, ob DEF FN in Verbindung mit USR die Beschränkung auf eine Zeile überwinden kann?
    Und wo die Maschinenroutine des USR-Vektors steht - oder wird der gar nicht verbogen (?) wäre nett wenn du dazu ein paar Takte sagen könntest..

  • Um auf die Eingangsfrage zurückzukommen - grundsätzlich gibt es folgende Methoden, um "Zusatzbefehle" zu realisieren. Gerade die erste Variante wird gerne übersehen:



    1. Unterprogramme (mit Parametern) definieren und dann mit GOSUB aufrufen,


    2. Maschinencode mit SYS aufrufen, und entweder POKE, PEEK() oder Routinen des Interpreters (s.o., CHKCOM, GETBYT, etc.) zur Parameterübergabe nutzen,


    3. einen Wedge in CHRGET bei $0073 reinsetzen. Eher eine schlechte Methode, stammt noch vom PET. Verlangsamt den Interpreter merkbar, da die Wedge immer abprüfen muß, von wo CHRGET angesprungen wurde (gibt 22 Stellen im Interpreter, nur eine ist brauchbar),


    4. einen Wedge in ($0308) reinhängen (character dispatch), zusammen mit einem Marker-Token (üblicherweise "@" oder "<-"). Dahinter können noch Standard-Tokens stehen, die dann anders interpretiert werden. Hab' ich z.B. in MINIGRAFIK für den VC-20 gemacht, wo dann die neuen Befehle @ON, @CLR, etc. dazukommen. Gängiges Kennzeichen dieser Erweiterungen ist, daß man zwischen THEN und den neuen Befehlen einen Doppelpunkt setzen muß, sonst gibt's einen ?SYNTAX ERROR (... es sei denn, IF ist auch umgebaut worden - hab' ich bei MG allerdings auch nicht gemacht),


    5. auch oben schon angesprochen - die aufwendigste Variante mit eigenem Tokenizer, Detokenizer, Character dispatch und Function Call. Da kann man die neuen Befehle nicht mehr von den normalen unterscheiden, es braucht kein Marker-Zeichen mehr. Hab' ich auch schon gemacht, sowohl für BASIC V2 als auch für BASIC V7.



    In den Fällen 4 und 5 muß man sich *sehr* genau im Interpreter auskennen und schauen, wie der selber arbeitet. Teilweise ist es wichtig, daß nicht nur die Register, sondern auch die Prozessorflags wieder exakt so hergestellt werden, wie sie der Interpreter erwartet (u.a. bei vorzeitiger Rückkehr in die Interpreter-Schleife). Das steht bei all' den unzähligen Beispielen leider nirgendswo explizit dabei.


    Gruß,


    Michael

  • Er tut genau das was du in Methode 1 empfiehlst: Implementierung als Unterprogramm.
    Für Häme sehe ich keinerlei Anlass.

    Das tue ich in Beitrag #20 auch. Siehst Du den Unterschied? Mein Beispiel funktioniert, ohne am Zustand des Interpreters herumzuschrauben zu müssen und ist ansonsten funktionsgleich. Und lesbarer. Und kann (sofern erwünscht) problemlos kompiliert werden, ohne daß der Compiler Schluckauf bekommt. Etc. pp.


    Du solltest doch mittlerweile mitbekommen haben, daß alle Postings von BIF dem gleichen Muster folgen: es wird irgendein trviales Programmierproblem möglichst so "gelöst", daß anhand des Codes für einen Programmier-Anfänger nicht die geringste Chance besteht, nachzuvollziehen, wie es funktioniert. Die Erklärungsarbeit überläßt BIF aber den Leuten, die derartige Methoden gar nicht brauchen um zum gleichen Ergebnis zu kommen.


    Also: für jedes dieser Beispiele von BIF läßt sich problemlos eine einfachere und verständlichere Re-Implementierung angeben. Mehr hab' ich nicht gemacht. Jetzt weißt Du, was das Programm in Beitrag #18 macht; was es *genau* macht - abzüglich BIFs "Dekoration" - steht in Beitrag #20.

  • Darstellen einer Windrose am Bildschirm.
    Die Namen der 4 Himmelssrichtungen sollen jeweils mittig an der X bzw Y-Achse angeordnet werden.


    PRINT FNCP(18.1)"NORD" FNCP(18.23)"SUED" FNCP(1.12)"WEST" FNCP(35.12)"OST"


    Meines Erachtens kann man bei BIFs Lösung hier einen einzigen String schreiben, ohne dass man Variablen zuweisen (und verbraten) muss. Können andere Lösungen das auch?

  • Meines Erachtens kann man bei BIFs Lösung hier einen einzigen String schreiben, ohne dass man Variablen zuweisen (und verbraten) muss. Können andere Lösungen das auch?

    BIFs "Lösung" verbrät die Variable a, die Variable re, den Funktionsnamen cp, den USR-Vektor und (natürlich!) die Lesbarkeit des Programms.
    Andere Lösungen wären:
    1. Steuerzeichen verwenden
    2. Stringfunktionen auf einen vordefinierten String aus "home", "cursordown"s und "cursorright"s anwenden
    3. Basicerweiterung verwenden
    4. brauchbare Programmiersprache verwenden

  • Darstellen einer Windrose am Bildschirm.
    Die Namen der 4 Himmelssrichtungen sollen jeweils mittig an der X bzw Y-Achse angeordnet werden.


    PRINT FNCP(18.1)"NORD" FNCP(18.23)"SUED" FNCP(1.12)"WEST" FNCP(35.12)"OST"


    Meines Erachtens kann man bei BIFs Lösung hier einen einzigen String schreiben, ohne dass man Variablen zuweisen (und verbraten) muss. Können andere Lösungen das auch?

    Geht in BASIC V2 mit einem geeigneten String aus Steuerzeichen out-of-the-box auch. Mit X,Y-Positionierung und wie in meinem Beispiel braucht das eben vier Unterprogrammaufrufe und vier PRINT-Anweisungen, aber was soll daran schlechter sein?


    Ansonsten gibt es sicher einige BASIC-Erweiterungen, die das als "TAB("- bzw. "SPC("-ähnliches Konstrukt (also, etwa wie in AT(X,Y)) für den Gebrauch als Argument in einer PRINT-Anweisung mitbringen - dabei sollte dem Benutzer die Implementierung dann auch egal sein, da es ja gerade Sinn der BASIC-Erweiterungen ist, das hinter einer komplizierteren Aktion *) stehende Magengrummen zu verbergen, und natürlich (hoffentlich!) auch schneller zu machen.



    *) Cursor-Positionierung ist nun gerade nicht wirklich kompliziert, weswegen ja ein Unterprogramm mit 2 POKEs und 1 SYS ausreicht, um AT(X,Y) nachzubilden. Bei Grafik-Anwendungen sieht's da natürlich anders aus.

  • Ansonsten gibt es sicher einige BASIC-Erweiterungen, die das als "TAB("- bzw. "SPC("-ähnliches Konstrukt (also, etwa wie in AT(X,Y)) für den Gebrauch als Argument in einer PRINT-Anweisung mitbringen

    Bei BIFs Lösung bekommst du die diesbezügliche Funktionalität in nur 2 Zeilen Definition DEF FN frei Haus & out-of-the box ohne eine einzige Zeile Maschinencode. Die Verwendbarkeit als Argument wie auch die konkrete Implementierung finde ich ehrlich gesagt ziemlich genial.


    Er benutzt den Vektor "Nächste Anweisung ausführen" indem er ihn in den USR-Vektor einsetzt, okay, aber verbiegen nachzuladende Basic-Erweiterungen nicht ebenso Vektoren?


    Die Möglichkeit, die Cursorpositionierung bzw. deren Syntax aus C-Quelltexten ( = brauchbare Programmiersprache" würde Mac Bacon sagen ;) )wiederzuverwenden, kann ein beträchtlicher Vorteil bei der Anpassung / Übernahme von Quelltexten zu sein.


    Das alles sage ich ohne deine Gegenvorschläge in irgendeinerweise zu schmälern; diese haben ebenso ihre Berechtigung.

  • Geht in BASIC V2 mit einem geeigneten String aus Steuerzeichen out-of-the-box auch.

    Konkret:



    windrose.prg


    :D

    Bei BIFs Lösung bekommst du die diesbezügliche Funktionalität in nur 2 Zeilen Definition DEF FN frei Haus & out-of-the box ohne eine einzige Zeile Maschinencode.

    Hier eine Zeile "Vorbereitung" und eine Zeile gewünschte Funktion. Wenn es auch notwendig war, Abkürzungen zu verwenden und den VC-20, um selbst die mit Abkürzungen eingegebene Zeile in des VC-20's 88-Zeichen-pro-Zeile-Limit hinein zu bekommen. Und dann nochmal in den C64 reingeladen und gespeichert, um die Ladeadresse auf $0801 zu korrigieren.


    Die Verwendbarkeit als Argument wie auch die konkrete Implementierung finde ich ehrlich gesagt ziemlich genial.

    Da ist einfach überhaupt nichts genial dran.


    Er benutzt den Vektor "Nächste Anweisung ausführen" indem er ihn in den USR-Vektor einsetzt, okay, aber verbiegen nachzuladende Basic-Erweiterungen nicht ebenso Vektoren?

    Natürlich müssen BASIC-Erweiterungen die Vektoren *ändern* um ihre Funktionalität zu realisieren. Sie passen sich aber idealerweise so in den Interpreter ein, daß keine verquere Syntax benutzt werden muß, um die Funktionalität auch anzusprechen. Und nochmal: der gemeine "BASIC-Erweiterungs"-Nutzer will überlicherweise gar nicht wissen, wie die Funktionalität implementiert ist, er kann und darf sich darauf verlassen, daß die Erweiterung das tut, was sie soll.


    Die Möglichkeit, die Cursorpositionierung bzw. deren Syntax aus C-Quelltexten (= "brauchbare Programmiersprache" würde Mac Bacon sagen) wiederzuverwenden, kann ein beträchtlicher Vorteil bei der Anpassung / Übernahme von Quelltexten zu sein.

    Also bitte: die Cursorpositionierung ist nun wirklich ein triviales Problem, das schon 1000x irgendwie gelöst wurde (bzw., auf dem C64, gelöst werden mußte) - und nochmals: zwei Standardlösungen (2x POKE, 1x SYS bzw. Cursor-Steuerzeichen) sind dir hier jetzt schon gezeigt worden, und beide funktionieren *einfach* *so*. Bei BIFs Programmfragmenten läufst Du schlimmstenfalls Gefahr, daß die Methode zwar in einem trivialen Umfeld funktioniert, dir den Interpreter aber so versaut, daß ein größeres Programm ohne dir erkennbaren Grund absemmelt.


    Davon ab arbeitet Standard-C nur auf stdin und stdout und da gibt's keine Cursor-Positionierung. :P


    Das alles sage ich ohne deine Gegenvorschläge in irgendeinerweise zu schmälern; diese haben ebenso ihre Berechtigung.

    Ah, ja.

  • Davon ab arbeitet Standard-C nur auf stdin und stdout und da gibt's keine Cursor-Positionierung. :P

    Cursor ist ja auch der Job des Terminals ;)


    Code
    1. printf("\033[%d;%dH", row+1, column+1);
  • Achso, Stephan, nochwas: dein Anwendungsbeispiel:

    PRINT FNCP(18.1)"NORD" FNCP(18.23)"SUED" FNCP(1.12)"WEST" FNCP(35.12)"OST"

    ... würde so ohnehin nicht wie gewünscht funktionieren: die FNCP(...)-Funktion liefert einen Wert (und zwar 0) zurück, der so unerwünschterweise mit ausgedruckt wird. In Zeile 3 von Beitrag #18 wird das unterdrückt, indem mit CHR$(FNCP(...)) daraus ein nicht-druckbares Zeichen generiert wird - das ist aber nur ein Notbehelf, der eigentlich nicht nötig sein sollte.


    Zitat von Unseen

    Cursor ist ja auch der Job des Terminals ;)

    :bia

  • Also bitte: die Cursorpositionierung ist nun wirklich ein triviales Problem, das schon 1000x irgendwie gelöst wurde (bzw., auf dem C64, gelöst werden mußte)


    Ganz recht - es wurde "irgendwie" gelöst ;)
    Irgendwie, z.b. mit Strings (= garbage collection wird bemüht) - nur nicht unbedingt stringent..
    Wie immer kommt es auf den Anwendungsfall an.

    Andere Lösungen wären:
    1. Steuerzeichen verwenden
    2. Stringfunktionen auf einen vordefinierten String aus "home", "cursordown"s und "cursorright"s anwenden
    3. Basicerweiterung verwenden
    (..)

    ad 1.: das wird rasch unübersichtlich. Ich habe das auch mal so gemacht. Beim Lesen und verstehen des Listings muss man die Steuerzeichen genau abzählen, um zu verstehen wo die Schreibmarke rauskommt und was wo in der BS-Maske landet. Macht fremde Programme sehr schwer zu verstehen.
    ad 2.: das ist Mikes Beispiel. Den Nachteil dass man sich die Stringmanipulation mit gehäufter Garbage Collection erkauft erwähnte ich schon; die Zeilen werden auch etwas aufgebläht. Edit: mit dem dazuzufügenden CHR$ relativiert sich das zugegebenermaßen -
    ad 3.: dagegen ist nichts einzuwenden ausser, dass wer kauft sich eine Kuh, wenn er nur ein Glas Milch braucht?



    Unfair wäre es freilich, wenn kritisiert wird dass ausgerechnet der USR-Vektor verbogen wird in BIFs Lösung.


    Leute, das ist der einzige Vektor, dessen ganze Daseinsberechtigung von den BASIC-Erfindern dazu bestimmt wurde verbogen zu werden! Wenn einer das macht, ist das so vorgesehen!

  • Ganz richtig, jetzt wird das nächste Faß aufgemacht.


    Stephan, bei der Programmgröße hier spielt die Garbage Collection absolut keine Rolle!


    Leute, das ist der einzige Vektor, dessen ganze Daseinsberechtigung von den BASIC-Erfindern dazu bestimmt wurde verbogen zu werden! Wenn einer das macht, ist das so vorgesehen!

    Ich weiß bessere Anwendungsfälle für die USR()-Funktion als Brechstangen in den Interpreter hineinzuwerfen.


    ad 3.: dagegen ist nichts einzuwenden ausser, dass wer kauft sich eine Kuh, wenn er nur ein Glas Milch braucht?

    Wenn's dir auf eine augenfällige Syntax ankommt, geht's auch mit einem Kalb (sprich: mit einer Minimal-BASIC-Erweiterung, die wirklich auch nur den PRINT-AT-Befehl mitbringt). Deren Nutzen ist dann aber auch nur auf diesen Anwendungsfall beschränkt.

  • Der fncp(x.y)-Krempel ist eh nur "schön", solange die Koordinaten hardcodiert sind. Sobald da was variabel gehandhabt werden soll, muss man die Teilkoordinaten aufwändig zusammenstückeln und aufaddieren. Bäh.

  • Mike:
    Auch nicht schlecht.


    Aber grundsätzlich hat man mit dieser Technik die Möglichkeit Befehle in Basic zu erstellen.


    In Form von


    :sysfncp(1.2): oder a=fncp(1.2)


    Intern besteht ja jeder Basic-Befehl aus einem Sys-Vektor, einem Befehlsnamen und einem Parameterteil.
    Und auf die im Beispiel gezeigte Art und Weise kann man das in Basic nachbilden.


    Die technische Funktionsweise ist einfacher als erwartet:


    -Man verbiegt den USR-Vektor einfach auf die Interpreterschleife.
    -Nach dem Abarbeiten von USR in der Formel befindet sich der Rechner dann im Interpretermodus.
    -Das if0then bewirkt dann das den Sprung in die nächste Zeile.
    -Hier wird dann ganz normal der Programmcode abgearbeitet.
    -mit sysre erfolgt dann das zurücksetzen in den Formelmodus und der Rücksprung zur Aufrufadresse.


    Schönen Gruß.

  • Hier noch mal ein leicht modifizierte Programm:
    Mit dem man die sys-Sprungadresse in die Formel reinnimmt.


    • 0 :rem----cursor setzen mit funktion
    • 1 :poke785,228:poke786,167:re=59736:gosub10:rem--vektor+definition
    • 2 :sysfncs(5.10)::print"p1": :rem---beispiel 1
    • 5 :end
    • 9 :
    • 10 :deffn cs(a)=58640+.*usr(.);if.then:return
    • 11 :poke214,a:poke211,(a-int(a))*101:sysre


    Schönen Gruß.