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:
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:
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 | ||
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 | ||