DEN C64'er AUSGEREIZT (Teil 1)
INTERRUPTKURS
Mehr als 8 Sprites gleichzeitig, Grafiken im Rand des Bildschirms, mehr als 16 Farben, skurrile Verfremdung von Bildern
und eine schier endlos scheinende Flut
von phantastischen Spezialeffekten gehören heutzutage einfach zum Standard der
Programmierkunst auf dem C'64 .
Wer hat da nicht schon einmal mit dem
Gedanken gespielt, sich selbst an die
Programmierung solcher Wunderdinge heranzuwagen.
Nimmt man sich die Zeit, Laufschriften
von Demos durchzulesen oder lauscht man
der hitzigen Unterhaltung eingefleischter Freaks, so stößt man immer wieder auf
Wörter wie Raster-Routine, Sideborder-Sprites, FLD-, FID-, und DYSP-ROUTINEN, die man auch mit der größter Anstrengung
in der angebotenen Fachliteratur einfach
nicht finden kann.
Selbstverständlich gibt es Bücher, die
sich mit Interrupteigenschaften ihres
Gerätes befassen. Sie werden allerdings
feststellen, daß die meisten dieses Thema
sehr kurz abhandeln und in der Regel
ausgerechnet da aufhören, wo es anfängt
spannend zu werden. Das völlige Fehlen
geeigneter Literatur liegt einerseits an
der mangelde Kenntnis des Sachverhaltes
und andererseits an der relativ späten
Entdeckung dieser Fähigkeiten des C'64 .
Es sei hier noch erwähnt, daß es sich
bei einigen Effekten die wir im Laufe
dieses Kurses behandeln werden, nur um
UNBEABSIGTIGTE Nebeneffekte der Chips
handelt, die vom Hersteller nicht geplant
waren und offiziell auch gar nicht existieren dürften. Das es sie aber dennoch
gibt, haben wir einzig und allein der Experimentierfreudigkeit einiger Freaks
zu verdanken.
Leider muß ich zum Verständnis der abgedruckten Programme Grundkennnisse der
Hardware, der Maschinensprache und Erfahrung im Umgang mit einem Assembler
oder einem Monitor voraussetzen, denn einen IRQ von BASIC aus zu programmieren
ist prinzipiell nicht möglich.
ALLGEMEINES ÜBER DIE INTERRUPTTECHNIK!
Was ist ein Interrupt? Wie funktioniert
er und wie kann ich ihn in meinen Programmen einsetzen? Dies sind die Fragen, die in der ersten Folge des Interruptkurses beantwortet werden sollen.
Was ein Interrupt ist, läßt sich einfach
beantworten. Ein Interrupt ist eine gesteuerte Unterbrechung und bedeutet, daß
spezielle Chips oder Befehle die Arbeit
des Rechners jederzeit unterbrechen können und ihn dazu zwingen, andere Routinen
auszuführen. Insgesamt gibt es VIER verschiedene IRQ-Möglichkeiten, die in sich
selbst noch unterteilt sind.
1-RESET 2-BRK (Break) INTERRUPT 3-CIA INTERRUPTS (IRQ-CIA und NMI-CIA) 4-VIDEO INTERRUPT
DER RESET-INTERRUPT
Dieser Interrupt wird gleich nach dem
Einschalten des Computers oder nach dem
drücken einer Resettaste ausgelöst. Ich
möchte aber darauf hinweisen, daß es sich
hierbei nicht um eine Unterbrechung im
eigentlichen Sinne handelt, sondern wie
der Name schon sagt:
to reset= zurücksetzen um einen totalen ABBRUCH eines laufenden
Programmes, gefolgt von einem neuinitialisieren des gesamten Rechners. Mit
dem RESET als Unterbrechung wollen wir
uns im Laufe dieses Kurses auch nicht
weiter beschäftigen.
DER BRK-INTERRUPT
Als nächstes gibt es die Möglichkeit, mit der Hilfe des Assemblerbefehls ' BRK' einen Interrupt durch den Prozessor
selbst auszulösen. Dieser Interrupt wird
auch als Software-IRQ bezeichnet. Da es
sich hierbei gleichzeitig um einen der
einfachsten Unterbrechungsarten handelt, ist sie wie geschaffen, um an einem einfachen Programmbeispiel den gewöhnlichen
Verlauf eines Interrupts zu demonstrieren. Für den BRK sind die folgenden
Adressen von Bedeutung.
$0316 Lo-Byte des BREAK-Vektors $0317 Hi-Byte des BREAK-Vektors
Diese Register befinden sich im RAM und
sind somit veränderbar. Das folgende Programmbeispiel soll dies verdeutlichen.
BRK-Demo:
1000 LDA #$17 Lo-Byte des BRK-Vektors 1002 STA $0316 umstellen. 1005 LDA #$10 Hi-Byte des BRK-Vektors 1007 STA $0317 umstellen. 100A BRK Starten des IRQ -------------------- 100B NOP 100C LDA #$0E Rahmenfarbe rücksetzen 100E STA $D020 rücksetzen 1011 LDA #$06 Hintergrundfarbe 1013 STA $D021 rücksetzen 1016 RTS Rücksprung zu Basic 1017 INC $D020 Rahmenfarbe ändern 101A LDA $DC01 PORT B auslesen 101D CMP #$EF prüfen ob SPACE gedrückt 101F BNE LOOP Nein ?? dann $1017 1021 PLA Y-Reg. vom Stack holen 1022 TAY 1023 PLA X-Reg. vom Stack holen 1024 TAX 1025 PLA Akku vom Stack holen 1026 RTI Interrupt verlassen
Wird dieses Programm mit ' SYS 4096' gestartet, beginnt sich die Rahmenfarbe
fortlaufend zu ändern. Drückt man anschließend die SPACE-Taste, so meldet
sich der Rechner mit ' READY' wieder.
Nachdem wir unsere Routine gestartet
haben, wird als erstes Lound Hi-Byte
des Break-Vektors ( welche Bedeutung jene
Register im einzelnen haben, wird anschließend geklärt) auf die Adresse $1017 gerichtet. Der Interrupt wird ausgelöst, sobald der Prozessor auf den BRK Befehl
stößt. Was dabei im Rechner vorgeht, läßt
sich in vier Punkte gliedern:
1 . Als erstes wird im STATUSREGISTER das
BREAK-FLAG gesetzt, um dem Prozessor
die Art des Interrups mitzuteilen. Anschließend wird Hiund Lo-Byte des
PROGRAMMCOUNTERS auf den Stack abgelegt, d. h. der Prozessor merkt sich die
Adresse, an der er den letzten Befehl
ausgeführt hat. Das Gleiche geschieht
übrigens auch bei einem ' JSR' Befehl.
Dies ist sehr wichtig, denn nur so kann
der Rechner seine Arbeit nach Beendigung des Interrups wieder fortsetzen.
In unserem Beispiel merkt er sich also
die Adresse:$100 A+$0002=$100 C.
Das zu der aktuellen Unterbrechungsadresse noch der Wert $02 dazuaddiert
wird, ist eine Außnahme, die bei anderen
Unterbrechungen nicht vorkommt.
2 . Um die FLAGS zu retten, wird zusätzlich
noch das komplette STATUSREGISTER auf
dem Stack abgelegt.
3 . Anschließend verzweigt der Rechner indirekt über die Adressen $ FFFE/$ FFFF
in die ROM-Routine bei $ FF48 . An dieser
Stelle findet man folgendes Programm
vor.
FF48 PHA Akku auf Stack retten FF49 TXA FF4 A PHA X-Reg. auf Stack retten FF4 B TYA FF4 C PHA Y-Reg. auf Stack retten FF4 D TSX Stackpointer ins X-Reg.
FF4 E LDA $0104, X Statusregister in Akku FF51 AND #$10 BREAK-Flag prüfen FF53 BEQ FF58 nicht gesetzt? Dann FF58 FF55 JMP ($0316) sonst Break-INTERRUPT FF58 JMP ($0314) Timer-IRQ
Diese Routine entscheidet, ob es sich
bei der Art des Interrups um einen
TIMERoder, wie in unserem Fall, um
einen BREAK-INTERRUPT handelt. Handelt
es sich um den Letzteren, dann wird zu
der Routine verzweigt, auf welche die
Register $0316/$0317 zeigen. Da wir nun
zuvor beide Register auf die Adresse
$1017 gerichtet haben, wird nun unsere
eigene IRQ-Routine angesprungen. Diese
macht sich auch augenblicklich durch
ändern der Rahmenfarbe bemerkbar.
4 . Um das Interruptprogramm wieder zu
verlassen, drücken wir einfach die
Space-Taste. Als erstes werden die
Werte für Akku und die beiden Register
X und Y vom Stack geholt. Um den Interrupt endgültig zu beenden, gibt es
den Befehl RTI( Return from Interrupt), was zu deutsch ungefähr soviel bedeutet wie ' Verlassen der Unterbrechungsebene' . Trifft der Prozessor nun auf
einen ' RTI' Befehl, dann werden als
erstes STATUSREGISTER und PROGRMM-COUNTER von Stack geholt und der Programmablauf kann an der Adresse $100 c
fortgesetzt werden.
Im Gegensatz zu anderen IRQ-Arten, bietet der BRK-INTERRUPT in der Praxis relativ wenige Anwendunggebiete. Man könnte
ihn eigentlich problemlos als Unterprogrammbefehl einsetzen, allerdings ist
dafür der ' JSR' Befehl wesentlich besser
geeignet, da seine Handhabung nicht so umständlich ist. Eine wesentliche Rolle
spielt der BRK bei Software-Instrumenten
wie z. B. Maschinensprachemonitore. Hier
werden die beiden Adressen $0316/$0317 so umgestellt, daß sie als Register-Indikatoren dienen.
DER CIA-INTERRUPT
Die folgenden Interrupt-Typen sind da
schon wesentlich interresanter. Hierbei
handelt es sich um sogenannte EXTERNE
Unterbrechungen. In ihrem C-64 gibt es
drei spezielle Chips, die aus einem bestimmten Grund den Prozessor zwingen die
augenblickliche Tätigkeit zu unterbrechen und eine andere Aufgabe auszuführen. Hierzu gehören die beiden CIA-Chips und der Video-Chip.
Selbstverständlich geschehen diese Unterbrechungen nicht einfach aus heiterem
Himmel, sondern können vom Programmierer
kontrolliert und gezielt eingesetzt
werden.
Beim BRK-IRQ konnten wir die Adresse, an der ein IRQ ausgelöst wird, ganz genau
festlegen. Beim Timer-IRQ ist dies nicht
ganz so einfach, weil hier die Unterbrechung nicht mehr vom Prozessor selbst
eingeleitet wird sondern von einem Chip, der gleich zweimal vorhanden ist. Gemeint
ist der ' Complex Interface Adapter', auch
unter der Kurzform CIA bekannt.
Der erste der beiden CIAs wird auch als
' IRQ-TIMER' oder' IRQ-CIA' bezeichnet. Er
besitzt die Basisadresse $ DC00 und wird
in erster Linie zur Verwaltung einiger
Vorgänge im Betriebsystem benutzt.
Den zweiten CIA bezeichnet man als
' NMI-TIMER' . Er besitzt die Basisadresse
$ DD00 und wird außer zur Verwaltung
einer event. vorhandenen RS232 Schnittstelle vom Betriebsystem für keine bestimmte Aufgabe vorgesehen.
Jeder CIA besitzt zwei Intervalltimer, die aus einem 16- Bit Zähler ( read only) und einem 16- Bit Zwischenspeicher ( write
only) bestehen. Daten, die in den Timer geschieben werden, landen im Zwischenspeicher, während die Lesedaten den
augenblicklichen Stand des Zählers angeben. Die beiden Timer können sowohl
unabhängig von einander, als auch im Zusammenhang benutzt werden. Die verschiedenen Betriebsarten erlauben das Erzeugen variabler Zeitverzögerungen.
Ein Intervalltimer ist mit einem Zeitlaufwerk vergleichbar, das kontinuierlich rückwärts zählt. Ist der COUNTDOWN
dieses Zählers abgelaufen, d. h. der Zähler
ist beim Wert Null angelangt, so wird ein
Interrupt ausgelöst. Diese Möglichkeit
wird beispielsweise vom Betriebssystem
gezielt eingesetzt, um in regelmäßigen
Abständen ( jede 1/60 Sekunde) den normalen Programmablauf zu unterbrechen und
indirekt über die Adressen $0314/$0315 in eine ROM-Routine zu springen. Auf
diese Weise wird die interne Uhr ( TI$) gestellt, die Tastatur abgefragt und das
Blinken des Cursors erzeugt.
Da der C64 zwei CIA-Chips besitz, gibt
es also insgesammt vier Intervalltimer, die man völlig unabhängig von einander
oder im Zusammenhang einsetzen kann.
Das folgende Beispielprogramm soll die
Arbeitsweise einer Timer-Unterbrechung, anhand einer nützlichen Anwendung verdeutlichen. Wir wollen einfach mit dem
Joy-Stick in Port 2 ein Sprite über den
Bildschirm bewegen. Allerdings soll dies
ausschließlich im Interrupt geschehen.
1000 SEI ;IRQ-sperren 1001 LDX #$01 ;SPRITE Nr.1 1003 STX $D015 ;einschalten 1006 STX $D027 ;Farbe = weiß 1009 LDA #$3F ;SPRITE DATAS 100b STA $07F8 ;von $3F*64 = $0FC0 holen 100e LDA #$64 ;X-,und Y-Position von 1010 STA $D000 ;Sprite Nr.1 auf $64 1013 STA $D001 ;setzen 1016 LDX #$0E ;Rahmen und 1008 LDY #$06 ;Hintergrund 101a STX $D020 ;auf 'blau' setzen 101d STY $D021 1020 LDX #$36 ;Lo-und Hi-byte 1022 LDY #$10 ;des IRQ-Vektors auf die 1024 STX $0314 ;Interrupt-Routine 1027 STY $0315 ;richten 102a LDX #$25 ;Lo-und Hi-Byte 102c LDY #$40 ;von Intervalltimer A 102e STX $DC04 ;setzen 1031 STY $DC05 1034 CLI ;Interrupt erlauben 1035 RTS ;Rücksprung zu Basic
1036 LDA $ DC00 ; Joy-Stick Register 21039 LSR ; prüfen ob Bit 1 gelöscht
103 A BCS $103 F ; Nein? Dann verzweigen!
103 C DEC $ D001 ; sonst Sprite aufwärts 103 F LSR ; prüfen ob Bit 2 gelöscht
1040 BCS $1045 ; Nein? Dann verzweigen!
1042 INC $ D001 ; sonst Sprite abwärts 1045 LSR ; prüfen ob Bit 3 gelöscht
1046 BCS $104 B ; Nein? Dann verzweigen!
1048 DEC $ D000 ; sonst Sprite nach links 104 B LSR ; prüfen ob Bit 4 gelöscht
104 C BCS $1051 ; Nein? Dann verzweigen!
104 E INC $ D000 ; sonst Sprite nach rechts
1051 JMP $ EA31 ; Sprung zum IRQ-Ende!
Starten sie dieses Programm mit
' SYS 4096', dann erscheint ein Sprite
links im Bildschirm und der Rechner meldet sich mit ' READY' und einem blinkendem
Cursor wieder. Außer einem Sprite und
einem blauen Bildschirm läßt sich auf
den ersten Blick auch nichts ungewöhnliches erkennen. Was man jedoch noch
nicht sehen kann, ist, daß analog zu den Standardfunktionen eine kleine Routine
im Interrupt abläuft, die nur auf ein
Signal vom Joy-Stick wartet.
Bewegen Sie einfach mal den Steuerknüppel und Sie werden merken, daß sich das
Sprite in alle Richtungen steuern läßt.
Was sie hier sehen ist, eine typische, kontrollierte Unterbrechung, wie sie zu
Beginn des Artikels beschrienen wurde.
Man hat den Eindruck, daß Eingaben über
die Tastatur und die Joy-Stick Abfrage, quasi gleichzeitig ablaufen. Daß dies
nicht der Fall ist, werden wir gleich
erkennen.
Die ersten Befehle, bis einschließlich
Adresse'$101 D' initialisieren Sprite und
Hintergrundfarbe und dürften uns keine
Schwierigkeit bereiten. Gleich darauf
werden die Vektoren des Timer-Interrups
umgestellt. Waren bei der Programmierung
des BRK-Interrupts die Adressen $0316 und $0317 von großer Bedeutung, so sind
es beim Timer-Interrupt die Adressen
$0314 und $0315 .
In unserem Demo-Programm richten wir
diesen Vektor auf Adresse $1036 . Auf
diese Weise können wir unsere Joy-Stick
Abfrage einfach in den normalen Verlauf
des Betriebsystems einbinden.
Der voreingestellte Wert in $0314/$0315 ist $ EA31 . An dieser Adresse befindet
sich eine Routine, die im Normalfall
60 mal pro Sekunde angesprungen wird, um
die Uhr weiterzustellen und die Tastatur
abzufragen. Aus diesem Grund sollte zum
Abschluß einer ' seriösen' IRQ-Routine
ein Sprung nach $ EA31 folgen.
Somit hätten wir alles geklärt bis auf
die Funktion der beiden CIA Register
$ DC04/$ DC05 . Zuvor haben wir einiges über
Intervalltimer erfahren und angedeutet, daß man damit Zeitverzögerungen erzeugen
kann.
Gemeint war damit, daß man die Zeit
zwischen den Interruptanforderungen beliebig verändern kann.
Wem die Joy-Stick Abfrage bisher zu
langsam war, der sollte einfach einen
kleineren Wert ausprobieren.
z. B.$1025( statt $4025) Nun ist die Joy-Stick Abfrage viel
flotter. Alle anderen Funktionen wurden
aber ebenfalls beschleunigt und dies
ist in Bezug auf die Cursor Steuerung
eher negativ, da man nicht mehr so genau
schreiben kann.
Der Grund für dieses allgemeine ' Speed
up' liegt daran, daß Intervalltimer A
bereits vom Betriebsystem benutzt wird.
Nun gibt es aber die Möglichkeit, die
Joy-Stick Abfrage von einem anderem
Intervalltimer abarbeiten zu lassen.
Gleich zu Beginn haben wir erfahren, das
der C64 mit Intervalltimer reichlich
bestückt ist, denn immerhin gibt es
gleich vier verschliedene davon.
Weitere Routinen finden Sie auf der
Rückseite der MAGIC-DISK, die man mit
Hilfe eines MONITORS einladen kann.
" TIMER-DEMO 2" ist identisch mit dem
zuletzt besprochenen Listing, nur das
der Joy-Stick über Intervalltimer B abgefragt wird. Zu diesem Zweck, mußten
folgende Befehle hinzugefügt werden.
1034 LDX #$80 ;Lo-und Hi-Byte von 1036 LDY #$10 ;Intervalltimer B 1038 STX $DC06 ;setzen 103B STY $DC07 103E LDA #%10000011 ;IRQ-Quelle 1040 STA $DC0D ;In ICR schreiben 1043 LDA #%00010001 ;Intervalltimer B 1045 STA $DC0F ;starten
Im " TIMER-DEMO 3" haben wir die letzte
Routine nochmals erweitert. Intervalltimer A des NMI-Timers wurde als dritte
Unterbrechungsanforderung hinzugezogen, um ein Musikstück im Interrupt abspielen
zu lassen. Sie sehen das die Interruptprogrammierung eine Fülle von Möglichkeiten bietet, die wir für uns nutzen können. Wenn sie auch noch etwas Zeit
zum Tüfteln investieren, finden Sie bestimmt noch eine Menge weiterer Möglichkeiten.
Damit schließen wir zunächst mal die
Einführung in die Unterbrechungsprogrammierung ab. Da wir in diesen Teil
schon fast alle IRQ-Arten angesprochen
haben, wollen wir uns in der nächsten
und allen weiteren Ausgaben nur noch mit
dem wichtigsten Typ des Interrupts
befassen: dem RASTER-Interrupt.
(IVO HERZEG)