DEN C64'er AUSGEREIZT (Teil 1)
INTERRUPTKURS -------------
Mehr als 8 Sprites gleichzeitig, Grafi- ken 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 he- ranzuwagen. Nimmt man sich die Zeit, Laufschriften von Demos durchzulesen oder lauscht man der hitzigen Unterhaltung eingefleisch- ter 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 exi- stieren dürften. Das es sie aber dennoch gibt,haben wir einzig und allein der Ex- perimentierfreudigkeit einiger Freaks zu verdanken. Leider muß ich zum Verständnis der abge- druckten Programme Grundkennnisse der Hardware, der Maschinensprache und Er- fahrung 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 Pro- grammen einsetzen? Dies sind die Fragen, die in der ersten Folge des Interrupt- kurses beantwortet werden sollen. Was ein Interrupt ist,läßt sich einfach beantworten. Ein Interrupt ist eine ge- steuerte Unterbrechung und bedeutet, daß spezielle Chips oder Befehle die Arbeit des Rechners jederzeit unterbrechen kön- nen und ihn dazu zwingen,andere Routinen auszuführen. Insgesamt gibt es VIER ver- schiedene 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 neuini- tialisieren 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 ein- fachen Programmbeispiel den gewöhnlichen Verlauf eines Interrupts zu demonstrie- ren. 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 Pro- grammbeispiel 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 an- schließend die SPACE-Taste, so meldet sich der Rechner mit 'READY' wieder. Nachdem wir unsere Routine gestartet haben, wird als erstes Lo- und Hi-Byte des Break-Vektors (welche Bedeutung jene Register im einzelnen haben, wird an- schließ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. An- schließend wird Hi- und Lo-Byte des PROGRAMMCOUNTERS auf den Stack abge- legt, 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 Beendi- gung des Interrups wieder fortsetzen. In unserem Beispiel merkt er sich also die Adresse: $100A+$0002=$100C. Das zu der aktuellen Unterbrechungs- adresse 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 in- direkt ü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 FF4A PHA X-Reg.auf Stack retten FF4B TYA FF4C PHA Y-Reg.auf Stack retten FF4D TSX Stackpointer ins X-Reg. FF4E 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 TIMER- oder, 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 In- terrupt endgültig zu beenden, gibt es den Befehl RTI(Return from Interrupt), was zu deutsch ungefähr soviel bedeu- tet wie 'Verlassen der Unterbrechung- sebene'. Trifft der Prozessor nun auf einen 'RTI' Befehl, dann werden als erstes STATUSREGISTER und PROGRMM- COUNTER von Stack geholt und der Pro- grammablauf kann an der Adresse $100c fortgesetzt werden. Im Gegensatz zu anderen IRQ-Arten, bie- tet der BRK-INTERRUPT in der Praxis re- lativ wenige Anwendunggebiete.Man könnte ihn eigentlich problemlos als Unterpro- grammbefehl 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 be- stimmten Grund den Prozessor zwingen die augenblickliche Tätigkeit zu unter- brechen und eine andere Aufgabe auszu- führen. Hierzu gehören die beiden CIA- Chips und der Video-Chip. Selbstverständlich geschehen diese Un- terbrechungen 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 Unter- brechung 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 Schnitt- stelle vom Betriebsystem für keine be- stimmte 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 Zwischen- speicher, während die Lesedaten den augenblicklichen Stand des Zählers an- geben. Die beiden Timer können sowohl unabhängig von einander, als auch im Zu- sammenhang benutzt werden. Die verschie- denen Betriebsarten erlauben das Er- zeugen variabler Zeitverzögerungen. Ein Intervalltimer ist mit einem Zeit- laufwerk vergleichbar, das kontinuier- lich 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 nor- malen 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 ver- deutlichen. 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 2 1039 LSR ;prüfen ob Bit 1 gelöscht 103A BCS $103F ;Nein? Dann verzweigen! 103C DEC $D001 ;sonst Sprite aufwärts 103F 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 $104B ;Nein? Dann verzweigen! 1048 DEC $D000 ;sonst Sprite nach links 104B LSR ;prüfen ob Bit 4 gelöscht 104C BCS $1051 ;Nein? Dann verzweigen! 104E 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 mel- det 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öhn- liches 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üp- pel 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'$101D' 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 60mal 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 be- liebig 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 ab- gefragt 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. Intervall- timer A des NMI-Timers wurde als dritte Unterbrechungsanforderung hinzugezogen, um ein Musikstück im Interrupt abspielen zu lassen. Sie sehen das die Interrupt- programmierung eine Fülle von Möglich- keiten bietet, die wir für uns nutzen können. Wenn sie auch noch etwas Zeit zum Tüfteln investieren, finden Sie be- stimmt noch eine Menge weiterer Möglich- keiten. Damit schließen wir zunächst mal die Einführung in die Unterbrechungspro- grammierung 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)