die Transferbefehle ("Programmieren" lernen)

  • Oft finden sich in Registern irgendwelche Werte, die man evtl. für weitere Berechungen noch benötigt, trotzdem möchte man erstmal mit diesem belegten Register vorher noch was anderes rechnen/machen.

    Bei manchen CPUs kann das sogar notwendig sein, weil man nur ein einziges echtes Rechenregister zur Verfügung hat (6502). Bei anderen gibt es zwar evtl. genug Register, aber man benutzt aus irgendwelchen Konventionen oder Zwängen (Subroutine gibt ein Ergebnis in einem bestimmten Register an den Aufrufer zurück) heraus immer die gleichen Register.

    In so einem Fall könnte man den aktuellen Inhalt einfach im Speicher irgendwo ablegen und später wieder dort herausholen.


    Es gibt aber evtl. auch eine einfachere Lösung:

    Man transferiert den Inhalt in ein anderes, gerade freies, nicht genutztes Register.


    Nun ist das auf einer CPU mit 3 Registern relativ sinnfrei, könnte man denken, aber auch da wird es oft gemacht, etwa weil bestimmte Operationen nur mit einem von den 3 Registern ausführbar sind. Richtig spannend wird soetwas natürlich, wenn viele Register vorhanden sind, da man dann auch bei aufwendigen Berechnungen eigentlich alle Zwischenergebnisse direkt in der CPU behalten kann.


    Der große Vorteil liegt dabei v.a. in der Geschwindigkeit - ein Register in ein anderes Register zu übergeben kann eine CPU recht fix erledigen, das gleiche Register im RAM zu speichern dauert vergleichweise LAAAAANGE !



    Bei den ARMs gibt es dafür den wahrscheinlich einleuchtendsten Befehl: MOV R1 R2 , also R1=R2

    oder auch MOV R1,R14 oder MOV R14,R1 usf.


    Beim 6502 beginnt diese Befehlsgruppe immer mit "T" für Transfer und dann folgt, was man wohin schreibt.

    Dort haben die Transferbefehle auch noch die besondere Bewandtnis, daß man nur den Akku bequem (!) auf dem Stapelspeicher ablegen kann, weil es für diesen einen netten Schreib- und Lesebefehl für diesen Stack gibt. Deshalb schreibt man dann erst den Akku, weg, transferiert anschließend das X-Register XR in den Akku, schreibt wieder den Akku weg, transferiert auch noch YR in den Akku, und schreibt nochmal den Akku weg; womit man dann alle drei Register "gerettet" hätte.


    Kurz: Transferbefehle sind schnell ! Können richtig benutzt ein prima Tool sein !

    (Sie machen allerdings, von solchen Standards wie der Sache mit dem "Register retten" abgesehen, manchmal den Quellcode etwas unübersichtlich, weil ständig was getauscht wird.)

    -- 1982 gab es keinen Raspberry Pi , aber Pi und Raspberries

  • =6502=


    Beim 6502 gibt es nur 3 Register, den Akku und XR sowie YR.


    Nun könnte man denken, daß man einfach jedes auf jedes transferieren kann. Leider geht das nicht. XR und YR lassen sich nicht aufeinander kopieren.


    Es geht nur


    Akku in XR : TAX und

    XR in Akku : TXA


    sowie


    Akku in YR : TAY und

    YR in Akku : TYA



    Für einen Rundtausch muß man sich also was einfallen lassen.


    Alle diese beeinflussen auch das Zero-Flag: wird also eine Null transferiert bekommt die CPU das mit und "hisst" die "Z" Flagge. (Außerdem wird auch das Negativ-Flag gesetzt, wenn eine Zahl mit einem (virtuellen) Minuszeichen transferiert wurde.)



    Und es gibt noch die beiden


    TXS und

    TSX


    Damit kann man den sogenannten Stapelzeiger schreiben(!) (aus dem X-Register) oder lesen (ins X-Register).

    Dieser zeigt an, wo gerade im Stapelspeicher was hineingeschrieben/gelesen werden kann. TXS ist die einzige Möglichkeit ihn zu beeinflussen ! (wobei man den eigentlich normalerweise wohl besser in Frieden "sein Ding" machen läßt)



    Wichtig : Alle Register, die als Quelle fungieren, von denen also der Wert kommt, behalten diesen Wert unverändert bei. Man hat also eine echte Kopie im Zielregister (oder dem Stapelzeiger) und auch noch das Original.

    -- 1982 gab es keinen Raspberry Pi , aber Pi und Raspberries

  • =6502=


    Hier nochmal ein Demo, was sich wieder an

    .5000 INC $FF19

    .5003 JMP $5000

    anlehnt.



    Da wieder nach Subroutine $6000 in die Warteschleife gesprungen wird, sollte die auch dort vorhanden sein.


    Nachdem sie mit

    S "SUBWAIT",8,6000,6020

    abgespeichert worden war, kann man sie nun mit


    L "SUBWAIT",8


    an genau die gleiche Stelle wieder laden.


    Wem das zu viel Aufwand ist, der möchte bitte den JMP $5006 Befehl in Adresse $500E schreiben. Er verpaßt dann aber den schönen langsamen Wechel.

    -- 1982 gab es keinen Raspberry Pi , aber Pi und Raspberries

  • =6502=


    Und damit auch im Bildschirm noch was passiert, hier der Scroller erneut, diesmal aber in der "richtigen" Richtung, d.h. von rechts nach links.




    Was ist anders als bei der links-rechts Variante ???


    Am besten zeichnet man sich das mal auf !


    Es wird immer ein Wert auf das vorhergehende Zeichen im Bildschirmspeicher kopiert. Dieses würde dadurch natürlich gelöscht/überschrieben und darum muß es VORHER gerettet und zwischengespeichert werden. Hier wird das YR zum Retten benutzt und nach $D0 geschrieben. $D0 ist beim C16 eine frei benutzbare Zeropage Adresse - es handelt sich also um eine Zeropage Adressierung. Von dort holt sich der Akku den Wert und schreibt ihn in den Bildschirm, aber(!): das, was da in den Akku geladen wird ist immer der Wert aus dem vorigen Durchlauf der Schleife, denn das YR mit dem geretteten Wert wird erst am Schleifenende abgespeichert.


    Wieder wichtig - es muß Text in Zeile 1 auf dem Bildschirm stehen. Und die Subroutine Warteschleife muß bei $6000 vorhanden sein (s.o.).


    Frage - was hat das mit Transferbefehlen zu tun ? , oder anders: kann man das evtl. mit Transferbefehlen eleganter lösen ?


    (bitte vorm Nachsehen selber probieren, ob man eine Lösung findet !)

    -- 1982 gab es keinen Raspberry Pi , aber Pi und Raspberries

    Einmal editiert, zuletzt von ThoralfAsmussen ()

  • Tja ... hier nun noch die Lösung zur Aufgabe oben


    Man kann natürlich die Zwischenspeicherung nach $D0 ersetzen, indem man den Wert einfach im Y-Register behält und in der nächsten Schleifenrunde direkt mit TYA in den Akku transferiert.



    Im allerersten Durchlauf wird auf die gleiche Weise das Zeichen der ersten Spalte gerettet (LDY $0C00) und dann sofort ganz nach hinten ans Zeilenende geschrieben.

    -- 1982 gab es keinen Raspberry Pi , aber Pi und Raspberries

  • =6502=


    Und weil nebenan in einem lesenswerten Z80 Beitrag das Thema Stack schon angemahnt wurde, hier mal noch DIE Anwendung für die Transferbefehle schlechthin.


    [Ach ja, das Thema selbst ist bereits vorbereitet, vorgedacht;, wer das hier anschaut und sich dann mal die Sub-Routine anschaut, weiß auch wie (ich sag mal $4000 und 6 Bytes). Erstmal MÜSSEN aber noch die Vergleichsbefehle kommen. Und Tabellen fänd ich auch noch gut. Aber vielleicht hat ja sonst jemand Lust, zu erzählen, was ein Stack so macht. Vielleicht ja sogar jemand von den Leuten, die wirklich gerade anfangen wollten mit dem Maschinencode ? Der allgemeine Teil Ist eigentlich nicht so schwierig und Beispiele, Extras und Anwendungen können ja dann noch extra folgen.]


    Es war schonmal erwähnt worden, daß man Register (üblicherweise) auf den Stack retten kann.

    Wie das geschieht steht auch schon kurz hier ganz oben im Text.


    Und so sieht das dann aus:



    Die drei Ladebefehle in der Mitte sind nur da, damit auch tatsächlich was überschrieben wird. Normalerweise würde an deren Stelle die Sub-Routine o.ä. stehen.


    PHA - (push accu) - schreibt den Akku auf den Stack,

    PLA - (pull accu) - holt den Wert von dort wieder hervor (in den Akku)


    Man sieht - auch völlig ohne Plan - was da passiert, daß nämlich

    a.) anfangs alle Register Richtung Akku transferiert werden (TXA,TYA)

    b.) daß im hinteren Teil die Register wieder ausschließlich über den Akku geholt werden (TAY,TAX)

    c.) daß im hinteren Teil die Register in genau umgekehrter Reihenfolge wie am Anfang auftreten




    Und damit man es glaubt, kann man es ausprobieren:

    mit Eingabe von ";" ist es möglich die aktuellen Registerwerte zu ändern, mit "R" zeigt man sie dann nochmal an.

    G6000 startet also: alle Register auf den Stack legen -> 3 unterschiedliche Werte in die Register laden und somit die alten Werte überschreiben -> alle Register wieder vom Stack holen.

    Wenn das "Retten" nicht klappen würde, müßten die Werte aus den Ladebefehlen (FF,80,25) am Ende in den Registern (AC,XR,YR) auftauchen - tun sie aber nicht !


    Einfach mal rumprobieren damit ! z.B. mal die Befehle TAX und TAY vertauschen.

    -- 1982 gab es keinen Raspberry Pi , aber Pi und Raspberries

    Einmal editiert, zuletzt von ThoralfAsmussen ()