SHIFTs - Bits schieben, schubsen und rotieren ("Programmieren" lernen)

  • Die Zahlen in heutigen CPUs kann man zwar als Dezimalzahl ausgeben und als Hexadezimalzahl schreiben, letztlich steht im RAM oder den Registern jedoch immer eine binäre Zahl.

    Je nach dem "Level", von dem aus man die ganze Maschine betrachtet, tangiert einen das mal mehr, mal weniger. Beim Benutzen eines Smartphones etwa dürften die wenigsten Menschen dabei die Vorstellung haben, daß sie da gerade aktiv und selbstbestimmt Einzelbits durch das Gerät bewegen. Auf Assemblerebene ist man diesem Phänomen deutlich näher.


    (Es ist übrigens auch gar nicht gesagt, daß das so bleiben muß. Man denke mal an Quantencomputer oder biologisch basierte Rechenmaschinen; und was die ScienceFiction sonst noch hergibt. Bei Flash Speichern (SSDs) ist so ein NICHT-Bit Zustand ja bereits heute Realität - neuerdings (2018) auch schon mit QuadLevelCells.)



    Bits haben zwei wesentliche Grundeigenschaften. Sie sind binär - haben also nur 2 Zustände. Und sie sind, zumindest, wenn man sie irgendwie sinnvoll benutzen will, geordnet - besser: sie sind angeordnet.


    (eine dritte Grundeigenschaft wäre evtl., daß sie quasi nicht-stofflich sind, und auch wenn sie tatsächlich "da" sind, etwa in einem Rechner, sie nie wirklich definiert "vorhanden" sind; etwa weil die Elektronenwolke, durch die sie gebildet werden, sich permanent verändert; sie sind quasi eine Idee, wie beim alten Platon, oder soetwas wie eine Note, die nur manifest wird, wenn sie jemand spielt (ob der/die den Ton trifft, ist dann eine weitere Frage)) und wo es nur recht Sinn macht, wenn sie zusammen mit anderen in einer (An-)Ordnung daherkommt)



    Die Anordnung von Bits geschieht - wie bei den anderen Zahlensystemen und ihren Grundelementen auch - indem man ihnen Stellen mit einem Stellenwert zuweist.


    Diese Anordnung ist nicht fix und wird bei fast allen Operationen damit irgendwie verändert.

    (Ausnahmen wie ODER Verknüpfung mit #$00 sind natürlich vorhanden, unter Erhalt des Musters.)


    Eine besonders spannende Frage bei Bits ist die nach ihrem aktuellen Zustand: "0" oder "1".

    Dies hat vor allem etwas damit zu tun, daß Bits, aufgrund ihrer "binären" Eigenschaft, oft als eine Art virtueller Umschalter benutzt werden (können). Etwa direkt gekoppelt an einen Ausgang, der dann "real" etwas bewirkt, z.B. ein Relais schaltet (Stichwort GPIO) oder auch als Repräsentation eines anderen virtuellen Objektes, wie z.B. einem Pixel in einer Schwarz-Weiß Grafik (Stichwort Bitplane).


    Die dabei auftretende Frage ist dann: Welchen Wert hat gerade das Element (Bit) in einer bestimmten Stelle einer Binärzahl ?

    Diese Frage kann man so natürlich auch z.B. bei Dezimalzahlen stellen, allerdings gibt es dort wesentlich weniger Anwendungen dafür, da selten einmal eine Einzelstelle einer Dezimalzahl mit ihrem Wert für etwas anderes eingesetzt wird, als einfach nur Stelle in dieser Zahl zu sein.

    Das ist bei Binärzahlen anders - weshalb sie sowohl als Muster als auch als Zahl gesehen werden müssen.


    Nun sind bei den Logik-Befehlen schon Möglichkeiten aufgetaucht, diese Frage zu beantworten. Etwa ließe sich die Frage nach einem gesetzten Bit dadurch entscheiden, daß man mit einem Wert UND verknüpft, der lediglich dieses Einzelbit gesetzt hat - und alle anderen Bits auf "0". Nach der UND Verknüpfung sind auf jeden Fall zumindest alle Bits außer dem interessierenden gelöscht, da sie ja durch die Vorgabe der "0"en im Verknüpfungswert bei der UND Verknüpfung zu "0" werden müssen. Einzig das "interessierende Bit" bleibt potentiell erhalten. War es nun selbst nicht gesetzt ("0"), wird es auch durch UND Verknüpfung nicht seinen Wert ändern. War es dagegen gesetzt, wird es erhalten bleiben. Am Schluß kann man unterscheiden - hat die Gesamtzahl einen Wert anders als Null, war das Bit anfangs gesetzt. Ist das Ergebnis gleich Null, war das Einzelbit nicht gesetzt. (Und genau soetwas macht beim 6502 der BIT Befehl.)

    (Übrigens: das funktionert also völlig unabhängig von den anderen Bits in der Zahl, denn diese sind ja bereits durch die UND Verknüpfung mit je einer "0" alle "stummgeschaltet". Man spricht dabei auch von: Ausmaskieren, sie wurden ausmaskiert.)


    Nun ist UND zwar eine Operation, die mit allen CPUs machbar sein dürfte, es hat sich aber gezeigt, daß man durch Einbau einer relativ kleinen Zusatzhardware innerhalb der CPU den gleichen Effekt mit kleinerem Aufwand erreichen kann; zzgl. weiterer nützlicher Effekte. Diese Zusatzlogik ist der: SHIFTER.


    Er macht auch genau, was sein Name sagt - er verschiebt, bringt an einen anderen Ort, verlagert, räumt um. Und zwar: Bits. Im allgemeinen macht er das auch nur in geordneter Form: er verschiebt die Bits in ihren Stellen um genau eine Position, und zwar alle Bits einer Binärzahl. Und auch alle in die gleiche Richtung, entweder zu der "höherwertigen" - das ist: nach links, oder zu den "niederwertigen" Positionen - nach rechts.


    Aus einer

    %00001010 wird so durch SHIFTen nach links eine

    %0001010x und eine

    %x0000101 durch SHIFTen nach rechts


    Man sieht: Sämtliche Werte haben genau um eine Stelle nach links bzw. rechts gewechselt.

    Dadurch wird aber natürlich an dem jeweilig entgegengesetzten Ende eine Stelle "undefiniert" - das "x".


    Die Befehle, mit denen man den SHIFTER steuert, unterscheiden sich nun

    a.) durch die Richtung und

    b.) dadurch, mit welchem neuen Inhalt bzw. wie sie die verwaiste Stelle "x" füllen.


    Interessant ist nun natürlich, daß man ja das "hinausgeschobene" Bit nicht sofort verwerfen muß, sondern dieses auch kurz zwischenspeichern und betrachten/auswerten kann. Damit ergibt sich nun auch die oben angesprochene Variante den Wert von Bits an bestimmten Stellen zu bestimmen - man verschiebt/shiftet das Bitmuster einfach solange bis das gewünschte Bit "aus der binären Zahl herausfällt".


    Und der Gesamtzahlenwert ? Das wurde schon im Zahlensysteme-Thread gezeigt: Der verdoppelt sich natürlich, wenn man nach links schiebt (wobei hierbei auch das höchste Bit besonders beachtet werden muß) oder er halbiert sich, wenn nach rechts geschoben wird.

  • =6502=


    Hier gibt es vier wesentliche Befehle für die Benutzung des SHIFTERs.

    Davon "schieben" zwei nach links, die anderen beiden nach rechts. Zwei füllen die entstandene Stelle "x" ganz einfach mit einer "0" auf, die beiden anderen legen dort den Wert des Carry-Flags hinein. (Und zwar den, von vor(!) dem Verschieben.)
    Allen gemeinsam ist, daß sie das "hinausgeschobene" Bit im Carry-Flag ablegen.


    Da dieses Flag benutzt wird, sind auch die Befehle CLC (clear-carry-flag) und SEC (set-carry-flag) wichtige Bedienelemente. Noch wichtiger sind die Auswertebefehle für die Abfrage des Carry-Flags - also die bedingten Sprünge BCC (branch-on-carry-clear) und BCS (branch-on-carry-set).


    Die Befehle sind

    ASL und LSR - welche die Stelle "x" mit "0" füllen sowie

    ROL und ROR - welche dort den Carry-Flag Inhalt von "vor dem Befehl" ablegen


    Der letzte Buchstabe jeden Befehles zeigt die Richtung der Schiebeoperation an: L = Left = Links , R = Right = Rechts.


    RO - steht für "Rotieren" und die beiden ersten Befehle haben das S für "SHIFT" sogar mittig im Namen.

  • =6502=


    ROR - ROtieren nach Rechts - ROtate Right


    Dieser Befehl schiebt zunächst auch nur. Und zwar die höherwertigen Bits um eine Position in Richtung der niederwertigen. Genau um eine Stelle. Das Bit-0 wird dadurch aus der Zahl hinausgelöst - und wird zunächst intern aufgehoben, bis nämlich das Carry-Flag seinen Inhalt in die freigewordene Stelle des Bit-7 übergeben hat. Erst danach wird das ehemalige Bit-0 ins Carry-Flag geschrieben.


    Man kann sich das auch anders vorstellen:

    Carry-Flag und das Byte, was geshiftet werden soll, bilden kurzfristig zusammen eine 9-Bit lange Zahl. Diese wird nun um eine Position nach rechts geshiftet, das dabei überzählige, "hinausfallende" Bit-0 wird anschließend im Carry-Flag abgelegt.


    %(Carry-Flag)00110011 ---> nach dem SHIFTen : %C0011001 + %1 (das alte Bit-0) ---> Carry-Flag neu setzen auf ehemaliges-Bit-0


    Rotieren heißt das darum, weil man durch diese Verbindung über das Carry-Flag, welches als neues Bit-7 gesetzt wird und anschließend das Bit-0 aufnimmt, einen Kreis schließt, der es ermöglicht, das Byte einmal/mehrmals zu "rotieren".

    Aber: Man rotiert nicht das Byte (also 8-Bit), sondern die Kombination Carry-Flag+Byte (also 9-Bit)!


    Für eine echte 8-Bit Rotation des Bytes allein, muß man sich was einfallen lassen (s.u.).


    ROR kann man mit normalen 16-Bit Adressen und direkt in der Zeropage benutzen. Man kann auch mit dem X-Register indizieren. Am Günstigsten ist es aber einfach nur "ROR" zu benutzen, das wirkt direkt auf den Akku und ist dadurch ca. 3mal schneller als die anderen Varianten.



    Das Beispiel zeigt genau das oben beschriebene Auslesen der Bits aus einer Binärzahl. Dazu werden die ersten 16 Zeichen auf dem Bildschirm bitweise "zerlegt" und ein "*" für ein gesetztes, ein "-" für ein nicht-gesetztes Bit dargestellt.

    Das Zeichen ganz links in der obersten Bildschirmzeile wird als oberstes Bitmuster angezeigt.




    SUBZEILEX2D0D1 wird bei $6050 benötigt.


    Die ROR Anweisung greift direkt auf die zu bearbeitende Speicherstelle zu (X indiziert) und schiebt bei jedem Durchgang das Bit-0 in das Carry-Flag, welches mit BCC und BCS ausgewertet wird.

    In Adresse $D0/$D1 wird durch die Subroutine die Anfangsadresse der X-ten Zeile abgelegt, wodurch in diese mittels Indirektion ($D0),Y und mit dem Y-Register als Index, die Bits in der "herausgelösten" Reihenfolge von hinten nach vorn aufgeschrieben werden können. Bit-7 steht also vorn, Bit-0 hinten in der Zeile - gezeichnet wird beginnend mit dem originalen Bit-0 (welches ja auch als allererstes herausrotiert wird). Damit der Buchstabe in Zeile 1 wieder seine ursprüngliche Form annimt, wird noch ein ROR nach der inneren Schleife angeschlossen - Merke: die Rotation ist erst mit 9 Bit vollständig abgeschlossen !


    Bitte verschiedene Zeichen in Zeile 1 probieren oder die Werte auch mal direkt in den Speicher schreiben

    >0C00 FF FE 85 95 12 ...

    Anschließend jeweils das Programm neu starten.


    Frage: Warum wird das Carry-Flag nie in einen definierten Zustand gebracht - etwa durch CLC oder SEC ganz am Anfang jeder "Runde" und/oder jeder Schleife ?

  • =6502=


    ROL - ROtieren Links - ROtate Left


    Dies ist i.P. das gleiche Kommando wie ROR - nur in die andere Richtung.

    Auch hier wird das Carry-Flag beim Verschieben mit einbezogen, da die Verschiebung aber von rechts-nach-links erfolgt, findet sich der Carry-Flag Inhalt in Bit-0 wieder. Das links "hinausgeschobene", ehemalige Bit-7 wird ganz am Schluß noch in das Carry-Flag geschrieben.


    %00110011(Carry-Flag) ---> SHIFTet zu: %0 (das Bit-7) + %0110011C ---> Carry-Flag wird auf %0 gesetzt (ehemals Bit-7)


    <<<---<<<---<<<---<<<---<<< RICHTUNG des SHIFTs nach Links


    Für die direkte Abfrage von Bits benutzt man daher, wenn die interessierenden Bits in der oberen Hälfte des Bytes liegen (d.h. im oberen Nibble), einen solchen Linksschiebebefehl.

    Für das "mathematische" Shiften, bei dem eine Zahl im Wert verdoppelt werden soll, ist der andere Linksschiebebefehl (ASL) besser geeignet.

    ROL hat aber demgegenüber die Möglichkeit, die Zahl zu erhalten, wenn man alle 9 Bits (inkl. Carry-Flag) einmal durchrotiert.

  • =6502=


    Was macht man nun, wenn es einfach darum geht ein Byte um sich zu rotieren ?

    Also einfach nur 8 Bit am Stück, nicht 9 Bit, wie die Befehle es vorgeben ?


    Es soll also Bit-0 in Bit-1 kommen, Bit-1 in Bit-2 ... und Bit-7 dann die freigewordene Stelle vom Bit-0 einnehmen.


    Man müßte dafür irgendwie das Carry-Flag so setzen, daß es immer gerade den Zustand des gerade aktuellen Bit-7 hat. Wenn man das schafft, dann wäre zwar Bit-7 vor jeder Verschiebung 2mal vorhanden (1x als es selbst und 1x im Carry-Flag), doch das würde das Rotieren ja nicht beeinträchtigen.


    Vorschlag:

    man benutzt den BIT Befehl und testet, damit das Bit-7; je nach Ergebnis setzt man das Carry-Flag oder löscht es; dann führt man eine Rotation (ROL) aus; wenn man das achtmal wiederholt, hat man das Byte einmal komplett "um sich selbst" rotiert.


    --> den Vergleichswert #%10000000 in eine Adresse speichern

    LDA #$80

    STA $D0

    --> dann das zu rotierende Byte in den Akku laden, mit BIT testen, wenn Bit-7 gesetzt war, das Carry-Flag setzen

    LDA $ROTIERBYTE

    CLC

    BIT $D0

    BEQ (über den nächsten Befehl hinweg)

    SEC

    ROL


    ... sollte klappen. Analoges Vorgehen in die andere Richtung, nur da mit anderem Vergleichswert (%00000001).



    Es geht aber auch viel einfacher:


    --> laden, komplettes Byte vergleichen, direkt rotieren

    LDA $ROTIERBYTE

    CMP #$80

    ROL


    Das funktioniert, weil der CMP Befehl ja sowieso das Carry-Flag setzt, wenn der Wert >= dem Vergleichswert ist.

    CMP #$80 setzt also das Carry-Flag für alle Akkuwerte >= $80, d.h. alle mit gesetztem Bit-7

    und noch schöner: er löscht das Carry-Flag natürlich auch, wenn im Akku, was kleineres steht.

    Damit kann man nun Bytes "in sich" nach links rotieren. Das CMP muß dazu aber natürlich vor jedem ROL stehen, bei 8mal Verschieben eben auch 8mal CMP.



    Für die andere Richtung gibt es auch was, allerdings ein kleines Stück länger:


    ---> laden, Bit-0 schonmal "probehalber" ins Carry-Flag, nochmal laden, rotieren

    LDA $ROTIERBYTE

    PHA

    LSR

    PLA

    ROR


    Hier wird das Carry-Flag per Schiebebefehl mit dem Wert aus Bit-0 versehen, während der Akkuwert auf dem Stack wartet, bis er nochmal in den Akku kommt und nun die vollständige Rotation stattfindet (mit quasi doppeltem Bit-0).




    Das Beispielprogramm zeigt das "8 Bit Komplett-Rotieren" nach Links




    SUBWAIT2 wird ab $6000 benötigt.

    Die Verzögerung bißchen höher einstellen, z.B.: >6007 4F


    Die CMP/ROL Kombination taucht zweimal auf, bei $500F innerhalb einer Schleife und bei $5022 außerhalb davon.

    In der Schleife wird ein Wert, dessen aktueller Zustand auch oben-links immer angezeigt wird, einmal komplett rotiert - alle 8 Bit werden dabei auch ausgewertet und in Bildschirmzeile 2 angezeigt. Nach dieser "Anzeigeschleife" wird noch ein weiteres Mal eine einzelne solche 8-Bit Rotation angeschlossen. Dann wird gewartet und von vorn begonnen. Dadurch ist das Byte nach jedem Anzeigen noch einmal um eine Stelle weitergerückt und dies wird dann in der Folgerunde auch so angezeigt.


    Ganz am Anfang wird bei $C6 die aktuell gedrückte Taste abgefragt (der Wert hat dabei nichts mit dem Tastenaufdruck zu tun) - es soll nur ermöglichen, das Bitmuster auf einfache Art zu ändern. Wer auf einem VC20, PET o.ä. ist, der muß evtl. eine entsprechende Speicherstelle nachschauen, oder den Anfangsteil einfach weglassen und bei $500A ins Programm einsteigen sowie den JMP am Ende anpassen.

  • =6502=


    LSR - Logical Shift Right


    Dies ist nun ein Schiebebefehl, der das freiwerdende "x" nicht mit dem Carry-Flag füllt. Stattdessen kommt an dessen Platz der Zahlenwert "0". Das Bit-0, welches aber beim Schieben nach Rechts aus der Binärzahl "freigesetzt" wird, findet seinen Platz im Carry-Flag.


    %0 + %10101101 ---> nach dem SHIFTen: %01010110 + %1 (das Bit-0, nun jedoch im Carry-Flag)


    Man sieht also: das Musters wandert nach Rechts. Der Binärzahlenwert wird dadurch kleiner. Da es Binärzahlen sind, wo jede Stelle doppelt soviel Stellenwert hat, wie die nächstkleinere, bedeutet ein Schieben in Richtung der nächstkleineren eine Halbierung des "Wertes" jeden gesetzten Bits. Die Summe aller Bits ist dann natürlich auch nur die Hälfte derer vor dem SHIFTen. Der Zahlenwert wird daher halbiert durch ein LSR.


    Weiterhin sieht man: das Muster wandert nach Rechts!

    Hatten wir eben schon, aber das soll noch mal der explizite Hinweis darauf sein, daß ein Bitmuster überhaupt nicht für eine Zahl stehen muß, sondern für irgendwas anderes "kodieren" kann.


    Und: Man sieht, daß die oberste Stelle, das Bit-7, von der neuen "0" überschrieben wird. Da diese aber bei den negativen Zahlen die Markierung dafür war, daß es sich um eine "Minuszahl" handelt, wird es wohl mit dem Halbieren von solchen Zahlen, nicht so einfach sein. Eine halbierte Minuszahl würde ja hier (Bit-7 = 0) automatisch zu einer positiven werden, und das klappt zumindest im normalen Zahlenuniversum eher selten.


    Und: Das Carry-Flag läßt sich natürlich auch wieder auswerten, so wie bei den ROtieren Befehlen (mit BCC/BCS).



    Das Beispiel benutzt LSR ... und das gleich dreimal.

    Eine Zahl die 3mal hintereinander halbiert wird, wird also durch 2^3 geteilt, das ist 8. Der Wert wird also "geachtelt".
    Man möge sich hier nocheinmal die Stellenwerte bei Binärzahlen ins Gedächtnis rufen: 1 - 2 - 4 - 8 - 16 - 32 - 64 - 128.

    Da sieht man auch schön, was mit dem Wert geschieht, wenn man drei Stellen weiterspringt.




    SUBWAIT2 wird bei $6000 benötigt
    SUBZEILEX2D0D1 wird bei $6050 benötigt.


    In $D8 bis $DF sollten ein paar unterschiedliche Werte stehen (nach Belieben)

    >00D8 00 08 20 40 FF F0 C0 80


    Es gibt zwei Schleifen. Die erste bis $5005 erhöht jeden dieser Werte um +1.

    Die zweite ab $500D durchläuft diese Werteliste und lädt jeden Wert in den Akku ($5014) wo er halbiert - und halbiert - und nochmal halbiert wird. Dieses ermittelte Achtel hat nun eine angenehme Größe, um auf dem Bildschirm Platz zu finden, weshalb er, auf der ab $500F ermittelten Bildschirmzeile (Adresse des Anfangs in $D0), als Balken ausgegeben wird - und indirekt über den Adresswert in ($D0) zzgl. dem Y-Register, in dem der geachtelte Wert steht und so die Balkenlänge angibt.


    Das JSR $D88B ruft eine Systemroutine auf, die den Bildschirm löscht (auf PET, VC20, C64 etc. bitte anpassen).

    (Die könnte man auch durch eine eigene ersetzen, ala LDX#$FF : STore #$20 nach Bildschirmadresse,X : DEX : NEXT)


    Frage: Wie bekommt man das "Flimmern" weg ? Muß man immer den kompletten Balken zeichnen ?


    Wozu das Ganze ?

    Man kann so sehr schön in einem Register einen Wert in der Bitbreite der Register "führen". Hier also 8-Bit mit Werten von 0-255. Für die Darstellung oder auch Anwendung reduziert man die Größe der Zahl auf ein "angenehmes" bzw. praktikables Maß.

    Andersherum ist es so auch möglich, Werte in feinerer Auflösung abzulegen, auch wenn man es evtl. noch gar nicht benötigt - es eröffnet dann aber für zukünftige Änderungen die Möglichkeit, sehr einfach die höhere Zahlenauflösung zu benutzen. Etwa wenn man nur einen Bildschirm mit 512x512 Pixeln hat, aber intern schon mit 4096 Pixeln die Werte rechnet, kann man in einfachster Form "anpassen", wenn das größere Display verfügbar wird. (Das Thema ist ja gerade mit 4K wieder mal relativ aktuell.)

  • =6502=


    Eine "Musterdemonstration" soll hier noch angeschlossen werden, v.a. weil es nun schonmal probiert und eingetippt ist, aber auch, weil es eine schöne Ergänzung zu ROL ist, es zudem auch ein schönes "Kopfkino" sein kann, wenn man sich darauf einläßt.




    SUBWAIT2 wird an $6000 benötigt.

    Höhere Werte mit z.B. >6007 DF einstellen.


    Der Akku bekommt den Wert "AA", das ist %10101010.

    Nun wird hier das Bit-7 direkt angeschaut und in Abhängigkeit davon das Carry-Flag gesetzt oder nicht. Dann wird rotiert, nach links, und anschließend wird das Carry-Flag ausgwertet und zwei Farbwerte verteilt, die bei $FF19 die Farbe des Bildschirmrandes setzen. Dabei folgt die Farbe nun also dem Muster, das man am Anfang in den Akku geschrieben hat.


    Frage: Was muß man ändern, wenn man das auf ROR umbauen will ?


    Frage: Ist die Auswertung des Carry-Flags sinnvoll, im Sinne von unabdingbar nötig ?


    Frage: Welche Zahlenwerte finden sich während des Programmablaufs alle im Akku ?? Welche Muster haben diese ?


    Die Kommandos PHP und PLP sichern und laden die Prozessorregister auf dem Stack - hier damit das Negativ-Flag für die nächste Runde erhalten bleibt und nicht in der Subroutine "gekillt" wird.


    Wer die Adresse $FF19 zum Farbwechsel nicht "hat", der schreibt evtl. den Wert einfach in den Bildschirm als Zeichen; das tut's auch.

  • =6502=


    ASL - Arithmetic Shift Left


    Der SHIFTER kann also, wie schon beim LSR, auch ohne einen Eingabewert aus dem Carry-Flag verwendet werden. Dabei wird die vakante Stelle "x" mit einer Null aufgefüllt, sozusagen. Das aus der Binärzahl hinausgeschobene Bit landet aber wieder im Carry-Flag. Da hier nun nach Links verschoben wird, ist das natürlich das Bit-7.


    %11001001 + %0 ---> nach dem SHIFTen: %1 (das Bit-7) + %10010010 ---> das Carry-Flag wird "eins" gesetzt



    Was sieht man nun hier im Unterschied zu den anderen Befehlen oben ?


    Die Schieberichtung ist andersherum als bei LSR. Und die "fixe" Null kommt von rechts zur Binärzahl dazu.

    Das Bit-7 landet im Carry und kann dort getestet werden.


    Der Zahlenwert der Binärzahl wird zunehmen. In Anlehnung an das oben Gesagte verdoppelt er sich natürlich.

    Aber ACHTUNG: Wenn das Bit-7 bereits "1" gesetzt ist, und z.B. auf Bit-6 eine %0 folgt, kann man gut sehen, daß das nur gilt, wenn man Zahlen kleiner-gleich der Hälfte des maximalen Zahlenwerts betrachtet:

    Bit-7=1, Bit-6=0: %10110011 -> nach dem SHIFTen: %1 + %01100110 (was eine kleinere Zahl ist, da Bit-7 nun "0").

    Dafür berücksichtigt man auf jeden Fall besser das Carry-Bit mit, als Bit-8, wo der Übertrag landet.


    An dieser Stelle wird dann evtl. auch klar, warum es zwei Befehle für Linksschieben gibt.

    Prinzipiell ist (vermutlich auch historisch) der einfachere der, welcher die 0 einfügt (hier ASL).

    Aber über die Kombination mit dem anderen, der das Carry-Flag mitbenutzt, lassen sich dann Befehlsketten mit Übertragsweitergabe auf die nächsthöhere Zahl bilden. Etwa:


    LDA #$99

    ASL

    PHA

    LDA #$00

    ROL

    PHA


    oder noch einfacher für einen 16-Bit Wert in zwei Adressen $D0/$D1 (Low-Byte/High-Byte)


    ASL $D0

    ROL $D1


    (funktioniert also an sich genauso, wie beim ADC Befehl, den man direkt an einen vorherigen ADC Addierbefehl anschließt (16-Bit Addition) )



    Und etwas weiteres Bemerkenswertes macht der ASL Befehl: Er verdoppelt auch Zahlen, die nach dem Zweierkomplement gebildete negative Zahlen sind ! Deshalb auch heißt er "arithmetischer" SHIFT.

    Das funktioniert natürlich bei 8-Bit nur relativ eingeschränkt, da ja der Zahlenbereich der negativen Zahlen schon von vorneherein nur die Hälfte der 8-Bit umfaßt (alle wo das Bit-7 "1" ist). Und da es ja um Verdopplung geht, kann man innerhalb des Bytes, soetwas sinnvoll nur für die Zahlen -64 bis zur 0 anwenden. Für alle unterhalb der -64 muß man dann das Carry-Flag wieder berücksichtigen.

  • =6502=


    Wie man gesehen hat, sind die Carry-Flags hier ziemlich involviert.


    Damit man ein wenig mehr Einfluß darauf nehmen kann, was das Flag gerade beiinhaltet, gibt es direkte Befehle zum Setzen und Löschen des Flags. Diese sind schon öfters mal benutzt und v.a. beim Addieren/Subtrahieren erwähnt worden.


    SEC - setzt das Flag

    CLC - löscht das Flag


    Damit würde dann ein CLC und anschließendes ROL quasi einem ASL entsprechen können.

    Ein SEC und ein ROL dagegen verdoppelt die Zahl und addiert noch eine 1 dazu. Hilfreich etwa beim Abzählen von Dingen (Zählen von Null vs. Zählen ab Eins).


    Man kann übrigens fast alle Flags mit solchen Befehlen beeinflussen.