Mit dem Rechner rechnen

Für diesen Beitrag wurde das CBM prg Studio verwendet.
weitersagen ...
Tweet about this on TwitterShare on FacebookShare on Google+Share on LinkedIn

CBM prg StudioDie Rechenbefehle

Wir haben bereits einige Rechenbefehle kennengelernt, z. B. INC oder DEX, hier geht es jetzt darum beliebige Werte zu addieren oder subtrahieren. Falls noch nicht geschehen, solltet ihr nochmal bei ‚Wie der Rechner rechnet‚ reinschauen. Das dort vermittelte Wissen werde ich hier nicht komplett wiederholen, wir werden dafür genauer betrachten, wie die Flags beeinflusst werden.

Um uns Beispiele anzuschauen, könnten wir nun einen Debugger (z. B. den vom CBM prg Studio) bemühen und unser Programm dort testen. Ich möchte zunächst aber das Programm aus dem letzten Beitrag ‚Nach unten und zurück‚ etwas umbauen.

Vorbereitung

Für unsere Rechenoperationen fügen wir als Erstes die Variablen TEXTADR (Zero-Page-Adresse für den auszugebenen Text) und BINPOS (Spalte in der unsere Ausgabe beginnt) hinzu. Dann brauchen wir noch N1 und N2, in diesen werden wir die Zahlen ablegen, mit denen wir rechnen. Die Variable SCREENPOS könnt ihr löschen.

Jetzt löschen wir alles zwischen

und

und fügen dazwischen folgendes ein:

Soweit nichts Neues für uns. Wir geben zu Beginn unsere (unspektakuläre) Programmaske showmask: aus. Anschließend springen wir zur Ausgabe des Textes für die Rechenoperation, dafür laden wir den Akku mit der gewünschten Text-Nr. und springen zu showoperator:. Um etwas zu rechnen, laden wir N1 in den Akku. ;<Operation> dient wieder als Platzhalter, das ersetzen wir später durch unseren Rechenbefehl und zum Schluß geben wir das Ergebnis über das Unterprogramm result: aus.

Unsere bisherige Textausgabe runtext: könnt ihr komplett löschen. Wir verwenden jetzt diese allgemeingültige Textausgabe-Routine:

Unsere neue Textausgabe soll (wie bisher) den Cursor an die im X– & Y-Register angegebenen Position setzten und einen Text bis zum $00 ausgeben. Jetzt soll aber die Adresse des Textes übergeben werden, damit wir beliebige Texte ausgeben können. Dazu hinterlegen wir vor dem Sprung zu textout: die entsprechende Adresse auf der Zero-Page bei TEXTADR.
Das einzig wirklich Neue ist, dass wir jetzt hinter textcharin:, unseren Text über eine auf der Zero-Page hinterlegte Adresse finden. Wir laden diese mit der Y-nach-indizierten-Adressierung in den Akku. Der Rest sollte eigentlich kein Problem für euch sein.
Zum Schluß findet ihr noch verschiedene Labels mit den Texten für unser erstes Beispiel.

Kommen wir zur Ausgabe unserer Maske.

Die wenigen Kommentare sagen doch eigentlich schon alles, oder? Nach löschen des BS geben wir unsere beiden Zahlen über binaryout: aus. Wie ihr seht können wir in einem Unterprogramm auch in ein anderes springen. Wir müssen natürlich darauf achten, dass das Unter-Unterprogramm nicht unser Unterprogramm beeinflusst, in dem z. B. Register oder Flags geändert werden. Zum Schluß geben wir noch zwei Texte aus, in dem wir die jeweilige Adresse in die Zero-Page nach TEXTADR schreiben und das X– & Y-Register mit der gewünschten Cursor-Position füllen.

Das wars dann auch schon, kommen wir zur Funktion um unseren Operanten auszugeben.

CMP: CoMPare (vergleiche mit Akku)
CMP unmittelbar ($C9, 2B, 2T, NZC)
Mit dem CMP-Befehl vergleichen wir den Akku mit dem angegebenen Wert. Dabei wird vom Akku der Wert des CMPs (also was hinter diesem steht) abgezogen. Es werden nur die Flags gesetzt, es gibt keine Veränderung im Akku oder im Speicher. Wir können mit dem Befehl erkennen, ob der Wert im Akku kleiner, größer oder gleich dem beim CMP angegebenen Wert (oder dem an der Adresse s. u.) ist.
Nehmen wir einfach mal an im Akku sei #$20 gespeichert, dann werden die betroffenen Flags bei unterschiedlichen ‚CMP-Werten‚ wiefolgt gesetzt.

Wir prüfen direkt hinter  showoperator:  mit CMP #$01 , ob im Akku eine #$01 liegt.

Wenn im Akku keine #$01 liegt, springen wir zur nächsten Prüfung showoperatornext_1: (hier ergänzen wir später, z. Zt. verlassen wir einfach das Unterprogramm). Haben wir aber eine #$01 im Akku, dann laden wir den Text für die Addition textadd: und springen zur Ausgabe showoperatortextout:. Dort laden wir die Cursorposition ins X– und Y-Register und springen für die Textausgabe ins Unterprogrtamm textout:. Danach wird unser Unterprogramm direkt verlassen.

Kommen wir zum letzten Unterprogramm, die Ausgabe des Ergebnisses.

PHP: PusH Processor-Status to stack (Statusregister (SR) auf den Stack legen)
PHP implizit ($08, 1B, 3T, <keine>)
Neben dem uns bereits bekannten Befehl PHA um den Akku auf den Stack zu legen, gibt es auch die Möglichkeit die Flags auf dem Stack zu speichern. Das ist wichtig für den Fall, dass man zwischen dem Setzen und Auswerten der Flags noch etwas Anderes erledigen möchte.
Wir springen direkt nach unserer Operation zu result:, um das Ergebnis, das ja im Akku steht, anzuzeigen. Da wir uns aber auch die Flags ausgeben möchten, müssen wir diese erstmal auf dem Stack zwischen parken.

Dann füllen wir das X– und Y-Register mit unserer Cursorposition und geben das Ergebnis aus dem Akku über binaryout: aus. Jetzt wird es Zeit unsere Flags anzuzeigen. Da unsere Ausgaberoutine für Binärzahlen die Zahl im Akku erwartet, holen wir die Flags mit PLA vom Stack in den Akku. Wie ihr seht ist es egal, wie die Daten auf dem Stack gelandet sind, der jeweilige Befehl holt sich die Daten einfach. Dann noch die Cursoposition festlegen und nochmal zu binaryout: springen.

Und hier unser komplettes Programm, bevor wir den ersten Operator einfügen.

Startet das Programm und ihr solltet folgendes sehen:

Achtung! Das Ergebnis stimmt nicht, da noch nicht 'gerechnet' wurde!!!
Achtung! Das Ergebnis stimmt nicht, da noch nicht ‚gerechnet‚ wurde!!!

Wir sehen die 1. und 2. Zahl untereinander. Vor der zweiten steht unser Operator (hier +). Unter der Zeile mit dem Gleichzeichen steht unser Ergebnis. Beachtet dass noch nicht gerechnet wurde und wir daher nur die erste Zahl als Ergebnis sehen, die ja  im Akku steht!
Weiter rechts sehen wir unsere Flags. Wir können dort kontrollieren wie sich unsere Operation auf die Flags ausgewirkt hat. Für uns sind jetzt erstmal die Flags NVZC interessant.

Dann wollen wir mal rechnen: Vorher aber nochmal der Hinweis, dass ‚Wie der Rechner rechet‚ immens wichtig fürs Verständnis der nächsten Zeilen ist! Dort wurde bereits erklärt was genau vorgeht und wie die Flags verändert werden. Hier schauen wir uns das nur nochmal an Beispielen an.

Ersetzt jetzt bitte ;<Operation>  mit

ADC: ADd with Carry (addiere mit Carry-Flag)
ADC unmittelbar ($69, 2B, 2T, NVZC)
Wir haben mit dem ADC-Befehl die Möglichkeit eine beliebige Zahl zum Akku zu addieren. Das Ergebnis landet dann wiederum im Akku und es werden die Flags NVZC verändert.

Die genauen Auswirkungen schauen wir uns jetzt mal im Beispiel an.
Wenn ihr das Programm auf einem frisch eingeschaltetem Rechner startet, dann wird %00000001 + %00000001 gerechnet und wir kommen auf %00000010. Die für uns wichtigen Flags NVZC bleiben auf 0.

Warum habe ich jetzt so auf den frisch eingeschalteten Rechner hingewiesen?
Dafür gibt es zwei Gründe:
Der erste ist, dass ADC nicht nur das Carry-Flag setzt, sondern es auch mit addiert. Wir haben bisher mit CLC das C-Flag gelöscht um die Cursorposition zu setzen. Daher hat unsere Addition hier das gewünschte Ergebnis geliefert. Was passiert wenn das Carry-Flag gesetzt ist, schauen wir und jetzt mal an, in dem wir vor  adc #N2  die folgende Zeile einfügen.

Damit setzen wir das Carry-Flag auf 1. Startet das Programm und jetzt kommt ‚plötzlich‚ %00000011 heraus! Wir sehen also, dass das Carry-Flag mit addiert wurde. Da wir lieber ganz sicher gehen wollen sollten wir vor einer Addition immer das Carry-Flag löschen. Ändert doch bitte sec  in clc , um ab jetzt immer für ein gelöschtes C-Flag vor der Addition zu sorgen.
Der zweite Grund ist das Decimal-Flag. Schauen wir uns das doch auch nochmal an. Wir ändern N1 dafür in %00001001 und starten das Programm. Wir erhalten %00001010, was wir auch erwartet haben. Fügt jetzt hinter clc   die nächste Zeile ein.

SED: SEt Decimal-Flag (das Dezimal-Flag setzen)
SED implizit ($F8, 1B, 2T, D)
Aktivieren des BCDinfoInfoBinary Coded Digit-Modes mit dem SED-Befehl. Im BCD-Modus werden Dezimalzahlen in jeweils einem Nibble kodiert, d. h. je Nibble sind statt bisher 0-F nur noch die Zahlen 0-9 kodiert. Wir verschwenden da natürlich Speicherplatz. Umso größer die Zahlen sind, desto mehr Speicher ‚verlieren‚ wir. Aber beim Umgang mit Dezimalzahlen, kann der BCD-Mode eine Erleichterung sein.
Startet jetzt noch mal das Programm, als Ergebnis wird uns %00010000 ausgeworfen, natürlich ist auch das D-Flag gesetzt. Nach den Ausführungen von eben sollte euch das Ergebnis jetzt klar sein. Wir erhalten nach %00001001 + %00000001 wieder dezimal 10, nur dass diesmal das Ergebnis im BCD-Format vorliegt. Im ersten Nibble steht die 1 im zweiten die 0.
Wie ‚übel‚ ein fälschlich aktivierter BCD-Modus sein kann seht ihr, sobald ihr den sed -Befehl löscht und hinter  ;*** Start des Assemblerprogrammes  wieder einfügt. Wenn ihr direkt auf dem C64 oder im Emulator entwickelt, speichert unbedingt vor dem Start des Programms!!
Es ist daher eine gute Idee beim Programmbeginn dafür zu sorgen, dass der BCD-Modus deaktiviert ist und ihn nur bei Bedarf zu aktivieren. Ersetzt den eben kopierten  sed -Befehl bitte mit:

CLD: CLear Decimal-Flag (das Dezimal-Flag löschen)
CLD implizit ($D8, 1B, 2T, D)
Um den BCD-Modus zu deaktiveren, verwenden wir den CLD-Befehl.

Überprüft durch einen Start bitte, dass wieder %00001010 als Ergebnis angezeigt wird und das D-Flag gelöscht ist. Machen wir weiter uns schauen uns an, wann welche Flags gesetzt werden.

Ändert N1 in %01110000 und N2 in %00010000. Durch diese Addition werden gleich zwei Flags verändert, wie ihr nach einem Start seht. Das Ergebnis %10000000 ist bekanntlich negativ (Bit-7 ist gesetzt), daher steht auch das N-Flag auf 1. Außerdem hat ein unerwarteter Vorzeichenwechsel stattgefunden (wir addieren schließlich zwei positive Zahlen), deshalb wurde auch noch das Overflow-Flag gesetzt.

Wenn wir jetzt in N1 mal %11110000 hinterlegen und das Programm wieder starten, sehen wir, dass wieder zwei Flags verändert wurden. Als erstes fällt auf, dass als Ergbnis %00000000 heraus kommt, daher ist das Zero-Flag gesetzt. Aber warum kommt 0 raus? Das Problem ist, dass das Ergbnis nicht mehr in 8-Bit passt. Wenn dieser Umstand eintritt, kommt es zu einem Übertrag und das C-Flag wird gesetzt. Genau das ist hier passiert.

Wie ihr zu Beginn gesehen habt, addiert ADC auch das Carry-Flag. Mit diesem Wissen könnt ihr euch evtl. vorstellen, wie man eine 16-, 24- oder 64-Bit Addition realisieren kann. Man löscht das Carry-Flag vor der ersten Addition und addiert dann vom niedrigsten Byte zum höchsten die beiden Zahlen inkl. C-Flag.

Das in ein Programm zu überführen, überlasse ich zunächst euch. Um den Rahmen dieses Beitrags nicht zu sprengen verschiebe ich das auf später, nach Abschluß des Assembler Tutorials werden wir darauf zurückkommen.

Zieh ab! Äh, abziehen ist unser nächstes Thema.

Lasst uns erstmal unser Programm anpassen, so dass auch das Minuszeichen ausgegeben wird.

Sucht die Stelle an der hinter textadd: das Pluszeichen zufinden ist und fügt dort einen Block fürs Minuszeichen ein (wo genau ist eigentlich egal, aber alle Texte gesammelt zu verwalten ist evtl. hilfreich).

Damit dieser Text ausgegeben werden kann müssen wir nun das Unterprogramm showoperator:  anpassen. Fügt zwischen showoperatornext_1:  und dem  rts  die nächsten Zeilen ein (diese beiden Stellen sind unten markiert).

Wenn  cmp #$01  oben nicht zutrifft, dann landen wir bei  showoperatornext_1:  und können direkt wieder den Akku mit cmp #$02  prüfen, da dieser ja durch CMP nicht verändert wird!

Jetzt müssen wir noch lda #$01                ;#$01 = Addition  in

ändern und schon wird unser Minsuzeichen angezeigt.

Fangen wir wieder klein an und setzen unsere Variablen N1 und N2 beide auf %00000001.
Ersetzen wir jetzt adc #N2  durch den Befehl für die Subtraktion:

SBC: SUbtract with Carry (subtrahiere mit Carry-Flag)
SBC unmittelbar ($E9, 2B, 2T, NVZC)
Wir haben hier das Gegenstück zum ADC. Daher verhält sich SBC auch sehr ähnlich. Es wird vom Akku der angebenen Wert (oder der an der angegebenen Adresse) abgezogen. Dabei werden auch wieder die Flags NVZC verändert. Natürlich muss beim SBC ebenfalls darauf geachtet werden, ob der BCD-Modus gewünscht ist.

Starten wir doch einfach mal das Programm und ziehen von %00000001 nochmal %00000001 ab. Ok, es kommt %11111111 heraus, daher ist auch das N-Flag gesetzt. Hat ja wunderbar geklappt. Was? Das muss doch Null sein, oder?
Wie erwähnt ist SBC sehr ähnlich zum ADC, somit hat auch hier das Carry-Flag eine besondere Bedeutung. Beim SBC ist die Verwendung des C-Flags etwas komplizierter wir merken uns einfach, dass beim SBC Carry auf 1 gesetzt werden muss und beim ADC auf 0! Ändern wir also clc  in sec  und erfreuen uns daran, dass nun (nach einem Programmstart) das korrekte Ergebnis %00000000 angezeigt wird und deshalb auch das Zero-Flag gesetzt wurde. Um einen Overflow zu bekommen könnt ihr jetzt z. B. von %10000000 einfach mal %01111111 abziehen.

Auch hier lassen sich durch eine wiederholte Subtraktion 16-Bit oder größere Zahlen verarbeiten.

So traurig es ist, aber der 6510 bietet keine Befehle für die Multiplikation bzw. Division. Diese könnt ihr z. B. durch eine wiederholte Addition oder Subtraktion nachbilden. Das werden wir auch nach Abschluß des Assembler Tutorials machen.
Aber gibt es wirklich überhaupt keine Multiplikations- und Divisionsbefehle, fragt ihr euch jetzt? Wer aufgepasst hat und die bisher gelernten Befehle revuepassieren lässt, dem fällt evtl. auf, dass wir sogar schon die Division verwendet haben. Es gibt zwei Befehle, die eine Multiplikation / Division mit 2 ermöglichen. Na, kommt ihr drauf? Richtig! ASL und LSR. Beim verschieben der Bits nach links verdoppelt sich der Wert, nach rechts wird er halbiert (durchs Carry-Flag können wir sogar erkennen, ob die ‚Division‚ glatt auf geht).


Und schon wieder die Liste der weiteren Adressierungsarten für die neuen Befehl.

Wir haben ja mit PHP das Statusregister (SR) auf den Stack gelegt, da wir den Wert dann aber in den Akku zurückgeholt haben, fehlt uns noch das Gegenstück zum PHP.

PLP: PuLl Processor-Status from Stack (SR vom Stack holen)
PLP implizit ($28, 1B, 4T, ALLE)
Mit PLP setzen wir also alle Flags im Statusregister (SR) mit dem Inhalt des Bytes vom Stack. Natürlich bleibt Bit-5 auch jetzt immer auf 1, aber die anderen Flags werden verändert. Wir müssen also vorsichtig mit dem Befehl umgehen. Sonst aktivieren wir versehentlich den BCD-Modus oder sperren Interrupts.


Da ADC und SBC sich sehr ähnlich sind, behandel ich beide gleichzeitig.

ADC absolut ($6D, 3B, 4T, NVZC)
Addiert zum Akku den Wert des Bytes an der angegebenen absoluten Adresse.

SBC absolut ($ED, 3B, 4T, NVZC)
Subtrahiert das Byte an der angegebenen absoluten Adresse vom Akku.

ADC absolut X-indiziert ($7D, 3B, 4-5T, NVZC)
Addiert zum Akku den Wert des Bytes das an der angegebenen absoluten Adresse plus X-Register liegt. Beim Überschreiten der Page-Grenze wird ein Extra-Takzyklus benötigt.

SBC absolut X-indiziert ($FD, 3B, 4-5T, NVZC)
Subtrahiert das Byte an der angegebenen absoluten Adresse plus X-Register vom Akku. Ein zusätzlicher Taktzyklus wird beim Überscheiten der Page-Grenze fällig.

ADC absolut Y-indiziert ($79, 3B, 4-5T, NVZC)
Addiert zum Akku den Wert des Bytes das an der angegebenen absoluten Adresse plus Y-Register liegt. Beim Überschreiten der Page-Grenze wird ein Extra-Takzyklus benötigt.

SBC absolut Y-indiziert ($F9, 3B, 4-5T, NVZC)
Subtrahiert das Byte an der angegebenen absoluten Adresse plus Y-Register vom Akku. Ein zusätzlicher Taktzyklus wird beim Überscheiten der Page-Grenze fällig.

ADC Zero-Page ($65, 2B, 3T, NVZC)
Addiert zum Akku den Wert des Bytes das an der angegebenen Zero-Page-Adresse liegt.

SBC Zero-Page ($E5, 2B, 3T, NVZC)
Subtrahiert das Byte an der angegebenen Zero-Page-Adresse vom Akku.

ADC Zero-Page X-indiziert ($75, 2B, 4T, NVZC)
Addiert zum Akku den Wert des Bytes das an der angegebenen Zero-Page-Adresse liegt.

SBC Zero-Page X-indiziert ($F5, 2B, 4T, NVZC)
Subtrahiert das Byte an der angegebenen Zero-Page-Adresse vom Akku.

ADC indirekt X-indiziert ($61, 2B, 6T, NVZC)
Addiert zum Akku den Wert des Bytes, das an der Adresse liegt, die sich an der angegebenen Zero-Page-Adresse plus X-Register befindet.

SBC indirekt X-indiziert ($E1, 2B, 6T, NVZC)
Subtrahiert das Byte an der Adresse, die sich an der angegebenen Zero-Page-Adresse plus X-Register befindet, vom Akku.

ADC indirekt Y-nach-indiziert ($71, 2B, 5-6T, NVZC)
Addiert zum Akku den Wert des Bytes, das an der Adresse plus Y-Register liegt, die sich an der angegebenen Zero-Page-Adresse befindet. Bei Überschreitung der Page-Grenze wird ein weiterer Taktzyklus benötigt.

SBC indirekt Y-nach-indiziert ($F1, 2B, 5-6T, NVZC)
Subtrahiert das Byte an der Adresse plus Y-Register, wobei sich die Adresse an der angegebenen Zero-Page-Adresse befindet, vom Akku. Bei einer Page-Grenzen Überschreitung wird ein Taktzyklus mehr benötigt.

Endlich! Das waren eine ganze Menge Kombinationsmöglichkeiten. Aber wir haben jetzt sämtliche Rechenbefehle des 6510 gesehen.


Wir haben mittlerweile die Adressierungsarten oft genug im Detail kennengelernt, daher liste ich ab jetzt nur noch kurz die Befehle auf. Zum späteren Nachschlagen möchte ich euch auf die Mnemonics verweisen.

Kommen wir zu den CMP-Befehlen. Neben dem Vergleich mit dem Akku (CMP) gibt es auch die Möglichkeit mit dem X– (CPX) und Y-Register (CPY) Vergleiche anzustellen.

CPX: ComPare X-Register (vergleiche mit X-Register)
CPX unmittelbar ($E0, 2B, 2T, NZC)
Wie der CMP-Befehl, nur dass hier der Vergleich mit dem X-Register stattfindet.

CPY: ComPare Y-Register (vergleiche mit Y-Register)
CPY unmittelbar ($C0, 2B, 2T, NZC)
Wie CPX, nur mit dem Y-Register.

CMP absolut ($CD, 3B, 4T, NZC)

CPX absolut ($EC, 3B, 4T, NZC)

CPY absolut ($CC, 3B, 4T, NZC)

CMP absolut X-indiziert ($DD, 3B, 4-5T, NZC)

CMP absolut Y-indiziert ($D9, 3B, 4-5T, NZC)

CMP Zero-Page ($C5, 2B, 3T, NZC)

CPX Zero-Page ($E4, 2B, 3T, NZC)

CPY Zero-Page ($C4, 2B, 3T, NZC)

CMP Zero-Page X-indiziert ($D5, 2B, 4T, NZC)

CMP indirekt X-indiziert ($C1, 2B, 6T, NZC)

CMP indirekt Y-nach-indiziert ($D1, 2B, 5-6T, NZC)

So, damit haben wir nach den Rechenbefehlen auch alle Vergleichsbefehle des 6510 kennengelernt.


Für den Moment lassen wir es erstmal gut sein. Weiterführende Rechenoperationen (große Zahlen, Multiplikation, Division und Kommazahlen) sind ein Thema für einen späteren Beitrag.

Wir kümmern uns als nächstes um die booleschen Befehle.


Schrott!!Naja...Geht so...Ganz gut...SUPER! (9 Bewertungen | Ø 5,00 von 5 | 100,00%)

Loading...


<<< zurück | weiter >>>

weitersagen ...
Tweet about this on TwitterShare on FacebookShare on Google+Share on LinkedIn

Schreibe einen Kommentar


Beachtet bitte, dass ich eure Kommentare erst manuell freigegeben muß, bevor sie auf der Seite erscheinen! Da ich nicht pausenlos am Rechner sitze, kann es schon mal etwas dauern, bis ein Kommentar für alle sichtbar ist.

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Protected by WP Anti Spam