Hallo Besucher, der Thread wurde 7,5k mal aufgerufen und enthält 53 Antworten

letzter Beitrag von Mike am

Viele Sprünge usw - wie handhabt ihr das?

  • Servus,


    hin und wieder gibt es ja den Fall, dass man eine Variable auf zig verschiedene Werte pruefen muss, und darauf basierend dann verzweigen muss; z.B. wenn der Nutzer eine Taste drueckt und man diese auswertet. Also sowas hier (nur exemplarisch):


    Code
    1. lda key
    2. cmp #'A'
    3. beq .hat_a_gedrueckt
    4. cmp #'B'
    5. beq .hat_b_gedrueckt
    6. cmp #'C'
    7. beq .hat_c_gedrueckt
    8. ...


    Nun kennt ihr bestimmt alle das Problem, dass die Sprungadressen irgendwann schnell zu weit weg liegen und er nicht mehr direkt hinspringen kann. Also wird es dann schnell sowas:



    Sprich, man springt eigentlich zweimal, und man schreibt auch noch viel "unnoetigen" Code. Wie sehr wuerde ich mir manchmal wuenschen, es gaebe auch ein BEQ mit einer absoluten Adresse...


    Gibt es hierfuer eine elegantere Loesung? Klar, wenn die Werte linear sind (z.B. von 0-20), dann koennte man die Sprungadressen alle in eine Tabelle schreiben und per Index zugreifen. Aber auch das ist nur in manchen Situationen "schoener" als das obige Konstrukt. Und bei Dingen wie Tastencodes, die eben nicht linear sind, waere das schon gar nicht moeglich. Ist das da oben wirklich der einzige sinnvolle Weg?

  • Du kannst natürlich neben der Sprungtabelle noch eine zweite Tabelle für die Vergleichswerte halten; dann kannst du eine generische Funktion für das Anspringen verwenden.
    Generisch kostet halt auch immer etwas Performance und Speicher. Es ist ein ewiges Abwägen :)

  • Wenn die Sprünge so einigermaßen "hintereinander" liegen und man nicht im ROM/Modul ist, benutze ich gerne Selfmod.
    Das hat den Vorteil, dass man bei der Rasterzeit immer gleich bleiben kann. Unbenutzte Bereiche fülle ich mit dem .Nothing auf.
    Worst-Case wären natürlich 2x256 Werte für die Tables. Hier habe ich die aber z.B. mit dem LSR schon verkleinert.

  • Ich mach mir für sowas gern eine kombinierte Tabelle mit Vergleichswert und Sprungziel, und mach dann einen indirekten JMP:


  • Die Einzelabfragen würde ich nur bei zwei oder drei verschiedenen Tasten nehmen, sonst immer Tabellen:

    Diese "saubere" Variante liefe auch aus dem ROM, in der Praxis nehme ich statt dem indirekten Jump eine Selbstmodifikation.

  • Könnte es so auch gehen?

    In einem konkreten Fall mit ca. 2 Dutzend wild gewürfelten Tastenabfragen war ich mir auch nicht zu schade, die "THEN"-Fälle praktisch zu in-linen. Solange die Fall-Routinen klein genug sind und immer sauber aus dem "switch" herausspringen geht das so:

    Code
    1. .switch
    2. CMP #A:BNE switch_00
    3. <Hier code für A>
    4. RTS
    5. .switch_00
    6. CMP #B:BNE switch_01
    7. <Hier code für B>
    8. RTS
    9. .switch_01
    10. [...]

    Der switch wird mit JSR aus der Hauptschleife angesprungen. Das kostet also 5 Bytes pro Fall (CMP/BNE/RTS) - selbst bei einer Tabelle mit Sprungzielen und Suchroutine braucht's für jeden Fall 4 Bytes (Suchbyte/Adresse/RTS) ... hängt also vom Einzelfall ab, was kompakter ist.

  • Ich nehme ACME, da kann ich per Makro schreiben:


    +beq ganz_weit


    und der Assembler ersetzt das Makro dann durch ein


    bne +
    jmp ganz_weit
    +


    Ja, zugegebenerweise nichts neues, aber dennoch lesbarer.

  • Wenn die Sprünge so einigermaßen "hintereinander" liegen und man nicht im ROM/Modul ist, benutze ich gerne Selfmod.

    Selbstmodifizierender Code? Das tut man nicht - das ist ganz ganz B Ö S E. :sonicht:
    Außer man will den Code bewusst verschleiern (z.B. bei Kopierschutzfunktionen).

  • Mal aus anderer Richtung betrachtet:


    Kommt es nur hier und da mal im Programm mit ein paar Werten vor, oder wird es ein großes Projekt mit vielen Dutzend Menüpunkten und Untermenüs, wo sich auch schnell mal ein bisschen mehr Code lohnt?
    Dann kann es sich wegen der Übersichtlichkeit und auch wegen des Platzes lohnen, das alles von einem Unterprogramm erledigen zu lassen und alle Parameter direkt hinter dem Aufruf abzulegen. Mit ein bisschen Stack-Manipulation kann man dann auch wieder hinter dieses Daten zurückspringen.


    Aus einem alten Quelltext (keine Ahnung mehr, warum ich die Parameter ausgerechnet so angelegt habe):

  • Vielen Dank, sind auf jeden Fall ein paar interessante Ansaetze dabei, die man je nach Situation nutzen kann.


    @detlef, selbstmodifizierenden Code verwende ich eigentlich staendig. In der Regel fuer Schleifenzaehler oder fuer Adressen die hochgezaehlt werden. Ist in vielen Faellen einfach die simpelste Variante, und boese finde ich das auch nicht. Es wird ja auch nicht wirklich "der Code" komplett veraendert, sondern lediglich ein Parameter eines Befehls, das ist jetzt nicht unbedingt Hexerei.

  • @detlef, selbstmodifizierenden Code verwende ich eigentlich staendig. In der Regel fuer Schleifenzaehler oder fuer Adressen die hochgezaehlt werden. Ist in vielen Faellen einfach die simpelste Variante, und boese finde ich das auch nicht. Es wird ja auch nicht wirklich "der Code" komplett veraendert, sondern lediglich ein Parameter eines Befehls, das ist jetzt nicht unbedingt Hexerei.


    bin ich auch kein Freund von, vorallem weil das moderne Systeme schon gar nicht mehr zulassen, und auch bei einigen Architekturen, wie z. B. AVR geht sowas nicht, daher sollte man sich das jetzt auch gar nicht mehr angewöhnen, finde ich.


    Aber wie immer, jeder wir er meint ;)

  • Das hat ja nix mit "Angewöhnen" zu tun. Auf einem System wie dem C64 ist das nunmal eine der simpelsten Methoden, etwas zu erreichen, was man ansonsten nur weitaus umständlicher zustande bekommt. Das hat nix mit anderen Systemen oder Prozessoren zu tun. Deinen Satz "aber jeder wie er meint" müsste eher lauten "auf jedem System das entsprechende Vorgehen/Werkzeug".