6. OP-Code-Referenz

 

In diesem Kapitel habe ich sämtliche OP-Codes des 6510 zusammengestellt, inklusive der entsprechenden Namen und Aufrufparameter. Hier können Sie immer wieder nachschauen, wenn Sie doch einmal einen Befehl vergessen haben. Sehr lehrreich ist auch der Link https://c64os.com/post/6502instructions, den Sie unbedingt besuchen sollten, wenn Sie gerade erst in die Assembler-Programmierung mit dem C64 anfangen. Ich selbst habe die OP-Code-Referenz (zumindest die legalen Codes) jedoch dem folgenden Buch entnommen:

 

Alles über den Commodore 64

Commodore Programmierhandbuch

Commodore GmbH, Lyoner Str. 38, D-6000 Frankfurt a.M.

ISBN-10 3-89133-000-6

 

Leider bekommt man das hier angegebene Buch nur noch antiquarisch z.B. über ZVAB.

Eine Sache sollten Sie aber noch wissen, bevor Sie dieses Nachschlagekapitel benutzen: Der 6510-Prozessor ist sehr einfach gestrickt, und setzt seine Befehle aus einfachen Mikrocodes zusammen. Das bedeutet, dass z.B. der LDA-Befehl aus Unterbefehlen besteht, die z.B. zunächst auf den Speicher zugreifen, um anschließend den zuvor gelesenen Wert an das entsprechende Ziel weiterzugeben. Dieses Ziel (aber auch die Quelle) wird ausgewählt, indem bestimmte Steuerleitungen aktiviert werden. Welche Steuerleitungen dies sind, verbirgt sich teilweise im OP-Code. Da der 6510-Prozessor damals noch keine weitreichenden Sicherheitskonzepte bot, wurden viele OP-Code-Nummern in den Handbüchern als ungültig bzw. nicht benutzbar gekennzeichnet. Dies ist aber so nicht richtig, denn es gibt mit den Illegalen OP-Codes durchaus auch Kombinationen, die funktionieren, und mit denen sogar mehrere Operationen parallel ausgeführt werden können. Ich habe die Illegalen OP-Codes vorerst hier nicht ausgeführt, weil ich noch einige Experimente dahingehend machen möchte, welche Codes wirklich auf einem Original-C64 funktionieren. Leider unterstützt auch der alte Hypra-Ass-Assembler keine Illegalen OP-Codes, aber ich arbeite bereits an einer Lösung.

Eine Sache muss ich jedoch noch anmerken: OP-Code-Referenzen gibt es viele, aber mir selbst hat die Strukturierungsvariante auf https://www.masswerk.at/6502/6502_instruction_set.html am besten gefallen, die ich hier auch verwende. Er Grund ist, dass man bei der Einteilung des OP-Code-Bytes in Nibbles am besten sehen kann, wie die einzelnen Befehle strukturiert sind. So stehen z.B. die meisten Befehle in Zeilen, und bestimmte Spalten wählen die entsprechende Adressierungsart aus. Leider ist dies nicht immer so, aber bei den meisten Befehlen kann man diese Regel anwenden. Die nachfolgende Tabelle mit der Strukturierungsvariante, die auch der zuvor angegebene Link benutzt, stellt nun in den Zeilen die Hex-Werte für die Hi-Nibbles, und in den Spalten die Werte für die Lo-Nibbles dar.

 

HI-Nibble

LO-Nibble

0

1

2

3

    4

5

 6

7

  8

 9

A

B

C

D

E

F

0

BRK impl

ORA X,ind

---

---

---

ORA zpg

ASL zpg

---

PHP impl

ORA #

ASL A

---

---

ORA abs

ASL abs

---

1

BPL rel

ORA ind,Y

---

---

---

ORA zpg,X

ASL zpg,X

---

CLC impl

ORA abs,Y

---

---

---

ORA abs,X

ASL abs,X

---

2

JSR abs

AND X,ind

---

---

BIT zpg

AND zpg

ROL zpg

---

PLP impl

AND #

ROL A

---

BIT abs

AND abs

ROL abs

---

3

BMI rel

AND ind,Y

---

---

---

AND zpg,X

ROL zpg,X

---

SEC impl

AND abs,Y

---

---

---

AND abs,X

ROL abs,X

---

4

RTI impl

EOR X,ind

---

---

---

EOR zpg

LSR zpg

---

PHA impl

EOR #

LSR A

---

JMP abs

EOR abs

LSR abs

---

5

BVC rel

EOR ind,Y

---

---

---

EOR zpg,X

LSR zpg,X

---

CLI impl

EOR abs,Y

---

---

---

EOR abs,X

LSR abs,X

---

6

RTS impl

ADC X,ind

---

---

---

ADC zpg

ROR zpg

---

PLA impl

ADC #

ROR A

---

JMP ind

ADC abs

ROR abs

---

7

BVS rel

ADC ind,Y

---

---

---

ADC zpg,X

ROR zpg,X

---

SEI impl

ADC abs,Y

---

---

---

ADC abs,X

ROR abs,X

---

8

---

STA X,ind

---

---

STY zpg

STA zpg

STX zpg

---

DEY impl

---

TXA impl

---

STY abs

STA abs

STX abs

---

9

BCC rel

STA ind,Y

---

---

STY zpg,X

STA zpg,X

STX zpg,Y

---

TYA impl

STA abs,Y

TXS impl

---

---

STA abs,X

---

---

A

LDY #

LDA X,ind

LDX #

---

LDY zpg

LDA zpg

LDX zpg

---

TAY impl

LDA #

TAX impl

---

LDY abs

LDA abs

LDX abs

---

B

BCS rel

LDA ind,Y

---

---

LDY zpg,X

LDA zpg,X

LDX zpg,Y

---

CLV impl

LDA abs,Y

TSX impl

---

LDY abs,X

LDA abs,X

LDX abs,Y

---

C

CPY #

CMP X,ind

---

---

CPY zpg

CMP zpg

DEC zpg

---

INY impl

CMP #

DEX impl

---

CPY abs

CMP abs

DEC abs

---

D

BNE rel

CMP ind,Y

---

---

---

CMP zpg,X

DEC zpg,X

---

CLD impl

CMP abs,Y

---

---

---

CMP abs,X

DEC abs,X

---

E

CPX #

SBC X,ind

---

---

CPX zpg

SBC zpg

INC zpg

---

INX impl

SBC #

NOP impl

---

CPX abs

SBC abs

INC abs

---

F

BEQ rel

SBC ind,Y

---

---

---

SBC zpg,X

INC zpg,X

---

SED impl

SBC abs,Y

---

---

---

SBC abs,X

INC abs,X

---

 

Die Adressierungsarten werden wie folgt abgekürzt:

 

·       --- zeigt an, dass es für die entsprechende Kombination keinen legalen OP-Code gibt

·       imm bezeichnet einen numerischen Wert, ein sogenanntes Immidiate

·       impl (implied) gibt an, dass der Befehl keinen Parameter besitzt, wie es z.B. bei CLC und SEC der Fall ist

·       # gibt an, dass ein Immidiate benutzt wird

·       A gibt an, dass der Befehl mit dem Akkumulator ausgeführt wird, wie es z.B. bei ROL und ROR der Fall sein kann

·       abs gibt an, dass die absolute direkte Adressierung benutzt wird

·       abs,X gibt an, dass die absolute direkte X-indizierte Adressierung benutzt wird

·       abs,Y gibt an, dass die absolute direkte Y-indizierte Adressierung benutzt wird

·       zpg gibt an, dass absolute Zeropage-Adressen benutzt werden

·       zpg,X gibt an, dass absolute Zeropage-Adressen benutzt werden, die aber X-indiziert sind

·       zpg,Y gibt an, dass absolute Zeropage-Adressen benutzt werden, die aber Y-indiziert sind

·       (ind,X) zeigt die indirekte Adressierungsart (Adr,X) an

·       (ind),Y zeigt die indirekte Adressierungsart (Adr),Y an

 

6.1 Load/Store-Operationen

 

Hierzu gehören alle Operationen, deren Name mit LD und ST anfängt, also z.B. LDA und STA, aber auch LDX, STX, LDY und STY.

 

6.1.1 LDA (load accumulator)

 

Beispiel: LDA (248),Y

 

LDA lädt einen 8-Bit-Wert in den Akkumulator (A) und setzt auch entsprechend die Flags neu. LDA kann mit den folgenden Adressierungsarten benutzt werden:

 

LDA #imm

LDA abs

LDA zpg

LDA zpg,X

LDA zpg,Y

LDA abs,X

LDA abs,Y

LDA (ind,X)

LDA (ind),Y

 

6.1.2 STA (store accumulator)

 

Beispiel: STA (250),Y

 

STA legt den 8-Bit-Wert im Akkumulator (A) in einer Speicheradresse ab. Die Flags werden hier nicht neu berechnet. STA kann mit den folgenden Adressierungsarten benutzt werden:

 

STA #imm

STA abs

STA zpg

STA zpg,X

STA zpg,Y

STA abs,X

STA abs,Y

STA (ind,X)

STA (ind),Y

 

6.1.3 LDX (load register X)

 

Beispiel: LDX #0

 

LDX lädt einen 8-Bit-Wert in das X-Register und setzt auch entsprechend die Flags neu. LDX kann mit den folgenden Adressierungsarten benutzt werden:

 

LDX #imm

LDX abs

LDX zpg

LDX zpg,Y

LDX abs,Y

 

Die Index-Register können nicht selbst Teil der Indexierungsquelle sein und so auch nicht für die indirekte Adressierung benutzt werden, wenn sie selbst als Ziel angegeben werden.

 

6.1.4 STX (store register X)

 

Beispiel: STX 1024

 

STX legt den 8-Bit-Wert im X-Register in einer Speicheradresse ab. Die Flags werden hier nicht neu berechnet. STX kann mit den folgenden Adressierungsarten benutzt werden:

 

STX abs

STX zpg

STX zpg,Y

 

Die Index-Register können nicht selbst Teil der Indexierungsquelle sein und so auch nicht für die indirekte Adressierung benutzt werden, wenn sie selbst als Ziel angegeben werden. STX hat aber noch eine Einschränkung: Die Y-indizierte direkte Adressierungsform funktioniert nur zusammen mit Adressen, die unter 256 liegen.

 

6.1.5 LDY (load register Y)

 

Beispiel: LDY #0

 

LDY lädt einen 8-Bit-Wert in das Y-Register und setzt auch entsprechend die Flags neu. LDY kann mit den folgenden Adressierungsarten benutzt werden:

 

LDY #imm

LDY abs

LDY zpg

LDY zpg,X

LDY abs,X

 

Die Index-Register können nicht selbst Teil der Indexierungsquelle sein und so auch nicht für die indirekte Adressierung benutzt werden, wenn sie selbst als Ziel angegeben werden.

 

6.1.6 STY (store register Y)

 

Beispiel: STY 1024

 

STY legt den 8-Bit-Wert im Y-Register in einer Speicheradresse ab. Die Flags werden hier nicht neu berechnet. STY kann mit den folgenden Adressierungsarten benutzt werden:

 

STY abs

STY zpg

STY zpg,Y

 

Die Index-Register können nicht selbst Teil der Indexierungsquelle sein und so auch nicht für die indirekte Adressierung benutzt werden, wenn sie selbst als Ziel angegeben werden. STY hat aber noch eine Einschränkung: Die Y-indizierte direkte Adressierungsform funktioniert nur zusammen mit Adressen, die unter 256 liegen.

 

6.2 Register-Transfer-Operationen

 

Der Name von Register-Transfer-Operationen fängt mit T an und beinhaltet die Operationen TAX, TAY, TXA, TYA, TSX und TXS. Bei den Namen wird stets die Quelle zuerst angegeben, und danach das Ziel. Die Index-Register können hier nicht als Ziel angegeben werden.

 

6.2.1 TAX (transfer A to X)

 

Überträgt den Inhalt des Akkumulators in das Index-Register X, der Inhalt des Akkumulators wird hierbei nicht verändert. Zero-Flag und Negative-Flag werden anhand des Inhalts von X neu berechnet.

 

6.2.2 TXA (transfer X to A)

 

Überträgt den Inhalt des Index-Registers X in den Akkumulator, der Inhalt des X-Registers wird hierbei nicht verändert. Zero-Flag und Negative-Flag werden anhand Inhalts des Akkumulators neu berechnet.

 

7.2.3 TAY (transfer A to Y)

 

Überträgt den Inhalt des Akkumulators in das Index-Register Y, der Inhalt des Akkumulators wird hierbei nicht verändert. Zero-Flag und Negative-Flag werden anhand des Inhalts von Y neu berechnet.

 

6.2.4 TYA (transfer Y to A)

 

Überträgt den Inhalt des Index-Registers Y in den Akkumulator, der Inhalt des Y-Registers wird hierbei nicht verändert. Zero-Flag und Negative-Flag werden anhand des Inhalts des Akkumulators neu berechnet.

 

6.2.5 TSX (transfer S to X)

 

Das Index-Register X ist zusätzlich mit dem Stack Pointer verbunden, deshalb kann der Inhalt des S-Registers an das Index-Register X gesendet werden. Eine „Nebenwirkung“ von TSX ist, dass das Negative-Flag und das Zero-Flag anhand der neuen Werte im X-Register auch entsprechend neu berechnet wird.

 

6.2.6 TXS (transfer X to S)

 

Das Index-Register X ist zusätzlich mit dem Stack Pointer verbunden, deshalb kann der Inhalt des X-Registers an das S-Register S gesendet werden. Hierdurch wird der Stack Pointer neu gesetzt. Die Flags werden von TXS nicht verändert.

 

6.3 Stack-Operationen

 

Die Namen der Stack-Operationen fangen mit PH (für push) oder PL (für pull) an. Nach PH oder PL folgt der Name des Registers, mit dem die Stack-Operation ausgeführt werden soll. Allerdings können die Index-Register nicht zusammen mit Stack-Operationen benutzt werden, auch nicht das X-Register, das ja mit dem Stack verbunden ist (s.o.). Woran dies liegt, und ob hierfür nicht ein Prozessor-Bug verantwortlich ist, konnte ich bis jetzt nicht herausfinden.

 

6.3.1 PHA (push accumulator)

 

PHA schiebt den Inhalt des Akkumulators auf den Stack. Auf dem Stack werden ausschließlich 8-Bit-Werte gesichert, und auch keine Flags, wie z.B. das Carry-Flag.

 

6.3.2 PLA (pull accumulator)

 

PLA zieht den Inhalt des Akkumulators vom Stack. Auf dem Stack werden ausschließlich 8-Bit-Werte gesichert, und die Flags werden von PLA anhand des zuletzt vom Stack gezogenen 8-Wits-Wertes neu berechnet.

 

6.3.3 PHP (push processor status)

 

PHP schiebt den Inhalt des Prozessor-Status-Registers P auf den Stack. Auf diese Weise können die Flags zwischengespeichert werden.

 

6.3.4 PLP (pull processor status)

 

PLP zieht den Inhalt des Status-Registers P vom Stack. Auf diese Weise können mit PHP und PLP Unterprogramme erstellt werden, die die Flags nicht verändern.

 

6.4 Arithmetisch-logische Operationen

 

Zu den arithmetisch-logischen Operationen gehören sowohl ADC und SBC, als auch die logischen Operationen AND, ORA und EOR. Der 6510-Prozessor besitzt keine NOT-Operation, diese muss durch EOR #$FF simuliert werden.

 

6.4.1 ADC (add with carry)

 

Beispiel: ADC #42

 

ADC addiert einen 8-Bit-Wert mit Übertrag zu dem Wert, der sich bereits im Akkumulator befindet. ADC kann zusammen mit Immidiates, aber auch zusammen mit Speicheradressen als Quelle benutzt werden. Wenn zuvor das Carry-Flag gesetzt war, dann wird zusätzlich zum Ergebnis 1 addiert. Wenn die Addition mit ADC ein 9 Bit großes Ergebnis ergeben würde, wird bzw. bleibt das Carry-Flag gesetzt, ansonsten wird das Carry-Flag gelöscht. Das Carry-Flag bleibt auch stets in der nächsten ADC-Operation erhalten, es sei denn, das Carry-Flag wird zuvor mit CLC per Hand gelöscht.

 

ADC kann mit den folgenden Adressierungsarten benutzt werden:

 

ADC #imm

ADC abs

ADC zpg

ADC zpg,X

ADC abs,X

ADC abs,Y

ADC (ind,X)

ADC (ind),Y

 

6.4.2 SBC (substract with carry)

 

Beispiel: SBC #42

 

SBC subtrahiert einen 8-Bit-Wert mit Übertrag zu dem Wert, der sich bereits im Akkumulator befindet. SBC kann zusammen mit Immidiates, aber auch zusammen mit Speicheradressen als Quelle benutzt werden. Wenn zuvor das Carry-Flag gelöscht war, dann wird zusätzlich vom Ergebnis 1 subtrahiert. SBC arbeitet im Gegensatz zu ADC mit vorzeichenbehafteten Zahlen, das heißt, dass Bit Nr. 7 als Vorzeichenbit interpretiert wird. Auch das Carry-Flag wird anders benutzt, als bei ADC, nämlich als Borrow-Flag. Das bedeutet, dass SBC das Carry-Flag löscht, wenn ein Übertrag stattfindet. Diese Tatsache ist eine häufige Fehlerquelle.

 

SBC kann mit den folgenden Adressierungsarten benutzt werden:

 

SBC #imm

SBC abs

SBC zpg

SBC zpg,X

SBC abs,X

SBC abs,Y

SBC (ind,X)

SBC (ind),Y

 

6.4.3 ORA (OR with accumulator)

 

Beispiel: ORA #$3F

 

ORA verknüpft den Inhalt des Akkumulators durch ODER- hier werden die einzelnen Bits des Akkumulators mit den Bits des Operanden verknüpft. Als Operand kann ORA sowohl Immidiates, als auch den Inhalt von Speicheradressen benutzen. Die Operation ORA berechnet die Flags neu, allerdings nur das Negative- und das Zero-Flag. Das Carry-Flag bleigt von ORA unberührt.

 

ORA kann mit den folgenden Adressierungsarten benutzt werden:

 

ORA #imm

ORA abs

ORA zpg

ORA zpg,X

ORA abs,X

ORA abs,Y

ORA (ind,X)

ORA (ind),Y

 

6.4.4 AND (logical and)

 

Beispiel: AND #$C0

 

AND verknüpft den Inhalt des Akkumulators durch UND- hier werden die einzelnen Bits des Akkumulators mit den Bits des Operanden verknüpft. Als Operand kann AND sowohl Immidiates, als auch den Inhalt von Speicheradressen benutzen. Die Operation AND berechnet die Flags neu, allerdings nur das Negative- und das Zero-Flag. Das Carry-Flag bleigt von AND unberührt.

 

AND kann mit den folgenden Adressierungsarten benutzt werden:

 

AND #imm

AND abs

AND zpg

AND zpg,X

AND abs,X

AND abs,Y

AND (ind,X)

AND (ind),Y

 

6.4.5 EOR (logical exclusice or)

 

Beispiel: EOR #$FF (dies entspricht NOT A)

 

EOR verknüpft den Inhalt des Akkumulators durch XOR- hier werden die einzelnen Bits des Akkumulators mit den Bits des Operanden verknüpft. Als Operand kann EOR sowohl Immidiates, als auch den Inhalt von Speicheradressen benutzen. Die Operation EOR berechnet die Flags neu, allerdings nur das Negative- und das Zero-Flag. Das Carry-Flag bleigt von EOR unberührt.

 

EOR kann mit den folgenden Adressierungsarten benutzt werden:

 

EOR #imm

EOR abs

EOR zpg

EOR zpg,X

EOR abs,X

EOR abs,Y

EOR (ind,X)

EOR (ind),Y

 

6.5 Bit-Schiebe-Operationen

 

Der Akkumulator, aber auch Speicherinhalte, können als lineares Schiebe-Register (LFSR) benutzt werden, zusammen mit dem Carry-Flag als zusätzliches 9. Bit. Leider kann der 6510-Prozessor eine Bit-Verschiebung immer nur um 1 Bit durchführen, weshalb Bitverschiebungen in schnellen Programmen vermieten werden sollten. Die vier Bitverschiebe-Operationen, die der 6510 unterstützt, sind LSR (logic shift right), ASL (accumulator shift left), ROR (rotate right) und ROL (rotate left).

 

6.5.1 LSR (logical shift right)

 

Beispiel: LSR 1024,X

 

LSR verschiebt den Inhalt des Akkumulators oder einer Speicheradresse um 1 Bit nach rechts. Dies entspricht einer Division durch 2 ohne Rest. Die Operation LSR berechnet die Flags neu, allerdings nur das Negative- und das Zero-Flag. Das Carry-Flag bleigt von LSR unberührt.

 

LSR kann mit den folgenden Adressierungsarten benutzt werden (impl bedeutet ohne zusätzliche Parameter):

 

LSR impl (Akkumulator)

LSR abs

LSR zpg

LSR zpg,X

LSR abs,X

 

6.5.2 ASL (accumulator shift left)

 

Beispiel: ASL 1024,X

 

ASL verschiebt den Inhalt des Akkumulators oder einer Speicheradresse um 1 Bit nach links. Dies entspricht einer ganzzahligen Multiplikation mit 2. Die Operation ASL berechnet die Flags neu, allerdings nur das Negative- und das Zero-Flag. Das Carry-Flag bleigt von ASL unberührt.

 

ASL kann mit den folgenden Adressierungsarten benutzt werden (impl bedeutet ohne zusätzliche Parameter):

 

ASL impl (Akkumulator)

ASL abs

ASL zpg

ASL zpg,X

ASL abs,X

 

6.5.3 ROR (rotate right)

 

Beispiel: ROR

 

ROR verschiebt den Inhalt des Akkumulators oder einer Speicheradresse um 1 Bit nach rechts, benutzt das Ziel allerdings im Gegensatz zu LSR als LFSR (linear feedback shift register). Dies bedeutet Folgendes: Das Bit, das sich im Carry-Flag befindet, wird von der linken Seite aus wieder hineingeschoben, und das unterste Bit (Bit Nr. 0) landet nach der Operation im Carry-Flag.

 

ROR kann mit den folgenden Adressierungsarten benutzt werden (impl bedeutet ohne zusätzliche Parameter):

 

ROR impl (Akkumulator)

ROR abs

ROR zpg

ROR zpg,X

ROR abs,X

 

6.5.4 ROL

 

Beispiel: ROL

 

ROL verschiebt den Inhalt des Akkumulators oder einer Speicheradresse um 1 Bit nach links, benutzt das Ziel allerdings im Gegensatz zu ASL als LFSR (linear feedback shift register). Dies bedeutet Folgendes: Das Bit, das sich im Carry-Flag befindet, wird von der rechten Seite aus wieder hineingeschoben, und das oberste Bit (Bit Nr. 7) landet nach der Operation im Carry-Flag.

 

ROL kann mit den folgenden Adressierungsarten benutzt werden (impl bedeutet ohne zusätzliche Parameter):

 

ROL impl (Akkumulator)

ROL abs

ROL zpg

ROL zpg,X

ROL abs,X

 

6.6 Inkrement- und Dekrement-Operationen

 

Der 6510 bietet einige Operationen an, mit dem Speicher- und Registerinhalte inkrementiert (um 1 erhöht) und dekrementiert (um 1 erniedrigt) werden können. Diese Operationen werden vor Allem in Schleifen eingesetzt, da Inkremente und Dekremente zusammen mit dem Ergebnis auch die Flags beeinflussen, und ferner nur wenige Prozessortakte benötigen.

 

6.6.1 INX (increment X)

 

INX erhöht den Inhalt des X-Registers um 1, ohne aber bei einem Überlauf von 255 zu 0 das Carry-Flag zu setzen. Zero- und Negative-Flag (dies wird gesetzt, wenn das oberste Bit 1 ist) werden aber beeinflusst. INX kann nur in der impliziten Form ohne Parameter benutzt werden.

 

6.6.2 DEX (decrement X)

 

DEX erniedrigt den Inhalt des X-Registers um 1, ohne aber bei einem Unterlauf von 0 zu 255 das Carry-Flag zu setzen. Zero- und Negative-Flag (dies wird gesetzt, wenn das oberste Bit 1 ist) werden aber beeinflusst. DEX kann nur in der impliziten Form ohne Parameter benutzt werden.

 

6.6.3 INC (address increment)

 

Beispiel: INC 1024

 

INC erhöht den Inhalt einer Speicheradresse um 1, ohne aber bei einem Überlauf von 255 zu 0 das Carry-Flag zu setzen. Zero- und Negative-Flag (dies wird gesetzt, wenn das oberste Bit 1 ist) werden aber beeinflusst.

 

INC kann mit den folgenden Adressierungsarten benutzt werden:

 

INC abs

INC zpg

INC zpg,X

INC abs,X

 

6.6.4 DEC (address decrement)

 

Beispiel: DEC 1024

 

DEC erniedrigt den Inhalt einer Speicheradresse um 1, ohne aber bei einem Unterlauf von 0 zu 255 das Carry-Flag zu setzen. Zero- und Negative-Flag (dies wird gesetzt, wenn das oberste Bit 1 ist) werden aber beeinflusst.

 

DEC kann mit den folgenden Adressierungsarten benutzt werden:

 

DEC abs

DEC zpg

DEC zpg,X

DEC abs,X

 

6.7 Vergleichs-Operationen

 

Mit den Vergleichs-Operationen CMP, CPX und CPY kann der Inhalt des Akkumulators oder der Inhalt der Index-Register mit einem anderen Wert, oder mit dem Inhalt einer Speicheradresse verglichen werden. Zu diesem Zweck wird intern (also ohne den tatsächlichen Inhalt der Quellregister zu verändern) der entsprechende Vergleichswert vom Inhalt des Quellregisters subtrahiert. Anhand dieses neuen, temporären Ergebnisses werden anschließend die Flags neu berechnet. Wenn dann z.B. das Ergebnis 0 war, kann man davon ausgehen, dass Registerinhalt und Vergleichswert gleich groß waren. Wenn dagegen ein negativer Wert als Ergebnis herauskommt, kann man davon ausgehen, dass der Registerinhalt kleiner war, als der Vergleichswert. Wenn dagegen ein positiver Wert als Ergebnis herauskommt, kann man davon ausgehen, dass der Registerinhalt größer war, als der Vergleichswert.

 

6.7.1 CMP (compare accumulator)

 

Beispiel: CMP #0

 

CMP vergleicht den Inhalt des Akkumulators mit einem Immidiate oder dem Inhalt eines Speicheradresse und setzt entsprechend die Flags neu. Das Carry-Flag bleibt hier unverändert, auch, wenn hier intern Subtraktionen aufgeführt werden.

 

CMP kann mit den folgenden Adressierungsarten benutzt werden:

 

CMP #imm

CMP abs

CMP zpg

CMP zpg,X

CMP abs,X

CMP abs,Y

CMP (ind,X)

CMP (ind,Y)

 

6.7.2 CPX (compare register X)

 

Beispiel: CPX #0

 

CPX vergleicht den Inhalt des X-Registers mit einem Immidiate oder dem Inhalt eines Speicheradresse und setzt entsprechend die Flags neu. Das Carry-Flag bleibt hier unverändert, auch, wenn hier intern Subtraktionen aufgeführt werden.

 

CPX kann mit den folgenden Adressierungsarten benutzt werden:

 

CPX #imm

CPX abs

CPX zpg

 

6.7.3 CPY (compare register Y)

 

Beispiel: CPY #0

 

CPY vergleicht den Inhalt des Y-Registers mit einem Immidiate oder dem Inhalt eines Speicheradresse und setzt entsprechend die Flags neu. Das Carry-Flag bleibt hier unverändert, auch, wenn hier intern Subtraktionen aufgeführt werden.

 

CPY kann mit den folgenden Adressierungsarten benutzt werden:

 

CPY #imm

CPY abs

CPY zpg

 

6.8 Bedingte Sprungbefehle

 

Bedingte Sprungbefehle springen zu einer neuen Adresse, wenn im Prozessor-Status-Register P bestimmte Flags gesetzt sind. Hypra-Ass kann auch ein Sprung-Label anstatt einer Adresse benutzen. Die Flags können Sie z.B. durch die Vergleichsbefehle CMP, CPX und CPY beeinflussen, aber auch z.B. durch eine Addition, bei der das Ergebnis 0 ist. Abhängig von diesem Ergebnis können Sie danach einen Sprung auslösen. Allerdings geben Sie bei den bedingten Sprüngen nur einen Adress-Offset an, die Sprungweite vor und zurück ist auf 127 Bytes begrenzt. Wenn Sie diese Regel nicht einhalten, wird von Hypra-Ass die Fehlermeldung BRANCH TOO FAR ausgegeben.

 

6.8.1 BEQ (branch if equal)

 

Beispiel: BEQ CONT

 

BEQ springt nur dann zu dem hinter BEQ angegebenen Label, wenn das Zero-Flag gesetzt ist. Übertragen auf einen Vergleich von A und B bedeutet dies, dass A=B ist. BEQ wird auch ausgeführt, wenn bei dem vorangegangenen Befehl ein Ergebnis von 0 aufgetreten ist. So springt BEQ z.B. auch, wenn nach DEX oder DEY der Inhalt des entsprechenden Index-Registers 0 ist. BEQ wird oft zusammen mit den Index-Registern in Schleifen eingesetzt.

 

6.8.2 BNE (branch if not equal)

 

Beispiel: BNE BACK

 

BNE springt nur dann zu dem hinter BNE angegebenen Label, wenn das Zero-Flag gelöscht ist. Übertragen auf einen Vergleich von A und B bedeutet dies, dass A<>B ist. BNE wird auch ausgeführt, wenn beim dem vorangegangenen Befehl ein Ergebnis ungleich 0 aufgetreten ist. So springt BNE z.B. auch, wenn nach DEX oder DEY der Inhalt des entsprechenden Index-Registers (noch) nicht 0 ist. BNE wird oft zusammen mit den Index-Registern in Schleifen eingesetzt.

 

6.8.3 BMI (branch if minus)

 

Beispiel: BMI CONT

 

BMI springt nur dann zu dem hinter BMI angegebenen Label, wenn das Negative-Flag gesetzt ist. Übertragen auf einen Vergleich von A und B bedeutet dies, dass A<B ist. BMI wird auch ausgeführt, wenn ein Ergebnis negativ ist. So springt BMI z.B. auch, wenn nach DEX oder DEY der Inhalt des entsprechenden Index-Registers einen negativen Wert ergäbe, also das 7. Bit gesetzt ist. BMI wird oft in Schleifen eingesetzt, aber nur selten zusammen mit den Index-Registern.

 

6.8.4 BPL (branch if plus)

 

Beispiel: BPL BACK

 

BPL springt nur dann zu dem hinter BPL angegebenen Label, wenn das Negative-Flag gelöscht ist. Übertragen auf einen Vergleich von A und B bedeutet dies, dass A>=B ist. BPL wird auch ausgeführt, wenn ein Ergebnis positiv oder 0 ist. So springt BPL z.B. auch, wenn nach DEX oder DEY der Inhalt des entsprechenden Index-Registers einen Wert enthält, bei den das 7. Bit gelöscht ist. Bei der Verwendung von BPL müssen Sie also vorsichtig sein, weil zusätzlich zu einem negativen Ergebnis auch der Fall mit beachtet wird, dass das Ergebnis wirklich 0 ist.

 

6.8.5 BCS (brach if carry set)

 

Beispiel: BCS 16BITS

 

BCS springt nur dann zu dem hinter BCS angegebenen Label, wenn das Carry-Flag gesetzt ist. Mit BCS kann z.B. festgestellt werden, ob bestimmter Code angesprungen werden soll, wenn das Ergebnis einer Berechnung nicht mehr in ein Byte passt.

 

6.8.6 BCC (brach if carry clear)

 

BCC CONT

 

BCC springt nur dann zu dem hinter BCC angegebenen Label, wenn das Carry-Flag gelöscht ist. Mit BCC kann z.B. erreicht werden, dass bestimmter Code übersprungen wird, wenn das Ergebnis einer Berechnung noch in ein Byte passt.

 

6.8.7 BVS (branch if overflow set)

 

BVS springt nur dann zu dem hinter BVS angegebenen Label, wenn das Overflow-Flag gesetzt ist. Das Overflow-Flag wird immer dann gesetzt, wenn bei einer vorzeichenbehafteten Operation wie SBC der gültige Zahlenbereich verlassen wird. Dies ist z.B. der Fall, wenn Sie von -128 ($80) den Wert 1 subtrahieren und -129 nicht mehr als Byte darstellbar ist. BVS wird vor Allem dazu benutzt, Fehler wie Overflow-Errors festzustellen und anschließend die korrekte Routine anzuspringen. Das Overflow-Flag kann aber auch „missbraucht“ werden, um ganz andere Dinge anzuzeigen- was auch von einigen Kernal-Routinen getan wird.

 

6.8.8 BVC (branch if overflow clear)

 

BVC springt nur dann zu dem hinter BVC angegebenen Label, wenn das Overflow-Flag nicht gesetzt ist. Das Overflow-Flag wird immer dann glöscht, wenn bei einer vorzeichenbehafteten Operation wie SBC das Ergebnis noch im gültigen Bereich (-128 bis +127) ist. BVC wird zusammen mit BVS vor Allem dazu benutzt, Fehler wie Overflow-Errors festzustellen.

 

6.9 Flag-Operationen

 

Mit den Flag-Operationen können einige Flags gezielt gesetzt oder gelöscht werden.

 

6.9.1 SET (set carry flag)

 

SEC setzt das Carry-Flag. Ansonsten werden keine Flags verändert. SEC sollte z.B. vor dem Beginn jeder Subtraktion angewendet werden, aber auch vor jeder Subtraktionskette, die mehr als ein Byte umfasst.

 

6.9.2 CLC (clear carry flag)

 

CLC löscht das Carry-Flag. Ansonsten werden keine Flags verändert. CLC sollte z.B. vor dem Beginn jeder Addition angewendet werden, aber auch vor jeder Additionskette, die mehr als ein Byte umfasst.

 

6.9.3 SEI (set interrupt disable flag)

 

SEI setzt das Interrupt-Disable-Flag. Ansonsten werden keine Flags verändert. SEI bewirkt, dass der Prozessor nicht mehr auf Interrupt-Quellen reagiert. SEI wird z.B. benutzt, um die Änderung des Timer-Interrupt-Zeigers in den Adressen 788 und 789 einzuleiten. SEI muss nach getaner Arbeit wieder mit CLI aufgehoben werden, sonst könnte das System einfrieren. Die sogenannten nicht maskierbaren Interrupts (NMIs) wie der Rasterstrahl-Interrupt sind allerdings nicht von SEI betroffen.

 

6.9.4 CLI (clear interrupt disable flag)

 

CLI löscht das Interrupt-Disable-Flag und hebt damit die Wirkung von SEI wieder auf. CLI bewirkt, dass der Prozessor wieder auf Interrupt-Quellen reagiert. Jedes SEI muss also nach getaner Arbeit wieder mit CLI aufgehoben werden, sonst könnte das System einfrieren. Die sogenannten nicht maskierbaren Interrupts (NMIs) wie der Rasterstrahl-Interrupt sind allerdings nicht von CLI betroffen.

 

6.9.5 CLV (clear overfow flag)

 

Das Overflow-Flag kann nur mit CLV gelöscht, nicht aber gesetzt werden. CLV sollte immer dann benutzt werden, wenn eine Fehler-Behandlungs-Routine für Überläufe abgeschlossen wurde, am besten direkt vor dem entsprechenden RTS-Befehl.

 

6.9.6 SED (set decimal mode flag)

 

SED setzt das Decimal-Mode-Flag des Prozessors. Der 6510 rechnet nun mit Dezimalstellen. Diese stehen in den einzelnen Nibbles eines Bytes anstatt mit Binärzahlen. Eine Addition des Wertes $09 und des Wertes $01 ergeben im Dezimalmodus

das Ergebnis $10- mit zusätzlich gesetztem Carry-Flag, das ein Überlauf einer Zehner-Stelle anzeigt.

 

6.9.7 CLD (clear decimal mode flag)

 

CLD löscht das D-Flag und kehrt daraufhin unverzüglich in den Binärmodus zurück. Leider bleiben dadurch die Flags in dem Zustand, den sie zuvor im Dezimalmodus hatten- und werden dadurch quasi bis zum nächsten Befehl, der die Flags neu berechnet unbrauchbar.

 

6.10 Direkte Sprünge

 

Direkte Sprünge geschehen, ohne dass zuvor bestimmte Flags geprüft werden- also immer und sofort. Direkte Sprünge werden entweder mit JSR oder JMP zu einer absoluten Adresse ausgeführt. Im Gegensatz zu JSR kann JMP auch indirekt springen, und zwar zu einer Adresse, die in einem 16-Bit-Zeiger steht. So etwas ähnliches wie JSR wird auch ausgeführt, wenn ein Interrupt auftritt, allerdings werden Interrupt-Routinen oft mit RTI (return from interrupt) anstatt RTS (return from subroutine) beendet.

 

6.10.1 JMP (jump to address)

 

Beispiel: JMP BACK

 

JMP springt direkt zu einer hinter JMP angegebenen Adresse, bei Hypra-Ass kann hier aber auch ein Label angegeben werden. JMP kann als einzige Sprung-Operation auch indirekte Adressen benutzen, JMP (50000) ist also erlaubt.

 

6.10.2 JSR (jump to subroutine)

 

Beispiel: JSR CHAROUT

 

JSR springt direkt zu einer hinter JSR angegebenen Adresse, bei Hypra-Ass kann hier aber auch ein Label angegeben werden. JSR kann nicht mit indirekten Adressen benutzt werden, JSR (50000) ist also nicht erlaubt. Im Gegensatz zu JMP sichert JSR vor dem Sprung die Adresse des direkt hinter JSR angegebenen Befehls auf dem Stack, sodass man in einfacher Weise wieder aus dem Unterprogramm zurückkehren kann.

 

6.10.3 RTS (return from subroutine)

 

RTS entnimmt dem Stack die Adresse des Programm-Counters, die der letzte JSR-Befehl dort vorher abgelegt hat und kehrt so zum Aufrufer zurück. Wenn Unterprogramme den Stack für temporäre Werte benutzen, muss dieser vor dem RTS-Befehl aufgeräumt werden (das heißt, der Stack Pointer muss den selben Wert haben, den er vor Eintritt in das Unterprogramm hatte).

 

6.10.4 RTI (return from interrupt)

 

RTI ist identisch mit RTS, nur dass RTI auch das Prozessor-Status-Register P wiederherstellt (das ist der Status, der direkt vor dem Eintritt in die Interrupt-Routine herrschte). Natürlich wird hierdurch auch implizit ein CLI-Befehl mit ausgeführt. Da vor Eintritt in eine Interrupt-Routine zusätzlich zu PC auch P auf dem Stack abgelegt wird, geht RTI mit dem Stack anders um, als RTS. RTS und RTI sind also nicht zueinander kompatibel.

 

6.11 Spezielle Befehle, die keiner bestimmten Kategorie angehören

 

6.11.1 BRK (break)

 

BRK löst einen Interrupt aus, der bewirkt, das sich (zumindest, wenn das BASIC-ROM aktiv ist) das Programm so verhält, als ob Sie RUN/STOP+RESTORE gedrückt hätten. BRK kann durch SEI maskiert werden, wird also bei gesetztem Interrupt-Disable-Flag ignoriert.

 

BIT (bitmask operation)

 

Bit 6 und 7 aus einer bestimmten Speicheradresse M werden in das Prozessor-Status-Register P übertragen, und zwar so: Bit 7 wird von M in das Negative-Flag, in Bit 6 von M in das Overflow-Flag übertragen. Außerdem ist, wenn der Akkumulator-Inhalt A AND M=0 ist, das Zero-Flag gesetzt, andernfalls wird das Zero-Flag gelöscht.

 

Bit kann zusammen mit folgenden Adressierungsarten benutzt werden:

 

BIT zpg

BIT abs

 

6.11.2 NOP (no operation)

 

NOP macht nichts, außer zwei Prozessortakte lang zu warten. NOP-Befehle bzw. mehrere NOP-Befehle müssen manchmal eingesetzt werden, um auf Hardware-Komponenten zu warten. Z.B. ist es sinnvoll, nach Auslesen der Adressen 56320 und 56321 (Tastaturstatus) zwei NOP-Befehle einzufügen, um dem CIA-Baustein Zeit zu lassen, sogenannte Tastaturpreller zu kompensieren. Aber auch für das sogenannte Code-Allignment, also das Ausrichten von bestimmten Unterprogramm-Adressen an Seitengrenzen, werden oft NOP-Befehle benutzt.

 

6.12. Flags

 

6.12.1 Negative-Flag

 

Dieses Flag wird immer dann gesetzt, wenn auch beim Ergebnis-Byte einer Berechnung (dies kann auch das temporäre Ergebnis nach einem Vergleich sein) das 7. Bit gesetzt ist. In den meisten Fällen zeigt ein gesetztes 7. Bit ein negatives Ergebnis an- dies muss aber nicht zwingend der Fall sein.

 

6.12.2 Overflow-Flag

 

Dieses Flag wird immer dann gesetzt, wenn das Ergebnis einer Berechnung den gültigen Wertebereich verlassen hat. Das Overflow-Flag ist in der folgenden Weise mit dem Negative-Flag gekoppelt: Wenn sich bei einer Addition von zwei Zahlen, die dasselbe Vorzeichen haben, nach der Berechnung das Vorzeichen geändert hat, dann kann man mit Sicherheit davon ausgehen, dass ein Überlauf des zulässigen Wertebereiches aufgetreten ist. Wenn sich stattdessen bei einer Subtraktion von zwei Zahlen, die ein entgegengesetztes Vorzeichen haben, nach der Berechnung das Vorzeichen geändert hat, dann kann man ebenfalls mit Sicherheit davon ausgehen, dass ein Überlauf des zulässigen Wertebereiches aufgetreten ist.

Wenn Sie z.B. zu der positiven Zahl +127 den Wert +1 addieren, dann wird das Overflow-Flag gesetzt. Ebenso wird das Overflow-Flag gesetzt, wenn Sie von -128 die Zahl 1 abziehen. Wenn Sie allerdings von 1 den Wert 2 abziehen, wird das Overflow-Flag nicht gesetzt. Übertrag (Carry) und Überlauf (Overflow) sind also zwei verschiedene Paar Schuhe und müssen auch gesondert behandelt werden.

 

6.12.3 Decimal-Flag

 

Wenn dieses Flag gesetzt ist, rechnet der 6510 im Dezimalmodus, ist dieses Flag gelöscht, rechnet der 6510 im Standard-Binärmodus.

 

6.12.4 Interrupt-Disable-Flag

 

Ist dieses Flag gelöscht, dann reagiert der 6510 auf externe Interrupt-Quellen, ist dieses Flag gesetzt, dann reagiert der 6510 nur auf nicht maskierbare Interrupts (NMIs).

 

6.12.5 Carry-Flag

 

Das Carry-Flag zeigt einen Übertrag (keinen Überlauf!) an, wenn es nach einer Rechen-Operation gesetzt ist. Im Carry-Flag befindet sich also das 9. Bit, das nicht mehr in ein Byte passt. Das Carry-Flag wird dann bei der nächsten Rechen-Operation mit beachtet. Bei einer SBC-Operation fungiert das Carry-Flag als Borrow-Flag: Wenn es gelöscht ist, dann wird bei der nächsten SBC-Operation 1 mehr subtrahiert, als dies normalerweise der Fall sein müsste. Bei ADC würde hier nichts geschehen, und nur dann zusätzlich 1 addiert, wenn das Carry-Flag gesetzt ist. Sie können sich diesen Sachverhalt wie folgt merken: Bei ADC (Gewinn) nehmen Sie tatsächlich einen Taler mehr ein, als das Ergebnis erwarten lässt, wenn nach einer Addition ein Übertrag auftritt. Bei einer Subtraktion (Schulden) muss man sich bei einem Übertrag zunächst einen Taler borgen. Da der 6510 so streng ist, wie Dagobert Duck, unterscheidet er auch genau so streng zwischen Einnahmen und Ausgaben.

Im Gegensatz um Overflow-Flag zeigt das Carry-Flag keinen Überlauf des gültigen Wertebereiches an, sondern enthält stets das 9. Bit des Ergebnisses einer Rechenoperation, das nicht mehr in ein einzelnes Byte passt. Das bedeutet also:

 

CLC

LDA #255 (vorzeichenbehaftet gesehen Wert -1)

ADC #1 (vorzeichenbehaftet gesehen Wert +1)

Ergebnis=$00, das Carry-Flag wird gesetzt, und das Overflow-Flag bleibt unverändert

 

SEC

LDA #0 (vorzeichenbehaftet gesehen Wert 0)

SBC #1 (vorzeichenbehaftet gesehen Wert +1)

Ergebnis=$FF, das Carry-Flag wird gelöscht, und das Overflow-Flag bleibt unverändert

 

SEC

LDA #128 (vorzeichenbehaftet gesehen Wert -128)

SBC #1 (vorzeichenbehaftet gesehen Wert +1)

Ergebnis=$7F, das Carry-Flag wird nicht gelöscht, und Overflow-Flag wird gesetzt

 

6.13 Prozessor-Register

 

6.13.1 Akkumulator A

 

Der Akkumulator ist das Rechenregister des 6510. Sämtliche Rechenoperationen, aber auch sämtliche logische Operationen werden mit dem Akkumulator ausgeführt. Die Akkumulator-Lade- und -Speicherbefehle LDA und STA beherrschen sämtliche Adressierungsarten.

 

6.13.2 Index-Register X

 

Mit dem Indexregister X und den INX- und DEX-Operationen können schnelle Schleifen realisiert werden. Außerdem kann mit dem Index-Register durch die indirekte indizierte Adressierungsart LDA (Adresse,X) in einfacher Weise auf die Zeiger in einem Zeiger-Array zugegriffen werden. Allerdings gilt hier die Einschränkung, dass 16-Bit-Zeiger nur im Zeropage-Bereich liegen dürfen.

 

6.13.3 Index-Register Y

 

Mit dem Indexregister Y und den INY- und DEY-Operationen können schnelle Schleifen realisiert werden. Außerdem kann mit dem Index-Register durch die indirekte indizierte Adressierungsart LDA (Adresse),Y in einfacher Weise auf Zeiger zugegriffen werden, die auch zusätzlich einen 8-Bit-Offset enthalten können. Obwohl hier die Einschränkung gilt, dass 16-Bit-Zeiger nur im Zeropage-Bereich liegen dürfen, ist (Adresse),Y die am häufigsten verwendete Adressierungsart beim C64.

 

6.13.4 Stack Pointer S

 

S zeigt auf den Speicherbereich, an dem als nächstes durch eine Push-Operation ein Wert abgelegt werden soll. S ist nur 8 Bit breit, für die Speicheradresse, auf die S zeigt, wird aber ein 9. Bit benutzt, das stets 1 ist. Der Stack-Bereich liegt also stets in dem Adressbereich 256 bis 511. Der 6510-Prozessor führt ein sogenanntes Post-Increment aus. Dies bedeutet, dass die Push-Operationen zunächst den entsprechenden Wert in der Adresse ablegen, auf die S zeigt, und anschließend S um 1 erhöhen. Die Pull-Befehle dagegen führen ein Pre-Decrement aus: S wird um 1 erniedrigt, und erst danach wird der Inhalt der Adresse, auf die S zeigt, zurückgegeben. Wenn S zuvor den Wert 0 hatte, dann zeigt S nach einem Pull-Befehl auf die Adresse 511. Diesen Zustand bezeichnet man als Stack-Unterlauf oder Stack-Underrun. Wenn S zuvor den Wert 255 hatte, dann zeigt S nach einem Push-Befehl auf die Adresse 256. Diesen Zustand bezeichnet man als Stack-Überlauf oder Stack-Overflow. Stack-Overflows sind sehr kritische Zustände, die meistens auch das System zum Absturz bringen.

 

6.13.5 Program-Counter PCL (PC Lo-Byte)

 

PC zeigt stets auf den Befehl, der als nächstes ausgeführt wird. Der 8-Bit-Prozessor 6510 muss aber den 16-Bit-Zeiger PC in ein Lo- und ein Hi-Byte aufteilen.

 

6.13.6 Program-Counter PCH (PC Hi-Byte)

 

PC zeigt stets auf den Befehl, der als nächstes ausgeführt wird. Der 8-Bit-Prozessor 6510 muss aber den 16-Bit-Zeiger PC in ein Lo- und ein Hi-Byte aufteilen.

 

6.13.7 Prozessor-Staus-Register P

 

P beinhaltet die Flags als einzelne Bits. P kann nicht direkt verändert werden, sondern nur indirekt durch PLP, SEC, CLC, SED, CLD, CLV, SEI und CLI. Die Bits in P sind wie folgt belegt:

 

Bit 7: Negative-Flag (N)

Bit 6: Overflow-Flag (V)

Bit 5: nicht benutzt (stets 1)

Bit 4: Break-Flag (B) (wird direkt nach einer BRK-Operation auf 0 gesetzt)

Bit 3: Decimal-Mode-Flag (D)

Bit 2: Interrupt-Disable-Flag (I)

Bit 1: Zero-Flag (Z)

Bit 0: Carry-Flag (C)

 

Mit PHP, STA, AND, ORA und PLP können die einzelnen Flag-Bits indirekt geändert werden. Der folgende Code setzt z.B. das Overflow-Flag:

 

PHP

PLA

ORA #$40

PHA

PLP

 

Der folgende Code löscht dagegen das Break-Flag:

 

PHP

PLA

AND #$EF

PHA

PLP

 

6.14 Der Decimal-Mode (D-Flag=1)

 

Im Dezimal-Modus (Decimal-Mode) wird der Inhalt des Akkumulators bei der Addition und Subtraktion anders behandelt, als im Binärmodus: Es werden die einzelnen Nibbles von Quelle (also dem Akkumuator) und Operanden (also dem Immidiate oder dem Inhalt einer Speicheradresse) betrachtet. Wenn allerdings der Wert im ersten Nibble überläuft bzw. unterläuft, dann wird noch kein Carry- oder Overflow-Flag gesetzt, sondern der Übertrag wird entsprechend im hohen Nibble mitbeachtet. Erst, wenn das hohe Nibble überläuft, wird das Carry-Flag entsprechend gesetzt.

Ein wirklicher Überlauf (also ein Overflow) kann aber nicht festgestellt werden, denn das Overflow-Flag wird von ADC erst dann gesetzt, wenn das hohe Nibble Werte enthalten müsste, die größer als 16 sind. Bei einer Subtraktion wird das Overflow-Flag nur gesetzt, wenn das hohe Nibble Werte zwischen 10 und 14 enthält. Auch lässt sich der Decimal-Mode nicht einfach durch Löschen des D-Flags ausschalten, dadurch stürzt das System ab. Stattdessen muss vor dem ersten SED-Befehl mit PHP das S-Register gesichert werden, und zum Verlassen des Dezimal-Modus muss statt CLD der Befehl PLP verwendet werden.

 

All diese Dinge habe ich leider nur durch langes Probieren herausgefunden, und ich vermute deshalb, dass der Decimal-Mode Bugs enthält, die auch die Emulatoren nur durch Probieren und anschließendes Implementieren der entsprechenden Regeln umsetzen können. Aufgrund dieses „komischen“ Verhaltens des Decimal-Modes wird dieser auch auf dem C64 kaum benutzt, und im Internet auch kaum richtig besprochen. Wenn ich weitere Dinge herausfinde, werde ich diese entsprechend ergänzen bzw. berichtigen.