Spiele-Kurs für Assemblerprogrammierer
(Teil 2)

64'er, Ausgabe 7/Juli 1989

Kein professioneller Programmierer kommt heute mehr ohne sie aus: Rasterzeilen-Interrupts sind das A und O eines jeden Spiels.

Im ersten Teil dieses Kurses beschäftigten wir uns hauptsächlich mit der Theorie der Spieleprogrammierung. Heute wollen wir zur Praxis übergehen. Als Einstiegsthema bietet sich hier die Interrupt-Programmierung geradezu an, da sie die Grundlage jeden professionellen Spiels darstellt.

Einige werden jetzt sicher fragen: »Was ist denn ein Interrupt überhaupt?« Nun, ein Interrupt (engl.: Unterbrechung) veranlaßt den Prozessor, sein zur Zeit laufendes Programm zu unterbrechen und ein anderes Programm zu bearbeiten. Ist dieses zweite Programm (die Interruptroutine) beendet, kehrt er zum unterbrochenen Programm zurück und setzt es dort fort, wo er es verlassen hatte. Für die Programmierung braucht man Interrupts hauptsächlich, um ständig wiederkehrende Tätigkeiten des Computers abzuwickeln. So zum Beispiel das Steuern von Sprites, das Spielen eines Musikstückes oder das Aufteilen des Bildschirms in verschiedene Bereiche. Heute wollen wir uns zunächst mit Interrupts im allgemeinen und anschließend mit den sogenannten »Rasterzeilen-Interrupts« beschäftigen.

Ein Interrupt ist eigentlich gar nichts Besonderes. In Ihrem C64 wird 60mal pro Sekunde ein solcher Interrupt ausgelöst. Das Hauptprogramm wird also 60mal pro Sekunde unterbrochen und mit einem anderen Programm fortgesetzt. Dieses Programm, die Interruptroutine, tut all das was der Computer »von selbst« abwickeln soll. Dies geht vom Cursorblinken über die Tastaturabfrage bis hin zu Erhöhung der Variabeln TI und TI$. Es arbeiten also zwei Programme scheinbar gleichzeitig!

Ein Interrupt muß nun irgendwie ausgelöst werden. Im Regelfall besorgt dies der CIA 1 (Complex Interface Adapter 1). Dieser Baustein enthält unter anderem einen Timer (Zeitgeber), der, wenn auf Null heruntergezählt wurde, einen Impuls an den Prozessor schickt und diesen dazu veranlaßt, das laufende Programm zu unterbrechen. Danach wird der Timer automatisch mit dem alten Startwert geladen und erneut heruntergezählt. Wurde ein Interrupt ausgelöst, beendet der Prozessor die Bearbeitung des letzten Machinenbefehls, schiebt das Statusregister und den Programmzähler auf den Stack und springt in die Interruptroutine, in der zunächst alle weiteren Prozessorregister ebenfalls auf den Stack geschoben werden. Am Ende jeder Interruptroutine müssen die auf dem Stack gespeicherten Register zurückgeholt werden, dann wird die Routine mit RTI beendet.

Tippen Sie nun folgende Zeile ein:

POKE 56325,25

Wie Sie sehen, blinkt der Cursor um ein Vielfaches schneller als vorher.

POKE 56325,100

Der Cursor blinkt nun deutlich langsamer.

Was haben wir nun mit diesen Befehl getan? In der Speicherzelle 56325 befindet sich das Hi-Byte des Timers A von CIA 1. Wir haben mit den POKE-Befehlen den Startwert des Timers verändert. POKEn wir zum Beispiel einen großen Wert, dauert es länger, bis der Timer vom Systemtakt heruntergezählt wird, der Interrupt wird seltener ausgelöst.

Nach einem Reset ist Timer A auf 60 Interrupts pro Sekunde eingestellt.

Es gibt beim C64 zwei Arten von Interrupts. Den IRQ (InterruptReQuest) und den NMI (Non-Maskable-Interrupt). Der IRQ läßt sich durch Setzen des I-Flags im Statusregister verhindern (maskieren). Möchten Sie also nicht, daß Ihr Programm durch einen Interrupt unterbrochen wird, setzen Sie dieses Flag einfach mit dem Befehl SEI (Set Interrupt disable flag). Der NMI hingegen läßt sich nicht durch Setzen eines Flags verhindern. Ein NMI kann zum Beispiel mit dem CIA 2 (dem zweiten CIA in C64) und mit der RESTORE-Taste ausgelöst werden. Man benötigt ihn zum Beispiel für die Abfrage externer Meßgeräte. NMIs sollen uns in diesem Kurs nicht interessieren. Viel wichtiger für Spiele sind IRQs. Ein IRQ läßt sich durch den CIA 1 und den VIC (Video Interface Controller = Videochip) auslösen. Der CIA 1 ist, wie schon gesagt, die »normale« IRQ Quelle, der VIC hingegen ist ein Sonderfall. Er ist als IRQ Quelle für die Spieleprogrammierung aber besonders interessant. Da dessen Bedienung allerdings kompliziert ist, mach wir Sie zunächst mit der »normalen« IRQ-Programmierung vertraut, sprich: IRQs durch den CIA.

Wenn der Prozessor ein IRQ-Signal vom CIA bekommt, arbeitet er den letzten Befehl ab, speichert das Statusregister und den Programmzähler auf dem Stack und führt dann automatisch den Maschinenbefehl JMP ($FFFE) aus. In den Registern $FFFE und $FFFF steht ein Pointer mit dem Wert $FF48. Ab $FF48 befindet sich im ROM im wesentlichen folgende Befehle:

 PHA
 TXA
 PHA
 TYA
 PHA
 JMP ($0314)

Nachdem die Register in der Reihenfolge Akku, X-Register, Y-Register auf den Stack gerettet worden sind, wird ein indirekter Sprung über den Vektor $0314 durchgeführt. Gewöhnlich hat dieser Vektor den Wert $EA31, so daß der Sprung JMP ($0314) direkt in die eigentliche IRQ-Routine führt (Tastaturabfrage usw.). Hier kann der Programmierer nun eingreifen. Während sich der Vektor bei $FFFE nicht ändern läßt, da er im ROM steht, kann der Vektor bei $0314 »verbogen« werden.

Schreiben wir nun in diese Speicherzelle den Start unserer eigenen IRQ-Routine, wird diese 60mal pro Sekunde aufgerufen.

Tippen Sie Listing 1 mit Hilfe des Makro-Assemblers HYPRA-ASS ein.

Starten Sie den Assembler mit den Befehl RUN. Ist die Assemblierung beendet, starten Sie das Maschinenprogramm mit SYS 49152.

Es verändert sich nun ständig das linke obere Zeichen auf dem Bildschirm. Es erscheint zwar die READY-Meldung, aber der Cursor ist nicht da. Warum nicht?

Die Zeilen 100 bis 160 dienen dazu, den IRQ-Vektor $0314 auf die eigene Routine zu stellen. Der Ihnen vielleicht noch unbekannte Befehl SEI dient dazu, das I-Flag im Statusregister zu setzen. Ist dieses Flag gesetzt, werden keine IRQs mehr ausgeführt. Dieser Befehl ist hier unbedingt notwendig, denn falls ein IRQ auftritt, wenn gerade erst das LO-Byte des Vektors verändert wurde, kommt es zu einer Katastrophe, weil der Computer nun ins Leere springt und abstürzt!

In Zeile 150 wird der IRQ durch den Befehl CLI wieder zugelassen. Dieser Befehl löscht das I-Flag.

Sehen wir uns nun die IRQ-Routine »IRQNEU« an. In Zeile 200 wird die Speicherzelle 1024 inkrementiert. Bei 1024 beginnt normalerweise das Video-RAM, folglich verändert sich das linke obere Zeichen. In Zeile 210 wird das Register $DC0D ausgelesen. Durch das Auslesen (!) dieses Registers wird das IRQ-Flag im CIA gelöscht. Das ist unbedingt notwendig, da der CIA sonst permanent einen IRQ auslösen würde, was zum Absturz des Computers führt. Anschließend werden alle Register vom Stack geholt, danach wird mit RTI ins Hauptprogramm zurückgesprungen.

Verlassen Sie nun mit /X HYPRA-ASS und tippen Sie folgende Zeilen ein:

 10 SYS 49152
 20 PRINT "HALLO";:GOTO 20

Starten Sie dieses kleine Programm mit RUN. Wie Sie sehen, wird das linke obere Zeichen ständig verändert und zusätzlich das Basic-Programm bearbeitet, es laufen also zwei Programme gleichzeitig!

Brechen Sie das Programm mit RUN/STOP-RESTORE ab und geben Sie das folgende Programm ein:

 10 SYS 49152
 20 INPUT A

Weshalb blinkt der Cursor nun aber nicht? Wir haben den IRQ-Vektor umgebogen und springen aus unserer IRQ-Routine direkt wieder ins unterbrochene Programm zurück. Da die alte IRQ-Routine, die das Cursorblinken und die Tastaturabfrage übernahm, nun nicht mehr ausgeführt wird, kann der Cursor logischerweise auch nicht erscheinen. Eine Eingabe ist folglich nicht mehr möglich, der INPUT-Befehl kann nicht bearbeitet werden und der Computer hängt sich auf.

Es müssen also beide IRQ-Routinen durchlaufen werden, erst Ihre neue und dann die alte. Das läßt sich sehr einfach dadurch bewerkstelligen, daß man, statt sofort wieder aus der IRQ-Routine ins Hauptprogramm zu springen, einfach in die alte IRQ-Routine hineinspringt. Das wird in Listing 2 realisiert.

Assemblieren Sie das Programm und starten Sie es mit SYS 49152. Sie sehen, wie sich das linke obere Zeichen wie gehabt verändert, allerdings blinkt jetzt der Cursor und der INPUT-Befehl arbeitet normal. Sie haben also quasi Ihre eigene IRQ-Routine ins Betriebssystem »eingehängt«.

Sie können so Programme praktisch nebenher ausführen lassen. Es ist allerdings darauf zu achten, daß diese Routinen möglichst kurz sind. Das heißt allerdings nicht, daß Sie sich auf das Inkrementieren von Speicherzelle beschränken müssen. Sie könnten beispielsweise Sprites im IRQ bewegen, Musik spielen, einen Text drucken oder ähnliche Dinge tun. Soll eine IRQ-Routine wieder »ausgehängt« werden, so muß der IRQ-Vektor mit seinem alten Wert beschrieben werden. Das besorgt die Routine »DEINIT« bei Zeile 180. Sie hat genau die umgekehrte Funktion wie die Routine »INIT« ab Zeile 100. Rufen Sie DEINIT mit SYS 49165 auf. Wie Sie sehen, verändert sich das linke obere Zeichen nun nicht mehr.

Stellen Sie sich vor, Sie programmieren ein Spiel, bei dem ein Raumschiff, sobald es mit einem anderen zusammenstößt, vom Bildschirm verschwinden soll. Mit Hilfe des VICs lassen sich derartige bewegliche Bilder (Sprites) leicht darstellen und auch bewegen (dies wird in Teil 3 besprochen), aber wie löst man die obige Reaktion bei Kollision dieser Bilder ohne Zeitverzögerung aus? Richtig! Mit einem IRQ, der vom VIC ausgelöst wird! Man muß dem VIC nur sagen, daß bei einer solchen Kollision ein IRQ ausgelöst werden soll. Die Sache hat nur einen Haken: Für den Prozessor sind alle IRQ-Impulse gleich, er würde also bei dem obigen IRQ schlicht und einfach in die »normale« IRQ-Routine springen, die Tastatur abfragen, den Cursor freundlich blinken lassen und dann das Hauptprogramm weiter abwickeln, und das war's! Dies bedeutet: Aktiviert man eine weitere IRQ-Quelle neben dem »System-IRQ«, muß man am Anfang der eigentlichen IRQ-Routine zunächst einmal feststellen, welche IRQ-Quelle den IRQ ausgelöst hat, danach muß entsprechend verzweigt werden. Da dies alles nicht ganz einfach ist, soll die Handhabung einer zweiten IRQ-Quelle zunächst am Beispiel des Rasterzeilen-IRQs behandelt werden, der obendrein, wie wir später sehen werden, für die Programmierung technisch einwandfreier beweglicher Grafik besonders wichtig ist.

Der VIC stellt folgende IRQ-Quellen zur Verfügung:

  1. Rasterzeilen-Interrupt
  2. Sprite-Hintergrund-Kollision
  3. Sprite-Sprite-Kollision
  4. Lichtgriffel-Interrupt

Der Lichtgriffel-Interrupt ist für die Spieleprogrammierung uninteressant. Die Spritekollisionen werden im Teil 3 dieses Kurses behandelt. Was übrig bleibt, ist der Rasterzeileninterrupt.

Um zu verstehen, was ein Rasterzeileninterrupt ist, müssen Sie zunächst etwas über den Aufbau des Monitorbildes wissen. Vereinfacht gesagt: Das Bild des Monitors besteht aus 280 »Rasterzeilen«, die ein flinker Elektronenstrahl 50mal pro Sekunde auf den Bildschirm zaubert, wobei die Zeilen vom links nach rechts und von oben nach unten beschrieben werden (Bild 1). Diese Rasterzeilen dürfen nun nicht etwa mit Bildschirmzeilen verwechselt werden. Darunter versteht man die Textzeilen, die vom VIC auf dem Monitor dargestellt werden, beim C64 bekanntlich 25. Da jeder Buchstabe 8 Rasterzeilen »hoch« ist, werden also nur 8 x 25 = 200 Rasterzeilen für die Darstellung von Zeichen (und Grafiken) genutzt, je 40 Rasterzeilen liegen obenhalb und unterhalb des genutzten Monitorbildes, einige dieser Zeilen erscheinen überhaupt nicht mehr auf dem Bildschirm.

Bild 1. Der Aufbau des Bildschirms durch den Rasterstrahl

Nun aber das Tolle: Der VIC besitzt Register, in denen stets die Nummer derjenigen Rasterzeile steht, die gerade beschrieben wird (aktuelle Rasterzeile). Es kommt aber noch besser: Man kann den VIC nämlich veranlassen, immer dann, wenn eine bestimmte Rasterzeile beschrieben wird, einen IRQ auszulösen, bequemer geht's wirklich nicht mehr!

Nehmen wir an, wir möchten zwischen den Rasterzeilen 100 und 200 eine andere Rahmenfarbe haben, als im übrigen Bildschirmbereich.

Wir programmieren also einen Rasterzeilen-IRQ für Rasterzeile 100. Ist der Rasterstrahl bei Zeile 100 angelangt, wird ein IRQ ausgelöst. Die entsprechende IRQ-Routine schaltet die Rahmenfarbe um. Der Rasterstrahl hat nun bis Zeile 100 die alte Rahmenfarbe dargestellt. Bei Rasterzeile 100 wird die Rahmenfarbe verändert und der Rasterstrahl stellt von nun an die neue Rahmenfarbe dar. Diese IRQ-Routine setzt nun gleichzeitig einen neuen IRQ für Rasterzeile 200. Der Rasterstrahl bewegt sich nun von Zeile 100 weiter und stellt die neue Rahmenfarbe dar, bis er bei Zeile 200 ankommt. Nun wird erneut ein IRQ ausgelöst. Die dazugehörigen IRQ-Routine stellt die alte Rahmenfarbe wieder her und programmiert den nächsten IRQ für Zeile 100. Dort fängt das eben Beschriebene wieder von vorn an (Bild 2).

Bild 2. Der mehrfarbige Bildschirmrahmen - ein IRQ-Produkt

Alles schön und gut, nur, was wird aus unserem System-IRQ? Dieser wird ja vom CIA ausgelöst. Wir haben es also jetzt mit zwei IRQ-Quellen gleichzeitig zu tun. Unabhängig davon, ob ein IRQ vom CIA oder vom VIC kommt, springt der Prozessor über denselben Vektor, also in die gleiche IRQ-Routine. Wir müssen daher in der IRQ-Routine zunächst feststellen, welche IRQ-Quelle den IRQ ausgelöst hat. War es der CIA, dann tritt die normale IRQ-Routine in Aktion, kam der IRQ vom VIC, dann wird die aktuelle Rasterzeile festgestellt und je nachdem, ob es Zeile 100 oder 200 ist, in die entsprechende IRQ-Routine verzweigt (Bild 5).

Um einen Rasterzeilen-IRQ zu programmieren, müssen mehrere Dingen getan werden:

  1. gewünschte Rasterzeile festlegen,
  2. VIC-IRQ einschalten (»freigeben«) und Raster-IRQ wählen,
  3. IRQ-Vektor »verbiegen«.

Für die Programmierung von Rasterzeilen-IRQs benötigt man vier Register:

Mit dem Rasterzeilenregister läßt sich durch Auslesen feststellen, in welcher aktuellen Rasterzeile sich der Rasterstrahl befindet. Da es 280 Rasterzeilen gibt, man mit 8 Bit aber nun 256 Kombinationen einstellen kann, muß es noch ein Bit 8 geben, um Zahlen > 256 darzustellen (9. Bit, da von 0-8 gezählt wird). Bit 7 im Register VIC+17 repräsentiert dieses 9. Bit (Bild 3), das zusammen mit den 8 Bit der Speicherzelle VIC+18 eine 9-Bit-Zahl bildet.

Bild 3. Das neunte Bit des Rasterregister ist Bit 7 im Register 17 des VIC. Hier kann die Position eines Raster-IRQ definiert werden.

Mit dem IRQ-Maskenregister sagt man dem VIC, ob er IRQs erzeugen soll, und wenn, welche IRQs. In Bild 4 sehen Sie den Aufbau des IRQ-Maskenregisters. Durch setzen von Bit 7 und zusätzlich von Bit 0, 1, 2 und/oder 3 werden die verschiedenen IRQ-Arten ausgewählt. In unserem Fall müssen Bit 7 und Bit 0 gesetzt werden, damit ein Raster-IRQ freigegeben wird, VIC+26 ($D01A) muß also mit 129 beschrieben werden.

Bild 4. Die Register 25 und 26 des VIC. Die Bits 4 bis 6 sind bei beiden nicht genutzt.

Mit dem IRQ-Requestregister läßt sich, nachdem ein IRQ aufgetreten ist, feststellen, ob der VIC der Auslöser war oder nicht. War er es, läßt sich zusätzlich noch feststellen, welche der internen Quellen aktiv war (Sprite-Sprite-Kollision, Rasterstrahl etc.).

Dieses Register hat die merkwürdige Eigenschaft, daß man es löscht, indem man seinen Inhalt wieder zurückschreibt! Das heißt: Lesen Sie dieses Register aus, müssen Sie es unmittelbar danach wieder beschreiben. Etwa so:

 LDA VIC+25
 STA VIC+25

Nun befindet sich er Wert im Akku und das Register ist gelöscht. Geschieht dies nicht, wird ein Dauer-IRQ ausgelöst, und der Computer stürtzt ab!

Es gilt ganz allgemein: Hat man eine IRQ-Quelle aktiviert, so muß man unbedingt sicherstellen, daß die IRQ-Routine nach Auftreten eines entsprechenden IRQs das jeweilige IRQ-Requestregister löscht.

Sehen Sie sich nun Listing 3 an. Dieses Programm erzeugt einen hellblauen Rahmen mit einem dunkelblauen Streifen. Starten Sie es mit SYS 49152.

In Bild 5 sehen Sie das Flußdiagramm dieses Programms, Tabelle 1 erläutert das Programm Zeile für Zeile.

Bild 5. Flußdiagramm einer IRQ-Routine, die einen mehrfarbigen Rahmen erzeugt. Zwei IRQs werden abwechselnd aktiviert.

Sieht man sich die Wirkungsweise des Programms genauer an, so stellt man fest, daß die Grenzen zwischen den zwei Farben etwas flimmern. Dies verstärkt sich jedoch drastisch, wenn Sie eine Taste drücken (und sei es nur die CTRL-Taste).

Woran könnte das nun liegen? Nun, wir haben zwei IRQ-Quellen zur gleichen Zeit eingeschaltet. Das bedeutet, daß es möglich ist, daß, wenn zum Beispiel ein IRQ vom CIA ausgelöst wurde, unmittelbar darauf ein IRQ vom VIC ausgelöst werden kann. Da die Betriebssystem-IRQ-Routine zu diesem Zeitpunkt noch nicht beendet ist und auch von keinem anderen IRQ unterbrochen werden kann, verzögert sich der Anruf der VIC-IRQ-Routine um Bruchteile einer Sekunde. Der Rasterstrahl läuft in dieser Zeit jedoch weiter, und so findet die Farbumschaltung zu spät statt.

Was folgern wir daraus? Man verzichtet auf den CIA-IRQ. Da der Cursor und die Tastaturabfrage benötigt werden, muß die System-IRQ-Routine jedoch trotzdem durchlaufen werden. Das erreicht man dadurch, daß man, anstatt die IRQ-Routine vollständig zu beenden, einfach in die Betriebssystem-IRQ-Routine springt (wie bei Listing 2).

Das wird in Listing 4 verwirklicht.

(H. Rosenfeldt)

  Listing 1. Verändert ein Bildschirmzeichen im IRQ
(download Quellcode in Hypra-Ass Format)
 
HYPRA-ASS  ASSEMBLERLISTING:
             5    -.LI1,4,0
             10   -.BA 49152
             20   -.GL IRQVEC = $0314       ;IRQVEKTOR
             30   -.GL CIACONTROL = $DC0D   ;CIA-KONTROLLREGISTER
C000 78     :100  -INIT       SEI           ;IRQ VERHINDERN
C001 A90D   :110  -           LDA #<(IRQNEU);IRQ-VEKTOR AUF NEUE
C003 A2C0   :120  -           LDX #>(IRQNEU);IRQ-ROUTINE STELLEN
CO05 8D1403 :130  -           STA IRQVEC
C008 8E1503 :140  -           STX IRQVEC+1
C00B 58     :150  -           CLI           ;IRQ WIEDER ZULASSEN
C00C 60     :160  -           RTS           ;UND ZURUECK...
;
C00D EE0004 :200  -IRQNEU     INC 1024      ;1024 INKREMENTIEREN
C010 AD0DDC :210  -           LDA CIACONTROL;IRQ-FLAG LOESCHEN
C013 68     :220  -           PLA           ;REGISTER ZURUECKHOLEN
C014 A8     :230  -           TAY
C015 68     :240  -           PLA
C016 AA     :250  -           TAX
C017 68     :260  -           PLA
C018 40     :270  -           RTI           ;UND MIT RTI ZURUECK...
            :60000-.EN
 
  Listing 2. Bindet Listing 1 ins Betriebssystem ein
(download Quellcode in Hypra-Ass Format)
 
HYPRA-ASS  ASSEMBLERLISTING:
             5    -.LI1,4,0
             10   -.BA 49152
             20   -.GL IRQVEC = $0314       ;IRQVEKTOR
             30   -.GL IRQALT = $EA31       ;ALTE IRQ-ROUTINE
C000 78     :100  -INIT       SEI           ;IRQ SPERREN
C001 A91A   :110  -           LDA #<(IRQNEU);IRQ-VEKTOR AUF NEUE
C003 A2C0   :120  -           LDX #>(IRQNEU);IRQ-ROUTINE STELLEN
C005 8D1403 :130  -           STA IRQVEC
C008 8E1503 :140  -           STX IRQVEC+1
C00B 58     :150  -           CLI           ;IRQ WIEDER ZULASSEN
C00C 60     :160  -           RTS           ;UND ZURUECK...
;
C00D 78     :180  -DEINIT     SEI           ;IRQ SPERREN
C00E A931   :190  -           LDA #<(IRQALT);VEKTOR MIT STARTADRESSE
C010 A2EA   :200  -           LDX #>(IRQALT);DER ALTEN IRQ-ROUTINE
C012 8D1403 :210  -           STA IRQVEC    ;BELEGEN
C015 8E1503 :220  -           STX IRQVEC+1
C018 58     :230  -           CLI           ;IRQ WIEDER ZULASSEN
C019 60     :240  -           RTS           ;UND ZURUECK...
;
C01A EE0004 :260  -IRQNEU     INC 1024      ;1024 INKREMENTIEREN
C01D 4C31EA :270  -           JMP IRQALT    ;IN DIE ALTE IRQ-ROUTINE
            60000-.EN
 
  Listing 3. Erzeugt einen blauen Bildschirmrahmen mit hellblauen Streifen
(download Quellcode in Hypra-Ass Format)
 
HYPRA-ASS  ASSEMBLERLISTING:
             5    -.LI1,4,0
             10   -.BA 49152                ;PROGRAMMSTART
             20   -.GL IRQVEC = $0314       ;IRQVEKTOR
             30   -.GL IRQALT = $EA31       ;ALTE IRQ-ROUTINE
             40   -.GL VIC    = $D000       ;BASISADRESSE DES VIC
             50   -.GL IRQMASK= VIC+26      ;IRQ-MASKENREGISTER
             60   -.GL IRQFLAG= VIC+25      ;IRQ-REQUESTREGISTER
             70   -.GL RASTER = VIC+18      ;RASTERZEILENREGISTER
             80   -.GL BORDER = VIC+32      ;RAHMENFARBE-REGISTER
             90   -.GL OBEN   = 100         ;OBERE RASTERZEILE
             100  -.GL UNTEN  = 200         ;UNTERE RASTERZEILE
             110  -.GL HIBIT  = VIC+17      ;BIT 8 DER RASTERZEILENNUMMER
;
C000 78     :200  -INIT       SEI           ;IRQ SPERREN
C001 A964   :210  -           LDA #OBEN     ;IRQ FUER RASTERZEILE 100
C003 8D12D0 :220  -           STA RASTER
C006 AD11D0 :230  -           LDA HIBIT     ;BIT 8 LOESCHEN
C009 297F   :240  -           AND #127
C00B 8D11D0 :250  -           STA HIBIT
C00E A981   :260  -           LDA #129      ;IRQ MASKIEREN
C010 8D1AD0 :270  -           STA IRQMASK   ;RASTER-IRQ
C013 A936   :280  -           LDA #<(IRQNEU);IRQ-VEKTOR AUF NEUE
C015 A2C0   :290  -           LDX #>(IRQNEU);IRQ-ROUTINE STELLEN
C017 8D1403 :300  -           STA IRQVEC
C01A 8E1503 :310  -           STX IRQVEC+1
C01D 58     :320  -           CLI           ;IRQ WIEDER ZULASSEN
C01E 60     :330  -           RTS           ;UND ZURUECK...
;
C01F 78     :350  -DEINIT     SEI           ;IRQ SPERREN
C020 A900   :360  -           LDA #0        ;ALL BITS IN MASKEN-
C022 8D1AD0 :370  -           STA IRQMASK   ;REGISTER LOESCHEN
C025 A931   :380  -           LDA #<(IRQALT);VEKTOR AUF ALTE
C027 A2EA   :390  -           LDX #>(IRQALT);IRQ-ROUTINE STELLEN
C029 8D1403 :400  -           STA IRQVEC
C02C 8E1503 :410  -           STX IRQVEC+1
C02F 58     :420  -           CLI           ;IRQ WIEDER ZULASSEN
C030 A90E   :430  -           LDA #14       ;NORMALE RAHMENFARBE
C032 8D20D0 :440  -           STA BORDER    ;EINSTELLEN
C035 60     :450  -           RTS           ;UND ZURUECK...
;
C036 AD19D0 :470  -IRQNEU     LDA IRQFLAG   ;VIC-IRQ-FLAG LESEN
C039 8D19D0 :480  -           STA IRQFLAG   ;UND WIEDER SCHREIBEN
C03C 3003   :490  -           BMI VICIRQ    ;WENN BIT 7 GESETZT, DANN VICIRQ
C03E 4C31EA :500  -           JMP IRQALT    ;SONST ZUR ALTEN IRQ-ROUTINE
;
C041 AD12D0 :520  -VICIRQ     LDA RASTER    ;RASTERZEILENREGISTER LESEN
C044 C9C8   :530  -           CMP #UNTEN    ;GROESSER/GLEICH 200 ?
C046 B010   :540  -           BCS OLDCOLOR  ;JA, DANN ALTE RAHMENFARBE
;
C048 A906   :560  -NEWCOLOR   LDA #6        ;SONST NEUE RAHMENFARBE
C04A 8D20D0 :570  -           STA BORDER
C04D A9C8   :580  -           LDA #UNTEN    ;NAECHSTER IRQ BEI ZEILE 200
C04F 8D12D0 :590  -           STA RASTER
;
C052 68     :610  -IRQRETURN  PLA           ;REGISTER ZURUECKHOLEN
C053 A8     :620  -           TAY
C054 68     :630  -           PLA
C055 AA     :640  -           TAX
C056 68     :650  -           PLA
C057 40     :660  -           RTI           ;UND MIT RTI ZURUECK...
;
C058 A90E   :680  -OLDCOLOR   LDA #14       ;ALTE RAHMENFARBE
C05A 8D20D0 :690  -           STA BORDER
C05D A964   :700  -           LDA #OBEN     ;NAECHSTER IRQ BEI ZEILE 100
C05F 8D12D0 :710  -           STA RASTER
C062 4C52C0 :720  -           JMP IRQRETURN ;UND IRQ BEENDEN...
             60000-.EN
 
  Tabelle 1. Listing 3 Zeile für Zeile dokumentiert  
200 Beginn der Initialisierung der neuen IRQ-Routine. Zunächst wird mit SEI der IRQ gesperrt.
210-220 Wahl der Rasterzeile, in der der erste Raster-IRQ auftreten soll.
230-250 Bit 8 (oder das 9. Bit) des Rasterzeilenregisters wird gelöscht.
260-270 Wahl des Rasterinterrupts als IRQ-Quelle. Bit 7 und Bit 0 (macht zusammen 129) werden gesetzt.
280-310 »Verbiegen« des IRQ-Vektors auf die neue IRQ-Routine.
320-330 IRQ wieder zulassen und die Initialisierungsroutine mit RTS verlassen.
 
350 Beginn der Deinitialisierungsroutine. Sie schaltet den VIC-IRQ wieder aus und korrigiert den IRQ-Vektor. Sie wird mit SYS 49183 aufgerufen (diesen Wert können Sie mit dem HYPRA-ASS-Befehl <-DEINIT ermitteln). In dieser Zeile wird der IRQ gesperrt.
360-370 IRQ-Maskenregister wird durch Beschreiben mit »0« gelöscht. Dies ist notwendig, da der VIC sonst trotzdem noch IRQs auslösen würde.
380-410 Der IRQ-Vektor wird mit seinem alten Wert belegt.
420 IRQ wird wieder zugelassen.
430-440 Die Rahmenfarbe wird auf hellblau gesetzt.
450 Die Routine wird mit RTS verlassen.
 
470-480 Beginn der neuen IRQ-Routine. Das IRQ-Requestregister wird gelesen und anschließend wieder zurückgeschrieben.
490 Ist Bit 7 gesetzt, kam der IRQ vom VIC und es wird zur Routine »VICIRQ« verzweigt.
500 War Bit 7 null, wurde der IRQ vom CIA ausgelöst und die alte IRQ-Routine (System-IRQ-Routine) wird abgewickelt.
520-530 Beginn der Behandlung des VIC-IRQs. Die Position des Rasterstrahls wird mit 200 verglichen.
540 Ist der Rasterstrahl bei Zeile 200 oder weiter, muß er an der unteren Grenze schon vorbeigewandert sein. Also muß die alten Rahmenfarbe eingeschaltet werden, hellblau. Es wird zur Routine »OLDCOLOR« verzweigt.
 
560-570 War der Rasterstrahl bei einer Zeile, die kleiner ist als 200, muß der IRQ bei Zeile 100 ausgelöst worden sein, da wir nur diese beide Möglichkeiten haben. Es wird nun die neue Rahmenfarbe eingeschaltet, dunkelblau.
580-590 Umstellung des Raster-IRQs auf 200, da dort wieder die alte Farbe eingestellt werden soll.
610-660 Die auf dem Stack gespeicherten Register werden wieder zurückgeholt, danach wird mit RTI in das unterbrochene Programm zurückgekehrt. Diese Routine gibt es auch im Betriebssystem, sie liegt bei $FEBC. Sie wird mit JMP $FEBC aufgerufen (nicht mit JSR $FEBC!).
 
680-690 Zu dieser Routine wird verzweigt, wenn der Rasterstrahl bei Zeile 200 war. Es wird nun die alte Rahmenfarbe, hellblau, wieder eingeschaltet.
700-710 Der nächste Raster-IRQ für Zeile 100 wird festgelegt.
720 Sprung nach Zeile 490, um die IRQ-Routine zu beenden.
 
  Listing 4. Bindet Listing 3 flimmerfrei ins Betriebssystem ein
(download Quellcode in Hypra-Ass Format)
 
HYPRA-ASS  ASSEMBLERLISTING:
             5    -.LI1,4,0
             10   -.BA 49152                ;PROGRAMMSTART
             20   -.GL IRQVEC = $0314       ;IRQVEKTOR
             30   -.GL IRQALT = $EA31       ;ALTE IRQ-ROUTINE
             40   -.GL VIC    = $D000       ;BASISADRESSE DES VIC
             50   -.GL IRQMASK= VIC+26      ;IRQ-MASKENREGISTER
             60   -.GL IRQFLAG= VIC+25      ;IRQ-REQUESTREGISTER
             70   -.GL RASTER = VIC+18      ;RASTERZEILENREGISTER
             80   -.GL BORDER = VIC+32      ;RAHMENFARBE-REGISTER
             90   -.GL OBEN   = 100         ;OBERE RASTERZEILE
             100  -.GL UNTEN  = 200         ;UNTERE RASTERZEILE
             110  -.GL HIBIT  = VIC+17      ;BIT 8 DER RASTERZEILENNUMMER
             120  -.GL CIATIME= $DC0E       ;TIMER A STEUERREGISTER
;
C000 78     :200  -INIT       SEI           ;IRQ SPERREN
C001 A964   :210  -           LDA #OBEN     ;IRQ FUER RASTERZEILE 100
C003 8D12D0 :220  -           STA RASTER
C006 AD11D0 :230  -           LDA HIBIT     ;BIT 8 LOESCHEN
C009 297F   :240  -           AND #127
C00B 8D11D0 :250  -           STA HIBIT
C00E A981   :260  -           LDA #129      ;IRQ MASKIEREN
C010 8D1AD0 :270  -           STA IRQMASK   ;RASTER-IRQ
C013 A936   :280  -           LDA #<(IRQNEU);IRQ-VEKTOR AUF NEUE
C015 A2C0   :290  -           LDX #>(IRQNEU);IRQ-ROUTINE STELLEN
C017 8D1403 :300  -           STA IRQVEC
C01A 8E1503 :310  -           STX IRQVEC+1
C01D AD0EDC :320  -           LDA CIATIME   ;TIMER A STOPPEN
C020 29FE   :330  -           AND #254
C022 8D0EDC :340  -           STA CIATIME
C025 58     :350  -           CLI           ;IRQ WIEDER ZULASSEN
C026 60     :360  -           RTS           ;UND ZURUECK...
;
C027 78     :380  -DEINIT     SEI           ;IRQ SPERREN
C028 A900   :390  -           LDA #0        ;ALL BITS IN MASKEN-
C02A 8D1AD0 :400  -           STA IRQMASK   ;REGISTER LOESCHEN
C02D AD0EDC :410  -           LDA CIATIME   ;TIMER A WIEDER STARTEN
C030 0901   :420  -           ORA #1
C032 8D0EDC :430  -           STA CIATIME
C035 A931   :440  -           LDA #<(IRQALT);VEKTOR AUF ALTE
C037 A2EA   :450  -           LDX #>(IRQALT);IRQ-ROUTINE STELLEN
C039 8D1403 :460  -           STA IRQVEC
C03C 8E1503 :470  -           STX IRQVEC+1
C03F 58     :480  -           CLI           ;IRQ WIEDER ZULASSEN
C040 A90E   :490  -           LDA #14       ;NORMALE RAHMENFARBE
C042 8D20D0 :500  -           STA BORDER    ;EINSTELLEN
C045 60     :510  -           RTS           ;UND ZURUECK...
;
C046 AD19D0 :530  -IRQNEU     LDA IRQFLAG   ;VIC-IRQ-FLAG LESEN
C049 8D19D0 :540  -           STA IRQFLAG   ;UND WIEDER SCHREIBEN
;
C04C AD12D0 :560  -VICIRQ     LDA RASTER    ;RASTERZEILENREGISTER LESEN
C04F C9C8   :570  -           CMP #UNTEN    ;GROESSER/GLEICH 200 ?
C051 B010   :580  -           BCS OLDCOLOR  ;JA, DANN ALTE RAHMENFARBE
;
C053 A906   :600  -NEWCOLOR   LDA #6        ;SONST NEUE RAHMENFARBE
C055 8D20D0 :610  -           STA BORDER
C058 A9C8   :620  -           LDA #UNTEN    ;NAECHSTER IRQ BEI ZEILE 200
C05A 8D12D0 :630  -           STA RASTER
;
C05D 68     :650  -IRQRETURN  PLA           ;REGISTER ZURUECKHOLEN
C05E A8     :660  -           TAY
C05F 68     :670  -           PLA
C060 AA     :680  -           TAX
C061 68     :690  -           PLA
C062 40     :700  -           RTI           ;UND MIT RTI ZURUECK...
;
C063 A90E   :720  -OLDCOLOR   LDA #14       ;ALTE RAHMENFARBE
C065 8D20D0 :730  -           STA BORDER
C068 A964   :740  -           LDA #OBEN     ;NAECHSTER IRQ BEI ZEILE 100
C06A 8D12D0 :750  -           STA RASTER
C06D 4C31EA :760  -           JMP IRQALT    ;UND ZUR ALTEN IRQ-ROUTINE...
             60000-.EN
 
  Lexikon:  
Interrupt
Impuls einer »Interrupt-Quelle«, der den Prozessor veranlaßt, ein laufendes Programm zu unterbrechen und statt dessen eine »Interrupt-Routine« auszuführen. Nach Abwicklung derselben wird das Hauptprogramm fortgesetzt.
IRQ
Interrupt-Request. Interrupt-Impuls, dessen Durchführung durch den Maschinenbefehl SEI unterbunden werden kann, bis der Befehl CLI abgearbeitet wird. Vorsicht! Ein einmal aufgetretener IRQ »wartet« solange, bis CLI die Ausführung ermöglicht!
NMI
Non-Maskable-Interrupt. Wie IRQ, läßt sich allerdings nicht durch SEI unterbinden. Mögliche NMI-Quellen: RESTORE-Taste und CIA 2.
IRQ-Steuer-Register
Programmierbare IRQ-Quellen besitzen stets IRQ-Maskenregister, die per Maschinenprogramm zweckentsprechend verwaltet werden müssen. Obwohl diese Register ganz normale RAM-Speicherzellen zu sein scheinen, verhalten sie sich völlig anders, sowohl beim Auslesen als auch beim Beschreiben.
IRQ-Maskenregister
Register im VIC, durch dessen Beschreiben eine IRQ-Quelle freigegeben bzw. gelöscht wird. Beim Aktivieren setzt man die »passenden« Bits durch Beschreiben, wobei Bit 7 grundsätzlich gesetzt sein muß. Man löscht durch Beschreiben mit 0.
IRQ-Requestregister
Register im VIC, in dem beim Auftreten eines IRQs Bit 7 zusammen mit einem weiteren Bit gesetzt ist, das eine Identifizierung der IRQ-Art gestattet. Nach Auftreten eines IRQs muß das IRQ-Requestregister unter allen Umständen gelöscht werden, dies geschieht durch Auslesen und Rückschreiben.