Interrupt-Kurs
"Die Hardware ausgetrickst..."
(Teil 1)
----------------------------------------
Wer kennt sie nicht, die tollen Effekte,
die die großen Alchimisten der Program-
mierkunst aus unserem guten alten "Brot-
kasten" herauszaubern: mehr als 8 Spri-
tes gleichzeitig auf dem Bildschirm,
Side- und Topborderroutinen, die die
Grenzen des Bildschirmrahmens sprengen,
sich wellenförmig bewegende Logos, Gra-
fikseiten, die sich in atemberaubender
Geschwindigkeit zusammenfalten, sowie
schillernd bunte 256-farbige Bilder. Man
könnte diese Liste noch bis ins Unendli-
che weiterführen und sie würde dennoch
die vollständig sein. In der Tat - was
manche Programmierer aus den Silizuimmo-
lekülen der Hardware des C64 herauskit-
zeln hat schon so manch Einen zum Stau-
nen gebracht. Hätte Commodore zur Mark-
teinführung des C64 schon gewusst, was
für eine Wahnsinnsmaschine sie da gebaut
hatten, hätten sich die Entwickler wahr-
scheinlich selbst an den Kopf gefasst.
All diese Effekte sind nämlich absolut
unbeabsichtigt unserer "Dampfmaschine"
der Computerwelt eingepflanzt worden,
sondern verdanken ihre Existenz einzig
und allein der unermüdlichen Ausdauer
der Freaks und Coder, die in nächtelan-
ger Herumtüftelei die Software dazu ent-
wickelten, die den C64 weitaus länger
leben ließ, als man je geglaubt hätte.
"Rasterinterrupt" heißt das Zauberwort
das, einem "Sesam öffne Dich" gleich,
dem Programmierer das Tor zur wunderba-
ren Welt der Computer-Effekte aufstößt.
Und um genau diese Effekte soll sich in
diesem Kurs alles drehen. Die nächsten
Monate sollen Sie hier erfahren, wie man
sie programmiert, und wir werden versu-
chen Ihnen das Grundwissen zu vermit-
teln, neue Routinen selbst entwickeln zu
können.
Im ersten Teil dieses Kurses wollen wir
uns nun zunächst um die Grundlagen küm-
mern, und den Fragen "Was ist ein Inter-
rupt?", "Wo kommt er her?" und "Was pas-
siert während eines Interrupts?" auf den
Grund gehen. Leider müssen wir Sie auch
darauf hinweisen, daß zur Programmierung
von Interrupts die gute Kenntnis der
Maschinensprache sowie des Befehlssatzes
des 6510-Prozessors (so wie er im 64er
seinen Dienst tut) Grundbedingung ist.
Desweiteren sollten Sie einen guten
Speichermonitor, bzw. Disassembler zur
Hand haben, da wir der Einfachheit hal-
ber alle Programmbeispiele als direkt
ausführbaren Code der Magic Disk beifü-
gen werden. Ein gutes Hilfsmittel dieser
Art ist z.B. der, mittlerweise sehr weit
verbreitete, "SMON".
1) WAS SIND INTERRUPTS?
Das Wort "Interrupt" kommt aus dem En-
glischen und bedeutet "Unterbrechung".
Und nichts anderes tut nun ein Inter-
rupt: er unterbricht den Prozessor bei
seiner momentanen Arbeit. Das tut er
natürlich nicht einfach aus heiterem
Himmel. Vielmehr hat der Programmierer
die Möglichkeit bestimmte Bedingungen
anzugeben, die einen Interrupt auslösen
sollen. Auf diese Weise kann man ganz
genau den Zeitpunkt bestimmen, zu dem
ein Interrupt auftreten soll. Nun wird
der Prozessor jedoch nicht nur einfach
angehalten, wenn er eine Interruptanfor-
derung bekommt. Da das Ganze ja auch
einen Nutzen haben soll, kann natürlich
ein ganz bestimmtes Programm von ihm
abgearbeitet werden, das auf das Inter-
ruptereignis reagieren soll.
Bevor wir uns jedoch in diese Dinge
stürzen, wollen wir erst einmal klären,
welche Interrupts der 6510 versteht. Er
kennt insgesamt vier Unterbrechungsty-
pen, die von ihm jeweils unterschiedlich
behandelt werden. Die Unterscheidung
wird ihm schon durch seinen hardwaremä-
ßigen Aufbau ermöglicht. Er verfügt näm-
lich über drei Eingangsleitungen, die
die entsprechenden Interrupts bezeichnen
(der vierte Interrupt ist ein besonde-
rer, den wir weiter unten besprechen
werden). Die Chips um den Prozessor he-
rum sind nun mit diesen Interrupt-
Leitungen verbunden, und können ihm so
das Eintreten eines Unterbrechungsereig-
nisses mitteilen. Sie sehen also, daß
Interrupts hardwaremäßig ausgelöst wer-
den. Um nun eine Unterbrechung zu einem
bestimmten Zeitpunkt auftreten lassen zu
können, sollte man sich ebenfalls in der
Programmierung der Hardware auskennen.
Und diese wollen wir in diesem Kurs
natürlich auch ansprechen.
Kommen wir nun jedoch zu den vier Inter-
rupttypen. Der Erste von ihnen ist wohl
Einer der einfachsten. Es handelt sich
um den "RESET", der von keinem externen
Chip, sondern vielmehr von einem "exter-
nen" Menschen ausgelöst wird. Er dient
dazu, den Computer wieder in einen Nor-
malzustand zu versetzen. Da der Prozes-
sor selbst nicht merkt, wann er Mist
gebaut hat und irgendwo hängt, muß ihm
der Benutzer das durch Drücken eines
RESET-Tasters mitteilen. Hieraufhin
springt er dann automatisch in ein spe-
zielles Programm im Betriebssystem-ROM
und stellt den Einschaltzustand des
Rechners wieder her. Ein Reset-Taster
ist demnach also direkt mit dem Reset-
Interrupteingang des Prozessors verbun-
den.
Der zweite Interrupttyp heißt "NMI" und
kann ausschließlich von CIA-B des C64
ausgelöst werden. Dies ist ein speziel-
ler Chip, der die Kommunikation zwischen
externen Geräten und 6510 ermöglicht.
Desweiteren ist die 'RESTORE'-Taste di-
rekt mit dem NMI-Eingang des Prozessors
verbunden. Drückt man sie, so wird eben-
falls ein NMI ausgelöst. Auch hier
springt der 6510 automatisch in eine
spezielle Routine des ROMs und führt ein
NMI-Programm aus. Es prüft, ob zusätz-
lich auch noch die "RUN/STOP"-Taste
gedrückt wurde und verzweigt bei positi-
ver Abfrage in die Warmstartroutine des
BASIC-Interpreters.
Der dritte Interrupt ist der wohl am
häufigsten benutzte. Er heißt "IRQ" und
wird von CIA-A, dem Zwilling von CIA-B,
sowie dem Videochip (VIC) ausgelöst.
Gerade weil der VIC diesen Interrupt
bedient, ist dies derjenige, mit dem wir
in diesem Kurs am meisten arbeiten wer-
den.
Der vierte und letzte Interrupt, ist
derjenige unter den vieren, der keine
Leitung zum Prozessor hat. Das liegt
daran, daß er ein softwaremäßiger Inter-
rupt ist, mit dem sich der Prozessor
quasi selbst unterbricht. Er wird aus-
gelöst, wenn der 6510 die "BRK"-
Anweisung ausführen soll. Demnach heißt
er auch "BRK"-Interrupt. Er ist eine
sehr einfache Unterbrechung, die eigent-
lich sehr selten verwendet wird, da sie
ja viel bequemer durch ein "JMP" oder
"JSR" ersetzt werden kann. Dennoch kann
er von Nutzen sein, z.B. wenn man einen
Debugger programmiert. Ebenso kann über
den BRK z.B. in einen Speichermonitor
verzweigt werden, der so die Register
oder einen bestimmten Speicherbereich zu
einem bestimmten Punkt im Programm an-
zeigen kann. Er unterscheidet sich von
den anderen Interrupts lediglich darin,
daß er softwaremäßig ausgelöst wird.
2) IRQ UND NMI - EIN UNGLEICHES PAAR
Diese beiden Interrupttypen sind für uns
die Interresantesten, da mit Ihnen Un-
terbrechungsereignisse der Hardware ab-
gefangen werden, die wir ja exzessiv
programmieren werden. Außer, daß sie
beide verschiende Quellen haben, unter-
scheiden Sie sich auch noch in einem
weiteren Punkt: während der NMI IMMER
ausgelöst wird, wenn ein Unterbrechung-
sereignis eintritt, kann der IRQ "mas-
kiert", bzw. "abgeschaltet" werden. Dies
geschieht über den Assemblerbefehl
"SEI", mit dem das Interruptflag des
Statusregisters gesetzt wird. Ist dieses
Flag nun gesetzt, und es tritt ein IRQ-
Interruptereignis ein, so ignoriert der
Prozessor schlichtweg das Auftreten die-
ser Unterbrechung. Dies tut er solange,
bis er durch den Asseblerbefehl "CLI"
die Instruktion erhält, das Interrupt-
Flag wieder zu löschen. Erst dann rea-
giert er wieder auf eintretende Unter-
brechungen. Dies ist ein wichtiger Um-
stand, den wir auch später bei unseren
IRQ-Routinen beachten müssen.
3) INTERRUPTVEKTOREN - WEGWEISER FÖR DEN
PROZESSOR
Was geschieht nun, wenn der 6510 eine
der oben genannten Interruptanforder-
ungen erhält? Er soll also seine mo-
mentane Arbeit unterbrechen, um in die
Interrupt-Routine zu springen. Damit er
Nach Beendingung des Interrupts wieder
normal fortfahren kann muß er jetzt ei-
nige Schitte durchführen:
1) Ist der auftretende Interrupt ein
BRK, so wird zunächst das dazugehö-
rige Flag im Statusregister gesetzt.
2) Anschließend werden High- und Lowbyte
(in dieser Reihenfolge) des Pro-
grammzählers, der die Speicheradresse
des nächsten, abzuarbeitenden Befehls
beinhaltet, auf den Stack geschoben.
Dadurch kann der 6510 beim Zurückkeh-
ren aus dem Interrupt wieder die
ursprüngliche Programmadresse ermit-
teln.
3) Nun wird das Statusregister auf den
Stack geschoben, damit es bei Been-
dingung des Interrupts wiederherge-
stellt werden kann, so daß es densel-
ben Inhalt hat, als bei Auftreten der
Unterbrechung.
4) Zuletzt holt sich der Prozessor aus
den letzten sechs Bytes seines Adres-
sierungsbereiches (Adressen $FFFA-
$FFFF) einen von drei Sprungvektoren.
Welchen dieser drei Vektoren er aus-
wählt hängt von der Art des Inter-
rupts ab, der ausgeführt werden soll.
Hierzu eine Liste mit den Vektoren:
Adresse Interrupt Sprungadr.
------------------------------------
$FFFA/$FFFB NMI $FE43
$FFFC/$FFFD RESET $FCE2
$FFFE/$FFFF IRQ/BRK $FF48
Da diese Vektoren im ROM liegen, sind
sie schon mit bestimmten Adressen vorbe-
legt, die die Betriebssystem-Routinen
zur Interruptverwaltung anspringen. Wir
wollen nun einmal einen Blick auf diese
Routinen werfen. Da der RESET keine für
uns nützliche Unterbrechung darstellt,
wollen wir ihn jetzt und im Folgenden
weglassen. Kommen wir zunächst zu der
Routine für die IRQ/BRK-Interrupts. Wie
Sie sehen haben diese beiden Typen den-
selben Sprungvektor, werden also von der
selben Service-Routine bedient. Da diese
jedoch auch einen Unterschied zwischen
den beiden machen soll hat sie einen
speziellen Aufbau. Hier einmal ein ROM-
Auszug ab der Adresse $FF48:
FF48: PHA ;Akku auf Stapel
FF49: TXA ;X in Akku und
FF4A: PHA ; auf Stapel
FF4B: TYA ;Y in Akku und
FF4C: PHA ; auf Stapel
FF4D: TSX ;Stackpointer nach
FF4E: LDA $0104,X ;Statusreg. holen
FF51: AND #$10 ;BRK-Flag ausmas-
kieren
FF53: BEQ $FF58 ;Nicht gesetzt also
überspringen
FF55: JMP ($0316) ;Gesetzt, also Öber
Vektor $0316/$0317
springen
FF58: JMP ($0314) ;Öber Vektor $0314/
$0315 springen
Dieses kleine Programm rettet zunächst
einmal alle drei Register, indem Sie sie
einzeln in den Akku holt und von dort
auf den Stack schiebt ($FF48-$FF4C).
Dies ist notwendig, da in der Interrup-
troutine die Register je ebenfalls be-
nutzt werden sollen, und so die ursprün-
glichen Inhalte beim Verlassen des In-
terrupts wiederhergestellt werden können
(durch umgekehrtes zurücklesen). Ab
Adresse $FF4D wird nun eine Unterschei-
dung getroffen, ob ein BRK- oder IRQ-
Interrupt aufgetreten ist. Ist ersteres
nämlich der Fall, so muß das BRK-Flag im
Statusregister, das bei Auftreten der
Unterbrechung vom Prozessor auf den
Stack geschoben wurde, gesetzt sein.
Durch Zugriff auf den Stack, mit dem
Stackpointer als Index in X, wird nun
das Statusregister in den Akku geholt.
Dies ist übrigens die Einzige Methode,
mit der das Statusregister als Ganzes
abgefragt werden kann. Natürlich geht
das immer nur aus einem Interrupt he-
raus. Das BRK-Flag ist nun im 4. Bit des
Statusregisters untergebracht. Durch
eine UND-Verknüfung mit diesem Bit kann
es aus dem Statusregisterwert isoliert
werden. Ist der Akkuinhalt nun gleich
null, so war das BRK-Flag nicht gesetzt
und es muß daher ein IRQ vorliegen. In
dem Fall wird auf den JMP-Befehl an
Adresse $FF58 verzweigt. Im anderen Fall
wird der JMP-Befehl an Adresse $FF55
ausgeführt.
Wie Sie sehen springen diese beiden Be-
fehle über einen indirekten Vektor, der
beim IRQ in den Adressen $0314/$0315,
beim BRK in $0316/$0317 liegt. Da diese
Vektoren im RAM liegen, können Sie auch
von uns verändert werden. Das ist also
auch der Punkt, an dem wir unsere Inter-
rupts "Einklinken" werden. Durch die
Belegung dieser Vektoren mit der Adresse
unser eigenen Interruptroutine können
wir den Prozessor also zu dieser Routine
umleiten.
Werfen wir nun noch einen Blick auf die
NMI-Routine, die durch den Vektor bei
$FFFA/$FFFB angesprungen wird. Sie ist
noch einfacher aufgebaut und besteht
lediglich aus zwei Befehlen:
FE43: SEI ;IRQs sperren
FE44: JMP ($0318) ;Öber Vektor $0318/
$0319 springen
Hier wird lediglich der IRQ geperrt und
anschließend über den Vektor in $0318/
$0319 gesprungen. Die Akku, X- und Y-
Regsiter werden NICHT gerettet. Das muß
unsere Interruptroutine selbst tun.
Zusammenfassend kann man also sagen, daß
beim Auslösen eines Interrupts jeweils
über einen der drei Vektoren, die im
Bereich von $0314-$0319 stehen, gesprun-
gen wird. Der angesprungene Vektor ist
dabei interruptabhängig. Hier nochmal
eine Öbersicht der Interruptvektoren im
genannten Bereich:
Adressen Interrupt Zieladr.
---------------------------------
$0314/$0315 IRQ $EA31
$0316/$0317 BRK $FE66
$0318/$0319 NMI $FE47
Diese Vektoren werden bei einem Reset
mit Standardwerten vorbelegt. Hierbei
wird dann die jeweilige Standardroutine
des Betriebssystems angesprungen, die
den entsprechenden Interrupt bearbeiten
soll. Möchten wir eigene Interrupts ein-
binden, so müssen wir lediglich die
Zieladresse der Vektoren auf den Anfang
unserer Routine verbiegen.
4) PROGRAMMBEISPIELE
Um das Prinzip des Einbindens eines ei-
genen Interrupts kennenzulernen, wollen
wir nun einmal einen eigenen Interrupt
programmieren. Es handelt sich dabei um
den einfachsten von allen, den BRK-
Interrupt. Hier einmal ein Programmli-
sting, daß Sie als ausführbares Programm
auch auf der Rückseite dieser MD unter
dem Namen "BRK-DEMO1" finden. Sie müssen
es absolut (mit ",8,1") in den Speicher
laden, wo es dann ab Adresse $1000 (dez.
4096) abgelegt wird. Gestartet wird es
mit "SYS4096". Sie können es sich auch
mit Hilfe eines Disassemblers gerne ein-
mal ansehen und verändern:
;*** Hauptprogramm
1000: LDX #$1C ;BRK-Vektor auf
1002: LDY #$10 ; $101C verbiegen.
1004: STX $0316
1007: STY $0317
100A: BRK ;BRK auslösen
100B: NOP ;Füll-NOP
100C: LDA #$0E ;Rahmenfarbe auf dez.14
100E: STA $D020 ; zurücksetzen
1011: LDX #$66 ;Normalen BRK-Vektor
1013: LDY #$FE ; ($FE66) wieder
1015: STX $0316 ; einstellen
1018: STY $0317
101B: RTS ;Und ENDE!
;*** Interruptroutine
101C: INC $D020 ;Rahmenfarbe hochzählen
101F: LDA $DC01 ;Port B lesen
1022: CMP #$EF ;SPACE-Taste gedrückt?
1024: BNE LOOP ;Nein, also Schleife
1026: PLA ;Ja, also Y-Register
1027: TAY
1028: PLA ;X-Register
1029: TAX
102A: PLA ;u.Akku v.Stapel holen
102B: RTI ;Interrupt beenden.
In den Adressen $1000-$100A setzen wir
zunächst einmal Low- und Highbyte des
BRK-Vektors auf Adresse $101C, wo unsere
BRK-Routine beginnt. Direkt danach wird
mit Hilfe des BRK-Befehls ein Interrupt
ausgelöst, der, wie wir ja mittlerweile
wissen, nach Retten der Prozessorregi-
ster über den von uns geänderten BRK-
Vektor auf die Routine ab $101C springt.
Selbige tut nun nichts anderes, als die
Rahmenfarbe des Bildschirms um den Wert
1 hochzuzählen, und anschließend zu ver-
gleichen, ob die 'SPACE'-Taste gedrückt
wurde. Ist dies nicht der Fall, so wird
wieder zum Anfang verzweigt, so daß
ununterbrochen die Rahmenfarbe erhöht
wird, was sich durch ein Farbschillern
bemerkbar macht. Wird die 'SPACE'-Taste
nun endlich gedrückt, so kommen die fol-
genden Befehle zum Zuge. Hier holen wir
die Prozessorregister, die von der Be-
triebssystem-Routine in der Reihenfolge
Akku, X, Y auf dem Stack abgelegt wur-
den, wieder umgekehrt zurück. Das ab-
schließende "RTI" beendet den Interrupt.
Diese Answeisung veranlasst den Prozes-
sor dazu, den alten Programmzähler, so-
wie das Statusregister wieder vom Stapel
zu holen, und an die Stelle im Hauptpro-
gramm zurückzuspringen, an der der BRK
ausgelöst wurde. Dies ist logischerweise
der Befehl direkt nach dem BRK-Kommando.
So sollte man normalerweise denken, je-
doch nimmt BRK eine Sonderstellung dies-
bezüglich ein. Bei IRQs und NMIs wird
tatsächlich der Befehl nach dem zuletzt
bearbeiten wieder ausgeführt, jedoch
wird beim BRK der Offset 2 auf den Pro-
grammzähler hinzuaddiert, weshalb nach
Beendigung des Interrupts ein Byte hin-
ter den BRK-Befehl verzweigt wird. Dem-
nach dient der NOP-Befehl, der nach dem
BRK kommt, lediglich dem Angleichen an
die tatsächliche Rücksprungadresse. Er
wird nie ausgeführt, da der Prozessor ja
an Adresse $100C weiterfährt. Hier nun
setzen wir die Rahmenfarbe wieder auf
das gewohnte Hellblau und geben dem
BRK-Vektor wieder seine Ursprüngliche
Adresse zurück. Würden wir das nicht
tun, so würde beim nächsten BRK-Befehl,
der irgendwo ausgeführt wird, automa-
tisch wieder zu unserem Bildschirmflak-
kern verzweigt werden. Hier jedoch würde
der Computer unter Umständen nicht mehr
aus dem Interrupt zurückkehren können,
weil wir ja nicht wissen, welche Befehle
hinter dem auslösenden BRK standen (wenn
es nicht der unseres Programmes an
Adresse $100A war).
LADEN SIE NUN DEN 2. TEIL DES KURSES !