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

letzter Beitrag von syshack am

Zusätzlichen Basic Befehl erstellen. Wie?

  • ...und mit etwas Nachdenken und Höflichkeit kann man auch auf triple Postings verzichten und statt dessen editieren.


    Ach, huch, ich rede ja mit BIF.
    Kann ich mir also auch sparen.

  • Das Cursorsetzen ist natürlich nur ein Beispiel für eine einzige Befehlserweiterung.


    Grundsätzlich kann man damit denk ich mal alle gewünschten Befehle oder Formeln nachrüsten, die das Basic V2 nicht bietet.


    Der Fantasie sind da keine Grenzen gelegt, da der Programmcode die mit der Funktion ausgefürht wird beliebig lang sein kann.



    Schönen Gruß.

  • In der Tat kann auch ein einzelnes gesetztes USR "griffiger" sein und lesbarer als ein GOSUB12345, vor allem wenn man die Möglichkeit (1) Parameter zu übergeben einbezieht.


    Bei obigen Beispielen fällt mir auch noch ein, dass die @'Basic-Fan' Methode, ein DEF FN mit einem USR zu verknüpfen, die Möglichkeit eröffnet nicht bloß einen zusätzlichen "Befehl" zu implementieren, sondern eine Befehls-Funktionalität.


    Seiteneffekte sind ja in vielen Programmiersprachen verpönt. Aber in Pascal-Delphi wurden sie bei den Properties ( Eigenschaften) bewusst einbezogen, weil sie Vorteile haben.


    Ich habe mal in Pascal einen Stack über Properties simuliert. Properties sind dort eine Variable, bei deren Zugriff je nach Lesen oder Schreiben unterschiedliche Seiteneffekte bewusst angestoßen werden können. In einem Emulator kann man z.b. auch solche Dinge wie "Lesen des Datenregisters A der CIA löscht zugleich das Interrupt-Flag im IFR" oder "Schreiben ins Timer-Latch High des VIA triggert zugleich Transfer des Low-Latch in den Counter" elegant bewerkstelligen, indem man die Peripheriebausteine und ihre Register als Ansammlung von Properties realisiert.


    Bei einem Stack (ja es ging um einen 6502-Emulator) ist der Seiteneffekt beim Lesen (Pull) das Hochzählen des Stack-Pointers vorher, und beim Schreiben (Push) das Herunterzählen des Pointers nachher.


    Gedanklich könnte man obiges Beispiel mit der Cursor-Positionierung statt auf die Zeropage-Stellen X,Y auch auf einen Software-Stackpointer anwenden.

  • Seiteneffekte sind ja in vielen Programmiersprachen verpönt.

    Meiner Meinung nach zurecht, denn:



    "Lesen des Datenregisters A der CIA löscht zugleich das Interrupt-Flag im IFR"

    ...bei sowas stellen sich mir als Programmierer alle Nackenhaare auf.
    Ein Datenregister lesen sollte exakt eine Sache tun: Die Daten aus dem Register auslesen. Wenn ich ein Flag löschen will, sollte ich das bewusst durchführen. Jede Obfuskation des Codes durch solche Sideeffects ist dem Verständnis und auch der Wartbarkeit des Codes massiv abträglich, und meist ist der Gewinn (in Zyklen oder Zeilen), so er überhaupt existiert, diese erhöhte Fehleranfälligkeit nicht wert.
    BIF ist ein Meister darin Code zu schreiben der völlig verschleiert was/wie er es tut - und oft gehen die Sideeffects seiner Codes weit über das augenscheinlich beabsichtigte hinaus. Das wurde hier schon mehrfach beleuchtet und erläutert, ich werde es daher auch nicht weiter vertiefen.
    Auch dass er da absolut beratungsresistent ist (er scheint es im Gegenteil zu geniessen und sich damit überlegen zu fühlen solche Frankensteincodes zu schaffen) ist mittlerweile selbst dem letzten klar.
    Giess ihm ruhig ein wenig Öl ins Feuer, Stefan, aber Du wirst recht allein dabei sein.


    Guter Code sieht einfach anders aus.

  • Die Eigenschaft ein Flag zu löschen während ein Datenregister gelesen wird, haben die Schöpfer der VIA 6522 - Bausteine von Mos Technologies implementiert (später Commodore Semiconductor Group), u.a. Bill Mensch, dessen Western Design Center die einzigen heute noch erhältlichen 65C02-Prozessoren herstellt.


    Das ist ein völlig offizielles und dabei sinnvolles Hardware-Feature. Und es ist keine "Obfuscation " von Code, es ist ja in Hardware gegossen, sondern hält den Code kurz und übersichtlich.
    (Und wenn Software die Hardware emuliert, muss sie zwangsläufig diese Eigenschaften von VIA / CIA - Hardware nachbilden - daher wirst du Code auch in Vice finden, der diese Doppeleffekte implementiert.)


    Ich möchte hier nur über den Gegenstand des Threadtitels diskutieren - geht das?

  • Ich möchte hier nur über den Gegenstand des Threadtitels diskutieren - geht das?

    Gern, wenn du es selbst dabei belässt :) Ich hab die Pascal-Implementierungen von diversen Emulationen nicht ins Gespräch gebracht und bin sowieso hier raus, weil der Thread durch BIF gekapert wurde.
    Und was wer wo implementiert hat ist für mein Verständnis von gutem Codestil (und auch dem vieler weiter User hier) nur sehr begrenzt relevant.
    Dass man hier in Software mit einer Mechanik etwas nachbilden kann was in Hardware gegossen wurde mag ja interessant sein, es ist dennoch für mein Verständnis unsauber (Zwei Funktionalitäten, zwei Funktionen, getrennt, leicht wartbar, leicht nachzuvollziehen).
    Aber wie schon erwähnt, ich bin raus. Have fun.

  • Seit den biffigen Postings und Stephans literarisch-obskuren Beigaben (ich geb's zu, ich verstehe diese leider nicht), habe ich eigentlich schon den Threadinhalt wieder vergessen....
    :D


    Es wäre vielleicht noch von Vorteil, wenn der Threadersteller sich mal zu Wort meldet, ob hier damit alles beantwortet wurde und man wohl zumachen kann.
    Zumal ja nicht mehr echtes Beef hier erscheint. Vielleicht kann der Threadersteller dann bei Gelegenheit im "Heute so gecodet" Thread seine Lösung präsentieren.

  • ...bei sowas stellen sich mir als Programmierer alle Nackenhaare auf.Ein Datenregister lesen sollte exakt eine Sache tun: Die Daten aus dem Register auslesen. Wenn ich ein Flag löschen will, sollte ich das bewusst durchführen.

    Bei Hardwareregistern sind solche Zusatzeffekte oftmals ein Muss, weil sonst gar keine korrekte Funktion gewährleistet werden kann:
    Zeitpunkt 1: Es kommen neue Daten von außen an, der I/O-Chip liest sie ins Register und setzt sein NEWDATA-Flag (und sagt der CPU ggfs. per Interrupt Bescheid).
    Zeitpunkt 2: Die CPU reagiert auf den Interrupt und sieht das gesetzte NEWDATA-Flag.
    Zeitpunkt 3: Die CPU liest die Daten aus dem Register.
    Zeitpunkt 4: Die CPU löscht das NEWDATA-Flag.


    Jetzt gibt es ein problematisches Zeitfenster zwischen Aktion 3 und Aktion 4: Kommen hier wieder neue Daten an, gehen sie verloren, da die CPU es nie erfährt. Da die CPU nicht beide Dinge gleichzeitig erledigen kann, muss das die Hardware erledigen.


    Genau aus diesem Grund werden z.B. auch beim VIC die Kollisionsregister bereits durch Lesezugriffe automatisch gelöscht.


    ...was dieses Thema mit dem BIF-Quark oder dem Thread zu tun hat? Keine Ahnung. :nixwiss:

  • MacB., ich hab mich wohl missverständlich formuliert: Dass das auf Hardware-Ebene geschieht (und u.U. geschehen muss) ist unbestritten. Mir ging es alleinig um die fragwürdige Umsetzung auf Software-Ebene.

  • In Post #7 und Post #14 dieses Threads habe ich das durchaus kund getan, dass ist das übliche und bewährte Vorgehen.

  • Dafür gibt es diverse Routinen im Kernal:
    GETBYT, CHKCOM, FRMEVL, GETPAR
    (..)

    Etwas unglücklich ist, dass gerade die drei letztgenannten Links "ins Leere" auf noch nicht existierende Seiten führen, ein Anfänger könnte sich dabei etwas verloren vorkommen ;)


    Deshalb hier Versuch einer kurzen Erläuterung anhand des ROM-Listings:


    - CHKCOM prüft nicht nur ob das "nächste Zeichen" (das worauf $7A /7B zeigt) ein Komma ist, es holt auch das "übernächste" Byte in den Akku, indem die berühmte selbstmodifizierende Character-Fetch-Routine des Interpreters per JMP angesprungen wird. Diese prüft das Binär-Byte schonmal vorab auf bestimmte Zeichen (Alpha/numerisch, space, Doppelpunkt) und setzt das Carry entsprechend. Das ist sogar die Hauptfunktion dieser Routine und daher finde ich CHKCOM etwas arg verkürzt als Bezeichnung, es wird eben auch geholt und nicht nur geprüft.



    - GETBYT $B79B: Basic kennt ja keinen eigenen Typ "unsigned Byte parameter", deswegen bedürfte auch dieser Punkt einer Erläuterung. Diese Routine ruft den Formelinterpreter auf; auf den ersten Blick liefert sie wahrscheinlich kein Byte im Akku sondern einen Integer-Wert irgendwo in den FAC-Registern zurück. Es lohnt sich, hier auf das deutsche c64-Wiki umzuschalten und dort wird gesagt:

    Holt Byte-Wert nach X: Liest einen Ausdruck aus dem BASIC-Text und wertet ihn aus; der 1-Byte-Wert wird dann in X und FAC+4 abgelegt.


    Ich vermute mit FAC+4 ist das fünfte Byte nach der Startadresse eines FAC, vermutlich FAC#1 gemeint. Unklarheiten bleiben.


    - GETPAR B7EB: hier sagt das deutsche Wiki: GETADR und GETBYT: Holt eine 16-Bit-Ganzzahl (nach $14/$15) und einen 8-Bit-Wert (nach X) - z.B. Parameter für WAIT und POKE.

  • Etwas unglücklich ist, dass gerade die drei letztgenannten Links "ins Leere" auf noch nicht existierende Seiten führen, ein Anfänger könnte sich dabei etwas verloren vorkommen

    Auch dem geneigten Anfänger sei dazu das Selbststudium an's Herz gelegt. Der Herr Gockel gibt reichhaltig und mannigfaltig Auskunft. Und sogar mein selbst, ein alter Herr, der sogar zu Zeiten sich des Programmierens ermächtigte als der Herr Gockel noch nicht geboren war, konnte die wichtigen Dinge dereinst in Erfahrung bringen, obwohl leicht tumb der Welt verbracht.
    Sprich: Ein bissel Eigeninitiative kann man wohl erwarten und dass ich nicht alle Links bis in die drölfte Instanz durchklicke ob sie denn grade valide sind tut dem Informationsgewinn (Name der Routinen, Suchkontext) keinen Abbruch.


    Es gibt hier in der Wolke reichhaltig Literatur zu diesen Themen, und ein Start sollte problemfrei möglich sein. Der Grundlegende Ansatz ist da und wurde in diesen Werken sicher x-fach durchgehechelt.

  • Nun, der SYSFN-Trick hat zumindet den Vorteil, daß man nicht erst Maschinensprache lernen muß und sofort mit Basic weiterarbeiten kann.


    Hier noch mal ein Demo-Listing für die Erstellung eines Basic-Befehls in Maschinencode mit Hilfe von String-Addition.
    gs$ : gosub/jsr adr
    ly$ : lade y Register
    ga$ : goto/jmp adr



    Bei dem Code wird zuerst der Sting aufgebaut und dann mit der Funktion fnsg(i) die Adresse ermittelt und in sc gespeichert.


    Quellcode

    • 20 :rem--sc$:cursor-setzen:
    • 21 :deffnsg(i)=fre(0)*0+peek(51)+peek(52)*256+i:rem--string-grenze
    • 22 :gs$=" ":ga$=chr$(76):ly$=chr$(164):
    • 25 :sc$=gs$+chr$(235)+chr$(183)+ly$+chr$(20)+ga$+chr$(12)+chr$(229)::sc=fnsg(.)
    • 29 :
    • 30 :sys(sc)10,3:print"hier"



    Schönen Gruß.

  • Der Vorteil ist natürlich nur dann ein Vorteil, wenn man es auch so sieht.


    1. Man benötigt damit zumindest keine Kenntnisse vom Speicher und speichert das Maschinen-Programm ganz normal im String RAM.
    Die Speicherzuweisung erledigt hierbei die String-Verwaltung
    2. Man kann den MC-String ganz normal als String laden oder abspeichern.


    Schönen Gruß.

  • Ich behaupt mal: wer so ein Konstrukt zusammen geklöppel hat, der weiß auch wo ne ecker Speicher frei ist (c000, cass-puffer o.a.).
    Hat auch nur unwesentliche Vorteile in der Laufzeitoder gleich sowas machen:


    1 DATAAAEA20C6FFA000A590D00B20CFFF997002C8C020D0F14CCCFFFF:FORI=1TO52 STEP2:POKEAD+H,DEC(MID$(E$,I,2)):H=H+1:NEXT


    Hat auch 'kleine' :) Vorteile zur Laufzeit und das Programm 'eiert' nicht irgenwo im Speicher rum (Stichwort: relative Adressierung)

  • Eure Lösungen stehen nicht in einem Gegensatz bzw. Konkurrenz, sondern in einem Ergänzungsverhältnis zueinander :)
    wat den eenen sin Ul, is den annern sin Nachtigall (was der eine überhaupt nicht mag, kann für den andern höchst erstrebenswert sein) (zitiert nach Duden)


    ... oder anders: Das Token für die Funktion DEC steht auf dem C16/116/128 zur Verfügung, während die Lösung von Basic-Fan auf den C64 zielt.


    Edit: ich komm momentan zeitlich nicht hinterher um Posting 55 bis ins Detail zu würdigen, bin aber ehrlich überrascht (u fasziniert) was hier möglich ist.

  • Ein BIF'scher Klassiker in der xten Auflage.

    Nun, der SYSFN-Trick hat zumindet den Vorteil, daß man nicht erst Maschinensprache lernen muß und sofort mit Basic weiterarbeiten kann.

    Klar, um dein Gewurstel mit Inhalt zu füllen (Chr$(164) -> LDY $ZP muss man natürlich keine Maschinensprache kennen. Die Opcodes sind per BIFscher Ableitung aus dem USR-Statement ableitbar welches Peek() ersetzt, was ebenfalls nach BIF ja eine Berechnung(!) ist. Auch ist es für den Basicprogrammierer sofort ersichtlich dass dieser tolle Gosubbefehl nur in den ersten 256 Bytes des Speichers - die VOLL ZU SEINER VERFÜGUNG STEHEN- funktioniert. Sowas ist in der Muttermilch mit drin.


    1. Man benötigt damit zumindest keine Kenntnisse vom Speicher und speichert das Maschinen-Programm ganz normal im String RAM.
    Die Speicherzuweisung erledigt hierbei die String-Verwaltung
    2. Man kann den MC-String ganz normal als String laden oder abspeichern.

    Ah, es wird ein wenig nach hinten gerudert, jetzt muss man nur keine Kenntnisse mehr vom Speicher haben. Stimmt, bei einem einzelnen Opcode klappt das problemfrei. Wehe aber wenn jemand mit diesem tollen "Trick" längere CHR$-Orgien verwenden will. Dann muss das *huch* schon relokatibel angelegt sein, weil man halt leider keinen Schimmer haben wird wo der Mist im Speicher landet. Soviel zur Sinnhaftigkeit.
    (Ähnlich wie dem zuerst eingebrachten Beispiel mit dem Cursorsetzen als REAL - das ist nur hübsch solange es hardcodiert ist, sobald man erst mal die Position berechnen muss ist der Klump total hirnbefreit, da das Vorberechnen der REAL grottenlahm ist.)
    Da ist ein Poke 1024+(40*Zeile)+spalte einfach deutlich lesbarer und noch dazu wesentlich flotter. Und wenn es denn ein String sein muss rufe ich die passenden Routinen ohne Usr() auf, oder nehme gleich Assembler- und zwar nicht in Chr$ verpackt, was noch dazu aus Speicherplatzgründen der größte Tinnef überhaupt ist.
    Bifs Lösung, lieber Stephan, steht zu gar nichts in Konkurrenz. Er ist einfach nur die dreckigste und sinnfreiste Variante von allen. Das einzige was er erreicht ist damit dass er frische Fische kriegt. Und vielleicht der ein oder andere Code-Esoteriker philosophische Grundbetrachtungen zur Position des Akkumulators in Konjunktion mit einem Kasten Bier im Weizenfeld bei Mondenschein anstellt.
    Nützlich wird er dadurch leider immer noch nicht.