SuperCPU durchleuchtet - Folge 2
Im letzten Kursteil haben wir besprochen, wie man eine SuperCPU erkennt. 
Hat man schliesslich festgestellt, dass die Turbo-Karte im Expansionsport
steckt, kann man diese natuerlich auch nutzen. Existierende Routinen laufen
um ein vielfaches schneller - doch der 65816-Prozessor, der in der SuperCPU
steckt, kann noch viel mehr!


Neue Opcodes

Wie jeder weiss, laufen auf der CMD-Turbokarte keine Programme, die illegale
Opcodes verwenden. Doch warum eigentlich? Warum kommt der 65816-Prozessor
damit nicht klar? Schliesslich laufen die "Illegals" auf nahezu allen
Prozessoren, auf neuen und alten C64's und C128's, obwohl oft das Gegenteil
behauptet wurde. Die Begruendung ist einfach: Saemtliche Hex-Werte (von
$00-$FF) sind beim 65816 mit Opcodes belegt! Waehrend der 8500 (im neueren
C64) und der 8502 (im C128) auf dem 6502/6510 basieren, ist der 65816 ein
vollkommen neuer Prozessor, der eine echte Weiterentwicklung darstellt.
Eigentlich ist ja der 65C02 der direkte Nachfolger des 6502. Bei ihm gibt es
zwar einige wenige neue Befehle, doch nichts weltbewegendes. Desweiteren sind
dort alle nicht legalen Opcodes mit "NOP" vorbelegt. Kein "echter" Nachfolger
also - ganz im Gegensatz zum 65816. Kennt man alle seine Faehigkeiten und
vergleicht ihn mit dem 6502, liegen Welten dazwischen. 
Doch wir wollen uns in diesem Kurs ganz langsam herantasten. Es gibt naemlich
nicht nur neue Befehle, sondern auch neue Adressierungsarten und einige
andere Features, die das Schreiben schneller Programme zur wahren Freude
machen. Dazu hat der 65816 noch die Faehigkeit, auf 16 Bit breite Register
umzuschalten!  Dies wird eines der ersten Dinge sein, die Sie in diesem Kurs
kennenlernen werden.


Eingebauter Emulator

Sobald der 65816 Strom bekommt, schaltet er sich automatisch in den
sogenannten "Emulation Mode". In diesem Modus verhaelt er sich, mit Ausnahme
der Tatsache, dass kleine 6502-Fehler ausgemerzt wurden, exakt wie der
6502/6510: Der Stack ist da wo er immer ist, es gibt 64K Adressraum,
8-Bit-Register, eine Zeropage bei $0000 und das Timing der Befehle ist
haargenauso wie beim 6510. Aus diesem Satz laesst sich vielleicht schon
erahnen, was fuer Moeglichkeiten der 65816 bietet - doch alles Schritt fuer
Schritt. Um die neuen Faehigkeiten voll zu nutzen, gibt es den "Native Mode".
Hier spielt der Prozessor alle seine Staerken aus. Dabei wurde dem Konzept
der 65XX-Serie in allen Punkten treu geblieben - Sie werden feststellen, dass
der 65816 die logische Weiterentwicklung des 6510 ist. Es gibt ein neues
Flag, das "E"-Flag, welches wiedergibt, in welchem Modus sich die CPU
befindet, und mit dem man selbst den Modus umschalten kann. 


Das Tor oeffnen

Verschaffen wir uns also Zutritt zu dieser neuen Welt. Der Befehl XCE
ist dazu da, den Inhalt des Carry-Bits mit dem des Emulation-Bits
auszutauschen. Es ist der einzige Befehl, mit dem auf dieses Flag zugegriffen
werden kann, da es quasi als neuntes Flag "hinter" dem Carry-Bit liegt 
(Siehe Schema). Es ist praktisch eine "Hintertuer", um in den Native Mode zu
gelangen - schliesslich sind wir im Emulation Mode, wo es eigentlich keine
zusaetzlichen Flags gibt. Ist dieses Flag 1, befindet sich der Prozessor im
6502-Emulation-Mode, ist es 0, sind wir im Native Mode des 65816. Folglich
schaltet man in den Native Mode, in dem man das Carry-Flag loescht und dies
mit dem Emulation-Bit vertauscht:

CLC
XCE

Das war es schon! Alle Moeglichkeiten des 65816 stehen uns offen. Uebrigens
kann man jetzt am Status des Carry-Bits erkennen, ob die CPU im Emulation-
oder im Native Mode war: Entsprechend ist das C-Flag naemlich jetzt 1 oder 0.
Bevor wir aber den ersten Schritt ins neue Land wagen, sichern wir uns den
Weg zurueck - wie kriegt man den Prozessor wieder in dem Emulation-Mode? Ganz
einfach: Das E-Bit muss wieder auf 1 gesetzt werden. Wieder fuehrt der Weg
durch das Carry:

SEC
XCE

Damit sind wir wieder im 6502-Emulation-Mode. Durch das Umschalten in den
Native Mode gehen einige Aenderungen vor sich, ueber welche man sich auf
jeden Fall im Klaren sein sollte. Der 65816 hat die Moeglichkeit, sowohl den
Akku als auch die Index-Register (X und Y) auf 16 Bit Breite umzuschalten.
Dabei gibt es vier Variationsmoeglichkeiten: Es koennen entweder Akku und  
Index-Register 8 Bit sein, oder Akku 16 Bit, Index-Register 8 Bit, oder Akku
8 Bit, Index-Register 16 Bit, oder Akku und Index-Register 16 Bit! Um dies
einstellen und festhalten zu koennen, gibt es im Native Mode zwei weitere
neue Flags: Das "M"-Flag und das "X"-Flag (Siehe Schema). Dabei muessen
allerdings zwei alte Flags weichen: Das bisher unbenutzte 6502-Flag (wo im
Monitor immer ein "-" zu sehen ist) sowie das Break-Flag gibt es im Native
Mode nicht mehr, dort sind ab sofort die neuen Flags zu finden.


Die Fahne hoch

Das "M"-Flag ist fuer den Akkumulator zustaendig. "M" steht dabei fuer
"Memory", da es ja hauptsaechlich der Akku ist, mit dem auf den Speicher
zugegriffen wird. Ist dieses Flag geloescht, hat der Akku 16 Bit. Das andere
neue Flag, das "X"-Flag, gibt die Breite der Index-Register X und Y an. Ist
es geloescht, sind sie 16 Bit breit, ist es gesetzt, haben wir 8 Bit. Beide
Index-Register haengen hier zusammen, man kann beispielsweise nicht das
X-Register auf 16 Bit schalten und das Y-Register auf 8 Bit belassen.
Doch wie schaltet man nun auf 16 Bit? Gibt es entsprechende Set- und
Clear-Befehle, wie fuer die anderen Flags? Nein - die Entwickler des 65816
haben sich hier etwas ganz neues einfallen lassen. Man wird sicherlich oefter
mal zwischen 8 und 16 Bit hin- und herschalten wollen - dies soll schnell
gehen. Statt verschiedene Befehle fuer die einzelnen neuen Flags gibt es
einen einzigen, mit dem ALLE Flags auf einen Schlag geloescht werden koennen,
und ein Gegenstueck, mit dem man ALLE setzen kann. Doch keine Sorge - auf die
alten Befehle wie SEI, CLC und so weiter muessen Sie natuerlich nicht
verzichten, sie existieren nach wie vor! Zum Beeinflussen der Flags fuer die
Breite der Register allerdings muss das neue Befehls-Duo verwendet werden.
Das Kommando zum Loeschen lautet REP (REset Processorstatus). Mit
Prozessor-Status sind hier die Flags gemeint (also genau wie bei den
bekannten Befehlen PHP und PLP). Hinter REP kommt dann ein 8-Bit-Wert, wobei
jedes Bit die einzelnen Flags repraesentiert. Um beispielsweise das Carry,
das Zero-Flag und das IRQ-Flag auf einen Schlag zu loeschen, genuegt ein

REP #%00000111

Hier ist deutlich zu sehen, wie der Befehl funktioniert: Bei jedem gesetzten
Bit wird das entsprechende Flag geloescht. Das "M"- und das "X"-Flag befinden
sich bei Bit 5 und Bit 4. Um also den Akku und die Index-Register auf 16 Bit
zu schalten, fuehren wir einen

REP #%00110000

aus. Nun sind Akku und Index-Register 16 Bit breit! Alle Flags, bei denen das
Argument zum REP eine Null hatte, bleiben unbeeinflusst. Zu diesem Befehl
gibt es wie erwaehnt noch ein Gegenstueck, mit dem man gleich mehrere Flags
auf einmal setzen kann. Er lautet SEP und funktioniert analog zum REP.
Schalten wir also Akku und Index-Register wieder auf 8 Bit zurueck:

SEP #%00110000

Jedes gesetzte Bit im Argument des SEP bewirkt ein Setzen des entsprechenden
Flags. Alle anderen Flags bleiben wieder unberuehrt.

Doch was bringen 16 Bit? Ist eine hoehere Bitverarbeitungsbreite wirklich ein
Garant fuer hoehere Geschwindigkeit? Die Antwort lautet: Ja, wenn man es
richtig macht. Denn obwohl in der Vergangenheit wie in der Gegenwart ja immer
wieder mit 16, 32 oder 64 Bit geworben wird, muss man diese auch geschickt
einsetzen, denn es ist klar, dass eine Addition der Zahlen 5 und 7 auf einem
32-Bit-Rechner mehr Taktzyklen schluckt als auf dem C64 mit 8 Bit -
schliesslich muessen nicht noch 3 Null-Bytes Ballast mitgeschleppt werden.
Gerade hier liegt die Staerke des 65816 im Native Mode, zwischen 8 und 16 Bit
umzuschalten - eine Moeglichkeit, die es auf anderen Prozessoren nicht oder
nur sehr eingeschraenkt gibt. Denn 16 Bit koennen tatsaechlich zu
betraechtlichen Geschwindigkeitssteigerungen fuehren. Eine simple Subtraktion
von zwei 16-Bit-Zahlen verdeutlicht dies: Der 8-Bit 6510 muss so programmiert
werden, dass er sich das Low-Byte der ersten 16-Bit-Zahl holt, dann davon das
Low-Byte der zweiten 16-Bit-Zahl abzieht, daraufhin das Ergebnis speichert,
dann das High-Byte der ersten Zahl holt, das der zweiten abzieht und
letztendlich speichert. Im 16-Bit-Modus des 65816 hingegen holt man die ganze
16-Bit-Zahl, zieht die zweite davon ab und speichert das Ergebnis. Drei
Schritte statt sechs! Listing 2.1 zeigt die 8-Bit-Variante, Listing 2.2 macht das
gleiche in 16 Bit. Wenn Sie das zweite nicht sofort 100%ig verstehen - keine
Bange, es werden weitere Beispiele und Erklaerungen folgen.

Eins ist wichtig: Die Opcodes bleiben dieselben! Dies hat fuer den Coder den
Vorteil, sich nicht umstellen zu muessen - der Assembler jedoch kann nicht
wissen, welche Flags zum Zeitpunkt der Programmausfuehrung wie gesetzt sind.
Ist "M" auf 16 Bit, erwartet die CPU nach einem absoluten LDA natuerlich
einen 16-Bit-Wert, also zwei Byte. Das selbe gilt fuer die Index-Register.

Dem Assembler muss deshalb mitgeteilt werden, wieviel Bit er zu assemblieren
hat. Der Pseudo-Opcode "£al" schaltet im Falle des F8-Assblaster den Akku auf
"Long" (16 Bit). Mit "£as" wird wieder auf "Short" (8 Bit) geschaltet. Analog
funktionieren "£rl" und "£rs" fuer die Index-Register. Die Befehle sind nur
fuer den Assembler gedacht, sie werden nicht in Object-Code gewandelt!  
Auffaellig an Listing 2.2 mag der SEI sein. Da im Native Mode auch die Vektoren
fuer die Interrupts an einer anderen Stelle liegen (wo im ROM nichts
passendes steht), sollte man vor dem Umschalten in den Native Mode immer den
IRQ sperren - oder einen eigenen Handler schreiben und das ROM deaktivieren.
Auf die Location der Vektoren werden wir in einem spaeteren Kursteil
natuerlich ausfuehrlich eingehen.


Der sechste Sinn

Wie REP und SEP grundsaetzlich funktionieren, ist relativ schnell zu
verstehen. Etwas schwerer ist es, einen Sinn dafuer zu entwickeln, wann sie
sinnvoll einzusetzen sind, da es immer mehrere Wege gibt, eine Sache zu
realisieren. Obwohl man jetzt sofort Lust hat, den 16-Bit-Modus fuer alles
und jedes zu benutzen, sollte doch offensichtlich sein, dass ein 8-Bit-Akku
fuer manche Aufgaben nach wie vor besser geeignet ist, beispielsweise bei
Manipulation von Zeichen in einem Charset. Alte 6510-Hasen sollen nicht
denken, ihre Routinen wuerden an Effizienz einbuessen, wenn sie, um aus dem
65816 das beste herauszuholen, nicht staendig im 16 Bit Mode sind. Der 8 Bit
Akku und die 8 Bit Index-Register sind schliesslich immer noch da, und
koennen sehr gut fuer das eingesetzt werden, was der 6510 schon erfolgreich
bewerkstelligt hat, bevor es die Option gab, auf 16 Bit umzuschalten. Selbst
im 8 Bit-Modus bietet der 65816 noch jede Menge Vorteile gegenueber dem 6510.
Es ist wichtig eine Art Rhythmus zu entwickeln, einen Sinn dafuer, wann man
umschaltet. Es ist nicht guenstig, andauernd zwischen 8 und 16 Bit hin- und
herzuschalten. Doch zum Glueck programmieren wir ja selbst den Assembler-Code
und lassen ihn nicht durch irgendwelche Compiler generieren, die dann
wer-weiss-was daraus machen - meistens Code, der durch Laenge und Ineffizienz
hervorsticht. Denn es ist offensichtlich, dass eine bestimmte Abfolge an
Operationen oder Berechnungen nicht immer in einer absolut festen Reihenfolge
ausgefuehrt werden muessen! Somit kann man dafuer sorgen, dass so viel wie
moeglich in einem Modus ausgefuehrt wird, bevor man umschaltet. Auf der
anderen Seite solle man sich klar darueber sein, dass ein Umschalten sehr
schnell zu mehr Effizienz fuehrt. Hier eroeffnet sich ein riesiges Gebiet
fuer Tueftler: Indem man die verschiedenen Moeglichkeiten durchgeht und damit
herumexperimentiert, laesst sich bald ein Sinn dafuer entwickeln, wie man am
optimalsten mit der 8/16-Bit-Umschaltung umgeht und das bestmoegliche
rausholt.
 
Gleich und doch anders

Was geht nun vor sich, wenn der Prozessor ein Programm abarbeitet? Wie werden
die neuen Flags beruecksichtigt? Man kann sie als Erweiterung zu den
entsprechenden Opcodes sehen. Angenommen, der Prozessor trifft auf folgenden
Befehl:

object code    instruction
BD 00 20       LDA $2000,X
  
Dieser Befehl bewirkt, wie wir wissen, dass der Akkumulator geladen wird, und
zwar mit dem, was ab der Adresse $2000 plus dem Inhalt des X-Registers steht.
Der Inhalt des X-Registers kann nun 8 oder 16 Bit sein, abhaengig davon, ob
das "X"-Flag gesetzt oder geloescht ist. Auch der Akkumulator kann 8 oder 16
Bit sein, was heisst, dass er mit 8 oder 16 Bit von der angegebenen Adresse
geladen werden kann (also Lowbyte von $2000 und Highbyte von $2001!). Der
Opcode, $BD, ist derselbe wie auf dem 6510. Durch die Flags "M" und "X" sind
dessen Moeglichkeiten aber betraechtlich ausgeweitet. Nehmen wir an, der
Prozessor findet folgende Bytefolge im Speicher:

object code
A9 00 20 A9 90 58

Ist der Akku auf 8 Bit, so ergibt sich:

instruction
LDA #$00 
JSR $90A9
CLI

Ist das "M"-Flag jedoch auf 0, der Akku also im 16-Bit-Modus, so liest der
Prozessor daraus:

instruction
LDA #$2000 
LDA #$5890

$A9 heisst LDA absolut. Im 8 Bit-Mode wird die nachfolgende $00 gelesen und
in den Akku gepackt - damit ist der Befehl abgeschlossen und das naechste
Byte, $20, wird gelesen. $20 steht fuer JSR - nach einem JSR kommen zwei
Byte, hier $A9 als Lowbyte und $90 als Highbyte, das ergibt einen JSR $90A9.
Der Befehl ist zuende, das naechste Byte wird geholt, eine $58. Interpretiert
ist dies der Befehl CLI.
Im 16-Bit-Mode laeuft das ganze dann so: $A9 heisst LDA absolut. Es folgen 16
Bit, also $00 als Lowbyte und $20 als Highbyte. Diese landen im Akku. Der
Befehl ist zuende, das naechste Byte wird geholt: $A9, also wieder LDA
absolut. Wieder wird das Lowbyte fuer den Akku geholt, $90, dann das
Highbyte, eine $58.
Hierum muss man sich beim Entwickeln von Programmen jedoch zum Glueck nicht
kuemmern - der Assembler erledigt alles, vorausgesetzt, Sie haben die
richtigen Pseudo-Opcodes zur Modus-Umschaltung gesetzt. Damit das Programm
dann so abgearbeitet wird, wie Sie es wollen, muessen "M"- und "X"-Flag des
65816 entsprechend mit REP oder SEP richtig gesetzt bzw. geloescht werden.
Haben Sie dieses Prinzip einmal verinnerlicht, steht dem unbegrenzten Einsatz
von 16 Bit nichts mehr im Wege!


Nie wieder Highbyte-Check

Wie mit dem Akku in 16 Bit verfahren wird, haben wir nun gesehen. Listing 2.3
zeigt anhand einer 16-Bit-Addition ein weiteres Beispiel, wie man 16 Bit
einsetzen kann. Noch interessanter wird der Einsatz von 16 Bit natuerlich bei
etwas komplexeren Berechnungen. Als kleines "Schmankerl" zeigen wir in
Listing 2.5 eine waschechte 16-Bit-Multiplikation - Listing 2.4 zeigt dasselbe
in 8 Bit - da werden die Unterschiede deutlich sichtbar.
Doch auch die Index-Register sind nun 16 Bit! Das heisst, mit einem
X-indizierten LDA beispielsweise kann man nun nicht mehr bloss auf einen
Bereich von 256 Bytes zugreiffen, sondern nun auf 65536, also volle 64K!
Somit entfallen beispielsweise die laestigen Ueberpruefungen des Highbytes
bei einer Routine wie in Listing 6. Wie unschwer zu erkennen ist,
funktionieren die bekannten Adressierungsarten wie gewohnt! Uebrigens
verbraucht ein STA $1000,X genau 5 Taktzyklen, egal, ob das Index-Register
einen 8- oder 16-Bit-Inhalt hat! Ist hingegen der Akku 16 Bit breit, ist der
Befehl etwas langsamer - schliesslich werden zwei Byte statt einem
gespeichert. Trotzdem dauert der Befehl dann nur einen Taktzyklus laenger!


Hin- und zurueck

Schaltet man ein 16-Bit-Index-Register auf 8 Bit zurueck, geht dessen
Highbyte unwiederruflich verloren und wird durch eine Null ersetzt. Schaltet
man umgekehrt die Index-Register von 8 auf 16 Bit hoch, so wird der
vorhandene 8-Bit-Wert durch eine Null als Highbyte ergaenzt, geht also nicht
verloren.
Im Gegensatz zu den Index-Registern verhaelt es sich beim Akkumulator so,
dass man ohne Verlust hin- und herschalten kann. Schaltet man den
16-Bit-Akku A auf 8 Bit, wird das Lowbyte der neue 8-Bit-Akku A. Das Highbyte
landet in einem "versteckten" Akkumulator B. B kann man als Anhaengsel" zu A
betrachten. Man kann, obwohl man sich im 8-Bit-Modus befindet, noch darauf
zugreifen: Der Befehl XBA (eXchange B and A) tauscht die Inhalte der beiden.
Somit ist B sehr nuetzlich, wenn man mal einen Wert zwischenspeichern moechte
und kein anderes Register mehr frei ist! Schaltet man den 8-Bit-Akku A auf 16
Bit um, hat der 16-Bit-Akku als Lowbyte den vorigen 8-Bit-Akku, das Highbyte
ist der Inhalt des "versteckten" B-Akkus.
Bestimmte Befehle, die den Akku-Inhalt komplett als 16-Bit-Wert ansehen und
behandeln, enthalten oft ein "C", welches darauf hindeutet, dass die vollen
16 Bit verwendet werden, egal ob sich der Akku im 8- oder 16-Bit-Modus
befindet. Was das fuer Befehle sind und was der 65816 sonst noch fuer
Geheimnisse in sich birgt, lesen Sie in den naechsten Teilen dieses Kurses!





Processor Status Register (in Emulation Mode) 7 6 5 4 3 2 1 0 ----- | | | E |------- Emulation 1=6502 Emulation Mode | | --------------------------------- | | | | | | | | | | N | V | | B | D | I | Z | C |----------- Carry 1=Carry | | | | | | | | | --------------------------------- | | | | | | | | | | | ------------------- Zero 1=Result Zero | | | | | | | | | ---------------- IRQ Disable 1=Disabled | | | | | | | ------------------- Decimal Mode 1=Decimal, 0=Binary | | | | | ------------------ Break Instruction 1=Break caused Interrupt | | | | | ----------------------------------- Overflow 1=Overflow | --------------------------------------- Negative 1=Negative



Processor Status Register (in Native Mode) 7 6 5 4 3 2 1 0 ----- | | | E |------- Emulation 0=Native Mode | | --------------------------------- | | | | | | | | | | N | V | M | X | D | I | Z | C |----------- Carry 1=Carry | | | | | | | | | --------------------------------- | | | | | | | | | | | | | ------------------- Zero 1=Result Zero | | | | | | | | | | | ---------------- IRQ Disable 1=Disabled | | | | | | | | | ------------------- Decimal Mode 1=Decimal, 0=Binary | | | | | | | -------------- Index Register Select 1=8 Bit, 0=16 Bit | | | | | -------------- Memory/Accumulator Select 1=8 Bit, 0=16 Bit | | | ----------------------------------- Overflow 1=Overflow | --------------------------------------- Negative 1=Negative


(w) Malte Mundt
©1999 Go64 Redax! & Count Zero/SCS*TRC for all HTML Stuff

[ Zum 1. Teil ][ Zum Index ][ Zum 3. Teil ]