Indirekte Adressierung

Es gibt 18 Antworten in diesem Thema, welches 4.067 mal aufgerufen wurde. Der letzte Beitrag (3. September 2018 um 10:08) ist von detlef.

  • Kaum hat man mal zwei Jahre keinen Code angefasst fängt man wieder bei Null an.... ;)

    Ich verzweifle gerade an Folgendem: ich habe eine kleine Routine zum anzeigen von PETSCII. Diese hat die Adresse, wo die PETSCII Daten liegen "hart verdrahtet" -> frame0000.

    Funktioniert auch alles. Ich möchte die Routine aber flexibel halten und unterschiedliche Screens übergeben.
    Also dachte ich irgend sowas wie dieser Aufruf:

    Code
    lda #< frame0000
        sta $80
        lda #> frame0000
        sta $81
        jsr petscii_init


    Und dann im Code die Adresse aus der ZP holen

    Code
    lda ($80)+0*$100,x
        sta $0400+0*$100,x
        lda ($80+1)+25*40+0*$100,x
        sta $d800+0*$100,x

    Aber das ist alles totaler Blödsinn und ich mische Äpfel mit Birnen.
    Kurzum: wie muss meine Routine aussehen, damit ich sie für mehrere Screens nutzen kann?
    Ist Selfmod-Code die Lösung?

    Ich danke Euch.

  • Das sieht schon ziemlich, nunja, wirr aus ;) dieses 25*40+whatever.

    Fassen wir mal kurz zusammen:
    Screen-RAM zeigt (ohne $D011 Tricks) von dem ausgewählten Screen $0000-$03E7 an.
    Also im Falle vom Basic-Startbildschirm $0400-$07E7.
    Selbiges gilt für das Color-RAM. Da dieses ab $D800 liegt, ist es der Bereich $D800-$DBE7.

    Da es aber weitaus einfacher ist, mit 4 pages, also $0400 zu rechnen, kann man doch 4 pages "stumpf" ins Screen-RAM und 4 pages in Color-RAM schreiben.

    Somit könnte man mit einem frameCounter arbeiten, welcher immer den offset für den nächsten frame berechnet.

    Und dann alle Adressen, also Start/Quelle der Framedaten, Ziel Screen-Ram und Ziel Color-RAM auf die Zeropage legen, sodass du diese entsprechend inkrementierst.
    Also indirekte-indexierte Adressierung nutzen anstelle von selbst-modifizierendem code (geht natürlcih auch).

  • Danke rayden für Deine Hilfe.

    Das sieht schon ziemlich, nunja, wirr aus dieses 25*40+whatever.

    Der Aufbau ist logisch aber ich verstehe was Du meinst


    Fassen wir mal kurz zusammen:
    Screen-RAM zeigt (ohne $D011 Tricks) von dem ausgewählten Screen $0000-$03E7 an.
    Also im Falle vom Basic-Startbildschirm $0400-$07E7.
    Selbiges gilt für das Color-RAM. Da dieses ab $D800 liegt, ist es der Bereich $D800-$DBE7.

    Korrekt


    Da es aber weitaus einfacher ist, mit 4 pages, also $0400 zu rechnen, kann man doch 4 pages "stumpf" ins Screen-RAM und 4 pages in Color-RAM schreiben.

    Somit könnte man mit einem frameCounter arbeiten, welcher immer den offset für den nächsten frame berechnet.


    Ja, das könnte ich machen, ich möchte aber u.a. so Sachen machen wie PETSCII auf PETSCII kopieren, auch partiell. Es geht also nicht nur um's umschalten. Ausserdem möchte ich mehr als nur 4 Screens durchschalten.

    Unabhängig vom Deinem sinnvollen Vorschlag möchte ich das Thema indirekte Adressierung anhand dieses Codebeispieles gerne ein für alle mal verstehen. Das blockiert mich jedes mal...

  • Partielles kopieren ist erst der übernächste Schritt, weil das verkompliziert das Vorgehen ein wenig, da Du ja wissen musst, an welche Stelle wieviel bytes neu sollen ;)

    Mal eben runtergeklimpert, ungestet. Es ist Freitag Abend und ein Glas Wein ist anwesend, somit keine Garantie - allerdings hoffe ich, das indirekte-indexierte Adressierung einigermassen verständlich rüber kommt...

    Der Kram setzt voraus, dass alle PETSCII-Screens hintereinander liegen (Screen 1 ab framePetscii0000, Screen 2 ab framePetscii0000 + $0400, Screen 3 ab framePetscii0000 + $0400 * 2, etc.) und selbiges für Color-Ram, welches ab frameColor0000 liegt.
    Vielleicht habe ich da auch noch nen Klopper drin, ich weiss es nicht.

    Der nächste frame wäre dann ein inc frameCounter und anschliessender Aufruf von drawFrameInit.
    Vielleicht habe ich da auch noch nen Klopper dirn, ich weiss es nicht, weil ungetestet.

    Nachtrag:
    Je nachdem auf welcher Raster-Position man die Routine aufruft, sieht man sehr wahrscheinlich, wenn das Schreiben der Daten für den neuen frame stattfindet. Eine Lösung wäre hierfür mit double-buffering (zwei Screens) zu arbeiten. So kann der eine Screen aktualisiert werden, während der andere angezeigt wird. Wenn die Aktualisierung erfolgt ist, wird auf den fertigen Screen umgeschaltet. Glas ist leer...

  • Bitte melde dich an, um diesen Link zu sehen.
    Mal grundsätzlich zur indirekten Addressierung:

    Du schreibst in zwei aufeinanderfolgende Zeropage-Adressen Lo,dann Hi deiner eigenlichen "Zieladresse". Also wenn du Daten von $2000 lesen willst schreibst du in $80 eine $00, in $81 dann $20.

    Mit lda ($80),y (und es muss hier Y benutzt werden!!), liest er nun den Wert aus genau der Adresse, die du in $80 und $81 abgelegt hast PLUS y. Wenn y = 0 ist liest du also den Wert aus genau $2000. Ist y=1 liest du den Wert aus $2001.

    Jetzt kannst du in einer Schleife y durchlaufen lassen und die entsprechenden Daten lesen. Allerdings gibt es noch ein Problem: y kann höchstens 255 sein, also könntest du nur die Daten von $2000 bis $20ff lesen.

    Deshalb kannst du nachdem y einmal durchlaufen wurde einfach das Hi (hier abgelegt in der Zeropage $81) um einen erhöhen und dann von neuem 256 Werte lesen - nämlich mit einem erneuten Durchlauf der Schleife (diesmal für die "neue" Adresse $2100).

    Also ungefähr so:

    Code
    ldy #0
    Loop
    lda ($80),y
    dey
    bne Loop
    inc $81
    ; jetzt ein compare, damit er nicht endlos erhöht. Bei Screendaten braucht man z.B. vier Durchläufe (4x256 = 1024)
    jmp Loop


    So, ich hoffe, es war nicht zu spät für mich... :drunk:

  • Vielen Dank Euch für die tolle Hilfe, obwohl alle Beteiligten schon leicht einem im Tee hatten :D
    Bitte melde dich an, um diesen Link zu sehen. Dein Hinweis, die Adresse in der ZP zu inkrementieren, hat nochmal viel zum Verständnis beigetragen, danke!

    Hier der Code, der nun CHAR RAM und COL RAM anzeigt:

    Ein Problem habe ich aber noch, das habt Ihr sicher gesehen: die vierte Schleife sollte eigentlich nicht bis 255 durchlaufen, sondern kürzer sein, weil alle Daten eingelesen wurden. Da hab ich noch Probleme, das effizient zu lösen, ohne nochmal Extraloops anzufügen. Wie mache ich das besser?

  • Zwei Tipps:
    Zum einen kannst du das Kopieren vom Colorram in die erste Schleife integrieren (und das gleiche y nutzen).
    Zum anderen würde ich Selfmod immer ein eigenes Label gönnen. Also direkt vor dem zu ändernden Befehl ein Label schreiben. So verhinderst du Fehler, wenn du später noch Befehle einfügst und dann dein petsciiloop+4 plötzlich was falsches erhöht.

    Beim letzten Durchlauf musst du dich fragen, wie wichtig dir eingesparte 24 Bytes sind. Die Petscii und Colorram Daten würde ich eh als 1kb Blöcke ablegen, ist aufgeräumter. Wenn du statt Indirekt. Adress. komplett auf Selfmod gehst könntest du y auch einfach viermal nur von 249/250 an runterzählen lassen, was genau 1000 ergibt.

  • Zum anderen würde ich Selfmod immer ein eigenes Label gönnen.

    Selfmod geht eigentlich gar nicht. Irgendwann wills du so eine Routinen mal in ein ROM packen und stehst du ziemlich dumm da.
    Ausserdem gehören Daten und Code aus fundamental-religiösen Gründen immer getrennt. :D

    Der 6502 hat ja gerade die vielen indirekten und indizierten Adressierungsarten, damit man eben keinen selbstmodifizierenden Code schreiben muss.
    Ich würde mir so einen Quatsch gar nicht erst angewöhnen.

    Ich programmiere schon seit 40 Jahren Microcontroller und es gab noch nie eine Notwendigkeit für selbstmodifizierenden Code.

  • Selfmod geht eigentlich gar nicht. Irgendwann wills du so eine Routinen mal in ein ROM packen und stehst du ziemlich dumm da.
    Ausserdem gehören Daten und Code aus fundamental-religiösen Gründen immer getrennt.

    Interessante Sichtweise mit dem ROM... wird bei mir niemals passieren, aber daran hatte ich noch nie gedacht, stimmt!
    Wenn ich kein selfmod mache, dann brauche ich noch mehr freie Adresse in der Zeropage, um auch die Position im Screen und Color RAM zu speichern, oder siehst Du einen anderen Weg?

  • Ich programmiere schon seit 40 Jahren Microcontroller und es gab noch nie eine Notwendigkeit für selbstmodifizierenden Code.

    Allein wie oft ich mit BIT / JMP gearbeitet hab... :rolleyes:

  • Hier der Code mit allen Änderungen (finde das immer gut, den Stand auch wieder zu posten für andere)

  • Interessante Sichtweise mit dem ROM... wird bei mir niemals passieren, aber daran hatte ich noch nie gedacht, stimmt!

    Sag niemals nie. Wenn du mehr Assembler machst, wer weiss ob du nicht auch mal was auf eine Cartridge packen willst.
    Wenn du dann statt indirekter Adressierung nur selbstmodifizierenden Code produziert hast, machst du alles neu. ;)

    Wenn ich kein selfmod mache, dann brauche ich noch mehr freie Adresse in der Zeropage, um auch die Position im Screen und Color RAM zu speichern, oder siehst Du einen anderen Weg?

    Nö, aber einen Mangel an Zero-Page-Adressen hatte ich noch nie. Da gibt es immer genügend Adressen, die während des Programmablaufs nicht genutzt werden.
    Da bin ich aber auf dem C64 nicht so fitt. Ich habe das ja überwiegend auf dem PET gemacht.

    Ich weiß ja nicht wie viel du jetzt schon nutzt, aber ein guter Kandidat sind immer die Fließkomma-Akkus und irgendwelche Tape-Pointer.

    Bitte melde dich an, um diesen Link zu sehen.

    Einmal editiert, zuletzt von detlef (2. September 2018 um 16:21)

  • Allein wie oft ich mit BIT / JMP gearbeitet hab..

    Selbstmodifizierend? Wie sieht das aus mit BIT?

    JMP-Adressen zu modifizieren ist eher eine Verschleierungs- als eine Programmiermethode. Da tun sich dann die Disassembler ziemlich schwer mit.
    Dafür gibt es JMP Indirekt oder man schiebt die Adressen auf den Stack und führt ein RTS aus.

    Es gibt definitiv keine Notwendigkeit für selbstmodifizierenden Code.

  • Doch, Speed. Fast alle Demos brauchen das. Bei dem JMP z.B. geht das schneller als der indirekte.

    Ja, ok. :thumbup:

    Dafür dauert aber das Schreiben des Zeigers wiederum länger. Das muss man sich dann im Einzelfall anschauen.
    Wenn man den Zeiger vor jedem JMP manipuliert, kann es sogar insgesamt langsamer werden. ;)

  • Ich programmiere schon seit 40 Jahren Microcontroller und es gab noch nie eine Notwendigkeit für selbstmodifizierenden Code.

    Mikrocontroller mit Code im Rom kann man nicht vergleichen mit Spieleprogrammierung für den C64 oder AppleII oder... Das sind zwei verschiedene Paar Schuhe.

    Da tun sich dann die Disassembler ziemlich schwer mit.

    Ja und? Man schreibt den Code doch nicht, damit ihn andere mit Disassemblieren angucken.

    wer weiss ob du nicht auch mal was auf eine Cartridge packen willst

    Heutzutage läßt man die Programme aber nicht direkt vom Cartridge laufen, sondern ein Entpacker entpackt das Programm ins Ram. Das Cartridge dient lediglich als Datenträger wie auch Diskette oder Tape. Für den extrem seltenen Fall, daß man wirklich Routinen braucht, die vom Rom aus laufen sollen, kann man das Programm daran anpassen. Aber bei diesen Sonderfällen weiß man auch schon vorher, was man tut. Die Wahrscheinlichkeit, daß ein Programm, welches fürs Ram konzipiert wurde, irgendwann mal in einem Rom laufen soll (Welches überhaupt? Kernal-Rom?) ist nahezu Null.

    Es gibt definitiv keine Notwendigkeit für selbstmodifizierenden Code.

    Mit solch fundamentalistischen Aussagen wäre ich sehr vorsichtig.
    Folgendes reale Beispiel:
    Auf dem AppleII soll die Soundausgabe per Menü gewechselt werden von Speaker (Adresse $c030) auf Kassettenausgang (Adresse $c020) wie z. B. im Spiel "Summer Games". Die genannten Adressen (auch bekannt als "Softswitches") werden in einer Schleife mittels BIT angesprochen, um dadurch die Membran zum Schwingen zu bringen bzw. den Ausgangspegel umzuschalten.
    Üblicherweise wird ein Wechsel nun so gehandhabt, daß im Programm bei allen BIT $c030-Befehlen die Low-Adresse von $30 auf $20 gepatcht werden. Würde man die extrem zeitkritischen Soundroutinen nun so schreiben wollen, daß man mittels indirekter Adressierung auf sie zugreift, benötigt man pro Zugriff 2 Zyklen mehr für den Befehl "STA (zp), y". ("LDA (zp),y" würde nicht gehen, da hierdurch der Akkumulator überschrieben wird.) Hinzukommt, daß das Y-Register verschwendet wird, das man aber als Zähler dringend benötigt. Folglich muß man den Zähler auslagern in eine Zeropage-Adresse und dort dekrementieren, was dann wieder 3 Taktzyklen mehr fordert. Damit dann mehrstimmige Musik hinzubekommen wird schwierig. Noch schlimmer wird es, wenn die Knacks-Aufrufe von $c030 in Malroutinen eingebaut werden, damit die Malschleife gleichzeitig als Frequenz-Verzögerungsschleife verwendet werden kann (z. B. Boulderdash), um dadurch wichtige Zeit zu sparen. Kein Programmierer hat den Fehler gemacht und ideologisch verkündet: "Das muß man jetzt aber unbedingt mittels indirekter Adressierung machen, weil Selbstmodifikation Teufelszeug ist."
    Merke: Selbstmodifikation ist nur dort wirklich schlecht, wo der Prozessor über einen eigenen Instruktionscache verfügt und nicht mitbekommt, daß sich der Code ändert bzw. die Anpassung an die Codeänderung mehr Zeit verschluckt als gewonnen wird. Der 6502 verfügt aber auch nach Jahrzehnten immer noch über keinen Cache. Daher war Selbstmodifikation in der Programmentwicklung auf dem 6502 schon immer fester Bestandteil, um Programme schnell und in einigen Fällen auch übersichtlicher zu gestalten, und so wird es auch weiterhin sein. Daran wird auch Deine Aussage nichts ändern.

    Einmal editiert, zuletzt von M. J. (2. September 2018 um 17:54)

  • Wie ich oben schon schrieb sind das Einzelfälle. Natürlich sind alle Mittel recht, wenn man ein taktgenaues Timing benötigt.
    Tektgenau heisst aber nicht, dass es automatisch immer schneller ist (wie manchmal behauptet wird).

    Und dadurch wird es trotzdem nicht zu einen guten Programmierstil und zu überichtlichem Code führt es schon mal gar nicht - höchstens vielleicht für die, die sich mit indirekter Adressierung nie auseinader gesetzt haben. ;)

    Und laufen Spiele etwa nicht im ROM? Ich kenne da eine ganze Menge. Ausserdem seit ihr oft sehr C64 fixiert. Es gibt ja auch noch den VC20, für den ich gerade anfange, Assemblerprogramme zu schreiben. Da geht in der Basisaustattung fast gar nichts im RAM.

    Bitte melde dich an, um diesen Link zu sehen.

    4 Mal editiert, zuletzt von detlef (3. September 2018 um 09:11)

  • Sollte man dann nicht gleich die Finger von Assembler lassen?

    Nö. Wenn's schnell sein soll, kommt man auf 8-Bittern um Assembler nicht herum. Und wenn's im ROM laufen soll, sowieso nicht.

    Aber ordentlich kommentiert und strukturiert und Tricks und Kniffe auf ein Minimum reduziert (also nur da wo es wirklich notwendig ist) kann man auch in Assembler ganz ordentliche Programme schreiben. Natürlich findet man auch viel Spagetti-Code (weil JSR/RTS ja so langsam und uncool ist... :D ).