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.