C 64
Tips & Tricks

Unterbrechen Sie mich bitte!

Im Gegensatz zum Bereich zwischenmenschlicher Beziehungen, wo jemanden zu unterbrechen als plumpe Unhöflichkeit eingestuft wird, ist dies einem Computer gegenüber nicht nur ein beliebtes, sondern sogar erwünschtes Verfahren effektvoller Programmgestaltung. Für die meisten Anwender aber sind solche, »Interrupt« genannte, Methoden — leider — mehr oder weniger »Böhmische Dörfer«.

Anhand der vielseitigen Hardware des Commodore 64 wollen wir nun einmal den Schleier des Geheimnisses ein wenig lüften, Ihnen mit einem Demonstrationsprogramm einige Anwendungsbeispiele zeigen und Sie auf Ihrem Bildschirm kein blaues, aber ein buntes Wunder erleben lassen.

Wenn Sie Ihren C 64 einschalten, und der Cursor blinkt, dann haben Sie bereits einen Interrupt erzeugt. Es handelt sich dabei um eine gezielte Programmunterbrechung, die auf ein bestimmtes Ereignis fixiert ist, so zum Beispiel den Inhalt einer Speicherstelle. Tritt der erwünschte Zustand ein, so räumt die Hardware diesem Vorgang Priorität vor allen anderen Aufgaben ein. Gleich was der Computer im Augenblick macht, er wird unverzüglich seine Tätigkeit einstellen und ein spezielles Unterprogramm abarbeiten, das ihm zuvor als Interruptroutine deklariert wurde. Im Betriebssystem-ROM befindet sie sich von Adresse $EA31 (dezimal 59953) bis $ECB8 (60600). Hier wird etwa 60mal in jeder Sekunde geprüft, ob eine — und falls ja, welche — Taste gedrückt wurde; hier wird das Blinken des Cursors erzeugt und der Motor der Datasette ein- oder ausgeschaltet. Wenn der Interrupt beendet ist, setzt der Computer die Bearbeitung des laufenden Programms exakt an der Stelle fort, an der er sich vor Eintritt des Interrupts gerade befand. Gewöhnlich merkt man von solchen Intermezzi nichts, weil der Prozessor eine ungeheure Geschwindigkeit entwickelt, deren Arbeitstakte sich bereits im Bereich von Mikrosekunden (millionstel Sekunden) bewegen.

Noch eine andere Einrichtung der von Ihnen benutzten Geräte ist mit extremer Schnelligkeit ausgestattet: Der Elektronenstrahl, der vor Ihren Augen 25mal in jeder Sekunde ein neues Bild auf die Mattscheibe zeichnet. Nach diesen grundsätzlichen Überlegungen wollen wir aus solchen Voraussetzungen einen Wettstreit der Systeme entwickeln und den Computer gegen den flotten Strahl antreten lassen. Unglaublich, werden Sie jetzt wahrscheinlich sagen, aber warten Sie ab! Möglich ist das nämlich, weil der für die Bildorganisation zuständige Video-Interface-Controller (VIC) stets genau darüber »im Bild« ist, welche Rasterzeile des Monitorbildes augenblicklich geschrieben wird. Das ist die Bedingung dafür, daß auf dem Schirm auch ein Bild im geordneten Zusammenhang wiedergegeben wird. Der VIC, dessen Register unter den Adressen 53248 ($D000) bis 53294 ($D02E) erreichbar sind, führt in der Speicherzelle 53266 ($D012) Buch über die jeweilige Zeilennummer. Darüber war schon einmal zu lesen, daß es unsinnig wäre, dort etwas hineinzuschreiben, weil man den Elektronenstrahl der Bildröhre gar nicht steuern könne. Und das ist nur zum Teil richtig, denn steuern können wir den Strahl nicht, wohl aber können wir ihn steuern lassen.

Dafür ist besagtes Register — wie übrigens viele andere auch — mit einer Doppelfunktion ausgerüstet. Abhängig von der jeweiligen Zugriffsart, Lesen oder Schreiben (PEEK oder POKE), erreicht man unter derselben Hausnummer verschiedene Adressaten. Wird das Register gelesen, so erfährt man die gerade bearbeitete Rasterzeile, wird es beschrieben, bleibt der übermittelte Wert gespeichert und dient dem internen Vergleich, ob er mit der aktuellen Zeile identisch ist. Wenn dieser Fall eintritt, so reagiert die Hardware selbständig darauf und erzeugt einen Interrupt — falls ein solcher vorgesehen war. Zuvor muß dem Computer nämlich noch mitgeteilt werden, daß dies ein Interruptauslöser sein soll. Die Anmeldestelle ist die Adresse 53274 ($D01A), das Interrupt-Masken-Register.

Diese Speicherstelle korrespondiert ständig mit ihrem Nachbarn 53273 ($D019), dem Interrupt-Request-Register. Ist die vorgewählte Rasterzeile erreicht, signalisiert 53266 dieses Ereignis durch Kippen des Bits 0 im Request-Register: Es nimmt den Wert 1 an. Ist Bit 0 auch in der Maske gesetzt, wird der Interrupt-Pin des VIC aktiv und löst die Unterbrechung aus. Im Demo-Programm schreiben wir zu diesem Zweck eine 1 in die Maske, und fortan gilt für uns die Gesetzmäßigkeit, daß Bildhintergrund und -rand unifarben dargestellt werden, nicht mehr. Wir ändern nämlich ab einer beliebigen Rasterzeile die Bildfarben, um sie einige Zeilen weiter abermals umzuschalten. Da das schneller vor sich geht, als das träge menschliche Auge es registrieren kann, resultiert daraus kein wirres Flackern, sondern ein konstantes mehrfarbiges Bild, das im Extremfall (siehe Beispiel »Regenbogen«) sogar alle 16 möglichen Farben des Randes und Hintergrunds gleichzeitig wiederzugeben in der Lage ist.

Noch aber funktioniert der neue Interrupt nicht, denn der C 64 weiß noch nichts von unserem Programmsegment, mit dem wir den Farbwechsel vornehmen wollen. Dazu müssen wir ihm die Adresse der Routine im Interrupt-Vektor ab Speicherstelle 788 ($0314) hinterlegen: Lowbyte in 788, Highbyte in 789. Aber auch jetzt wird das Ergebnis immer noch nicht unseren Erwartungen entsprechen, da uns laufend der immer noch aktivierte Systeminterrupt in die Quere kommt und nach der Vektorenänderung ebenfalls in der neuen Routine unkontrolliert arbeitet. Dieser Interrupt stammt aus einer ganz anderen Quelle, vom Complex-Interface-Adapter (CIA), auf den wir gleich noch zu sprechen kommen. Softwaremäßig können wir ihn durch Setzen der Interruptflagge (SEI) nicht unterbinden, weil damit auch der ebenfalls maskierbare Rasterzeilen-Interrupt abgeschaltet würde. Zwei Möglichkeiten gibt es, das Problem zu lösen: Der CIA-Interrupt wird belassen, muß dann aber zu Beginn der Interrupt-Routine abgefragt (zum Beispiel durch Prüfen des Bits 0 im Request-Register 53273) und gegebenenfalls durch eine Umleitung über Sprungbefehle unschädlich gemacht werden, oder er wird eliminiert durch Schreiben des Wertes 127 in die Speicherstelle 56333 ($DCOD). Weil wir in unserem Demo-Programm weder Cursor-Blinken noch die Tastaturabfrage benötigen, entscheiden wir uns für letztere und löschen gleich zu Programmbeginn (siehe Assembler-Listing) den CIA-Interrupt völlig.

Außerdem ist zu berücksichtigen, daß der Elektronenstrahl auf allen Seiten ein Stück über den Rand des auf dem Monitor sichtbaren Bildes hinausschreibt. Dadurch entstehen Zeilennummern bis 280, so daß von Register 53266 aus bei einem Überlauf ein Highbyte nach Bit 7 des Registers 53265 übertragen werden muß. In dieser als »SCREEN« definierten Speicherstelle liegen allerdings auch einige andere Funktionen, die gegebenenfalls durch eine logische OR-Verknüpfung berücksichtigt werden müssen. Das Demo-Programm verwendet diese Funktionen indem durch Löschen des Bits 4 der Bildschirm ausgeblendet wird, so daß auf dem Schirm nur noch ein ganzflächiger Rand erscheint, obwohl auch weiterhin Texte auf den jetzt unsichtbaren Hintergrund ausgegeben werden können. Beispiel: POKE 53265, PEEK (53265) AND 255-16 läßt den Hintergrund verschwinden, POKE 53265, PEEK(53265) OR 16 zaubert ihn dann wieder herbei.

Der Rasterzeilen-Interrupt ist eine Spezialität des C 64, die ihn zu einem äußerst vielseitigen Gerät macht. Die Programmier-Profis der Videospiel-Produzenten benutzen ihn nicht allein dafür, oben blauen Himmel und unten braune Erde darzustellen, sondern auch, um ein nur partielles Scrolling zu erzielen, um Text und hochauflösende Grafik zu mischen, um gleichzeitig normale und Multicolorzeichen zu benutzen und und und… Aber damit erschöpfen sich die Interruptmöglichkeiten des Commodore 64 noch nicht. Bit 1 (Wert = 2) der Maske in 53274 erzeugt einen Interrupt, wenn ein Sprite Berührung mit einem Zeichen hat, Bit 2 (Wert = 4) wenn Sprite mit Sprite zusammenstößt, Bit 3 (Wert = 8) wenn ein Impuls vom Lightpen kommt, oder der Feuerknopf eines am Controlport 1 angeschlossenen Joysticks gedrückt wird und schließlich Bit 7 (Wert = 128), wenn eines der genannten Ereignisse eingetreten ist

Und dann ist da noch der bereits genannte CIA-Interrupt, der von einem gleich doppelt vorhandenen Baustein stammt. Beiden sind jedoch im C 64 teilweise unterschiedliche Aufgaben zugewiesen. CIA 1 hat die Basisadresse 56320 ($DC00), CIA 2 eine solche von 56576 ($DD00). Hier erfolgt beispielsweise die Tastaturdekodierung, die Abfrage von Joysticks, Paddles und Lightpen, die serielle Datenübertragung zu einer Schnittstelle, hier befinden sich die Echtzeituhren und die Timer. Wenden wir uns letzteren in CIA 1 zu.

Der Timer ist ein 16-Bit-Zählregister, das nach dem Starten ohne weiteres Zutun mit einer konstanten Geschwindigkeit dekrementiert, das heißt jeweils um 1 abwärts gezählt wird. Durch die zuvor beim VIC schon erwähnte Doppelfunktion besteht die Möglichkeit, den Timer ab einem bestimmten Wert zählen zu lassen: Lesen der Adresse 56324 ($DC04) liefert den aktuellen Stand des Lowbytes, Hineinschreiben den Startwert des Timers, ebenso beim Highbyte unter der Adresse 56325. Auf diese Weise wird der Systeminterrupt erzeugt, da der Computer in der Initialisierungsphase den Timer mit dem Wert 16421 (= 37 low und 64 high) lädt. Wenn der Timer über Null hinauszählt und damit einen sogenannten Unterlauf erzeugt, wird das durch Setzen des Bits 0 im lnterrupt-Control-Register 56333 ($DC0D) signalisiert. Auch dieses arbeitet wieder doppelt: Lesen ergibt die Interruptanforderung, ein Schreibzugriff erzeugt die Maske, die darüber entscheidet, welches Ereignis Interruptauslöser sein soll. Besondere Beachtung beim Beschreiben des Registers verdient Bit 7, das darüber bestimmt, ob die nachfolgend gesetzten Bits 6 bis 0 in der Maske gesetzt oder gelöscht werden. Alle übrigen bleiben unangetastet. Deshalb löscht 127 (Bit 7 nicht gesetzt!) im Demo-Programm sämtliche Maskenbits (siehe Bitmuster im Assembler-Listing, Zeile 1010), deshalb setzt 129 (Bitmuster 10000001) das Bit 0 der Maske und schaltet damit auf Interrupt durch den Timer. Erzeugen Sie doch einmal im Direktmodus auf dem Bildschirm ein längeres Zählintervall durch Heraufsetzen des Timer-Highbytes: POKE 56325, 255 läßt den Cursor sehr träge werden, POKE 56325, 5 versetzt ihn in nervöses Flattern.

Dieser Timer besitzt einen Zwillingsbruder mit den Adressen 56326 und 56327, der nicht nur gleichartig konstruiert ist und ebenfalls eigenständig einen Interrupt auf Bit 1 des Control-Registers erzeugen kann, sondern sich auch mit dem Timer A koppeln läßt. Beide Timer können nämlich auf verschiedene Taktquellen gelegt, unterschiedlich getriggert werden. Im Demo-Programm nutzen wir das aus, indem wir Timer A Systemtakte zählen lassen (Bit 5 des Registers 56334 gelöscht — Standardeinstellung), Timer B hingegen nur die Unterläufe von Timer A durch Setzen des Bits 6 im Register 56335. Dadurch erhalten wir einen 32-Bit-Zähler, der beliebige Zeitverzögerungen ermöglicht, eleganter als mit jeder ausschließlich softwaremäßig realisierten Warteschleife, weil die Timer von keinem Interrupt unterbrochen werden.

In der Warteschleife des Demo-Programms (Listing ab Zeile 9010), die als Subroutine angelegt ist, wird mit den Timern kein Interrupt erzeugt, sondern lediglich eine Zeitverzögerung erzielt. Dazu wird zunächst Timer B mit den vom Hauptprogramm im Akku und im Y-Register übergebenen Werten geladen, während Timer A konstant mit einem mittleren Wert arbeitet. Dann werden beide gestartet durch Setzen des Bits 0 im Register 56334 für Timer A und 56335 für Timer B. Aus Zeile 9030 des Listings ist ersichtlich, daß Bit 3 für Timer A gelöscht ist, was Continuous- oder Dauerbetrieb zur Folge hat. Jedesmal, wenn der Timer einen Unterlauf hat, lädt er umgehend wieder den zwischengespeicherten Startwert und beginnt erneut zu zählen. Den anderen schalten wir hingegen auf One-Shot (Zeile 9050), einen »Einzelschuß«. Bei einem Unterlauf lädt er zwar wieder den Startwert, bleibt aber stehen, wodurch sein Start/Stoppbit automatisch gelöscht wird. Wir prüfen dies, indem wir Bit 0 logisch nach rechts ins Carry verschieben, wo es bequem mit einem Branchbefehl untersucht werden kann. Erst der One-Shot-Betrieb des Timers B stellt sicher, daß ein Unterlauf auch erkannt wird, weil er im Dauerbetrieb vorübergehen könnte, während sich der Prozessor im Interrupt befindet. Außerdem verlangt ein gesetztes Bit 4 in der Warteschleife für beide Timer Force-Load, einen unbedingten Ladevorgang. Unabhängig davon, ob der Timer gerade läuft oder nicht, wird der Startwert geladen. Mit einem gelöschten Bit 4 kommt ein neuer Startwert erst dann zum Tragen, wenn er nach dem nächsten Unterlauf geladen wird.

Neben den Unterläufen der beiden Timer kann das Control-Register 56333 auf Bit 2 auch einen Interrupt erzeugen bei Übereinstimmung der Echtzeituhr 56328 bis 56331 mit einer vorgewählten Alarmzeit, auf Bit 3 durch ein volles oder leeres Schieberegister (56332) und auf Bit 4 durch den Impuls einer externen Signalquelle. Ein gesetztes Bit 7 zeigt hier an, daß mindestens eines der gesetzten Bits auch in der Maske gewählt ist, also ein Interrupt stattfindet. Das Interrupt-Flag wird aber bereits durch Lesen des Registers gelöscht.

Diese Kurzabhandlung vermag nur ansatzweise darzustellen, wie flexibel das Instrumentarium ist, das hier dem Anwender zur Verfügung steht. Speziell die zahlreichen Interruptmöglichkeiten verlangen geradezu nach Anwendung, wobei wir abschließend auf eine bisher nicht erwähnte noch zu sprechen kommen müssen. Denn das Demo-Programm läuft in einer Endlosschleife, die nicht ohne weiteres abgebrochen werden kann. Außerdem wird ja durch den lahmgelegten Systeminterrupt ohnehin kein Tastendruck mehr erkannt. Den Ausweg aus diesem Dilemma, haben die Konstrukteure geschaffen, als sie den sogenannten NMI erfanden. Das ist die Abkürzung für nicht maskierbarer Interrupt, eine hardwareseitige Unterbrechungsmöglichkeit mit so hoher Priorität, daß selbst ein gesetztes Interruptflag keine Rolle spielt. Wir können den NMI auslösen, indem wir die STOP-Taste gedrückt halten und gleichzeitig auf die RESTORE-Taste klopfen. Und schon ist alles wieder normal: Bild und Interrupt. Sie können zwar gleich wieder mit SYS49152 ins Demo-Programm starten, doch vielleicht lassen Sie sich selbst einmal etwas einfallen. Nur zu — unterbrechen Sie doch Ihren Commodore 64 bitte mal …

(Helmut Welke/aa)
CHRCOL 0286 CLEAR E544
COLOR C198 CRA DC0E
CRB DC0F EXIT C147
FLAG 00FB GETPAR C17D
GOLD C141 GRUND D021
HIGH C1B8 ICR DC0D
INIT C000 INTRO C1C8
IREQU D019 IRMASK D01A
IRQ1 C12C IRQ2 C158
IRQ3 C173 IRQOFF EA81
LINE 00FD LOOP1 C03E
LOOP2 C04C LOOP3 C0B7
LOOP4 C0F9 LOW C1A8
NAME C1DD OBEN C168
PLOT FFF0 PRINT AB1E
RAND D020 RASTER D012
ROT C139 SCREEN D011
TEXT C1FE TIME C115
TIMERA DC04 TIMERB DC06
VECTOR 0314 WAIT C125
Die Symboltabelle für das Maschinensprache-Programm »Rasterzeilen-Interrupt«
5 run100
10 **********************************
15 *                                *
20 *     rasterzeilen-interrupt     *
25 *                                *
30 *         (basic-loader)         *
35 *                                *
40 *       ein  demo-programm       *
45 *                                *
50 *     fuer den  commodore 64     *
55 *                                *
60 *        von helmut welke        *
65 *                                *
70 *        moehnestrasse 26        *
75 *                                *
80 *        5760  arnsberg 1        *
85 *                                *
90 *       tel. (02932) 23627       *
95 *                                *
99 **********************************
100 :
110 printchr$(147)"loading":ad=49152
120 readx:ifx<0goto300
130 pokead,x:ad=ad+1:s=s+x:goto120
200 :
300 ifad=49737ands=64373thensys49152
500 print:print"datas fehlerhaft"
600 :
700 :
800 :
1000 data 32,68,229,169,127,141,13,220
1010 data 141,4,220,141,5,220,162,0
1020 data 142,26,208,202,134,251,169
1030 data 11,141,17,208,133,253,169
1040 data 14,141,33,208,133,254,169
1050 data 44,160,193,141,20,3,140,21
1060 data 3,160,1,140,18,208,140,26
1070 data 208,136,169,25,32,21,193,166
1080 data 254,169,2,32,21,193,232,134
1090 data 254,224,200,144,244,166,253
1095 :
1100 data 169,2,32,21,193,232,134,253
1110 data 224,115,144,244,162,7,160
1120 data 13,24,32,240,255,169,200,160
1130 data 193,32,30,171,169,150,160
1140 data 0,32,21,193,169,27,141,17
1150 data 208,169,200,32,21,193,140
1160 data 26,208,140,32,208,32,68,229
1170 data 162,4,142,33,208,160,3,24
1180 data 32,240,255,169,254,160,193
1190 data 32,30,171,169,30,160,0,32
1195 :
1200 data 21,193,169,88,160,193,141
1210 data 20,3,140,21,3,162,25,134,253
1220 data 142,18,208,173,25,208,141
1230 data 25,208,160,1,140,26,208,136
1240 data 169,2,32,21,193,232,134,253
1250 data 224,170,144,244,169,100,32
1260 data 21,193,32,68,229,140,26,208
1270 data 169,115,160,193,141,20,3,140
1280 data 21,3,173,25,208,141,25,208
1290 data 160,0,132,251,200,140,18,208
1295 :
1300 data 140,26,208,169,0,32,21,193
1310 data 169,15,141,134,2,162,3,24
1320 data 32,240,255,169,221,160,193
1330 data 32,30,171,169,20,160,0,32
1340 data 21,193,206,134,2,16,237,169
1350 data 1,168,32,21,193,76,0,192,141
1360 data 6,220,140,7,220,169,17,141
1370 data 14,220,169,89,141,15,220,173
1380 data 15,220,74,176,250,96,165,251
1390 data 240,9,16,15,169,0,170,164
1395 :
1400 data 253,208,14,169,2,162,1,164
1410 data 254,208,6,169,7,162,255,160
1420 data 1,134,251,140,18,208,141,32
1430 data 208,173,25,208,141,25,208
1440 data 76,129,234,173,33,208,73,2
1450 data 168,41,2,240,6,165,253,24
1460 data 105,21,44,165,253,141,18,208
1470 data 140,33,208,76,79,193,198,251
1480 data 166,251,16,4,162,15,134,251
1490 data 189,152,193,72,188,184,193
1495 :
1500 data 189,168,193,170,104,142,18
1510 data 208,140,17,208,141,32,208
1520 data 141,33,208,76,79,193,4,11
1530 data 5,9,3,10,1,14,7,15,2,13,0
1540 data 8,6,12,1,11,254,235,219,203
1550 data 179,162,138,122,106,82,75
1560 data 59,51,33,27,155,27,27,27,27
1570 data 27,27,27,27,27,27,27,27,27
1580 data 27,14,8,31,196,69,77,79,45
1590 data 208,82,79,71,82,65,77,77,17
1595 :
1600 data 17,17,17,17,13,29,29,29,29
1610 data 29,29,29,29,29,210,193,211
1620 data 212,197,210,218,197,201,204
1630 data 197,206,45,201,78,84,69,82
1640 data 82,85,80,84,0,156,213,78,68
1650 data 32,78,85,78,32,32,32,32,17
1660 data 17,17,83,69,72,69,78,32,211
1670 data 73,69,32,32,32,32,17,17,17
1680 data 78,79,67,72,32,68,69,78,13
1690 data 17,17,17,17,17,29,29,29,29
1695 :
1700 data 29,29,29,29,29,29,210,160
1710 data 197,160,199,160,197,160,206
1720 data 160,194,160,207,160,199,160
1730 data 197,160,206,0
1740 data -1
Basic-Lader von »Rasterzeilen-Interrupt«
                        R A S T E R Z E I L E N - I N T E R R U P T

                       %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

                          Ein Demo-Programm fuer den COMMODORE 64

         von Helmut Welke, Moehnestrasse 26, 5760 Arnsberg 1, Telefon (02932) 23627

                                ***** ASSEMBLERLISTING *****

pass 2
30:    c000                   .opt p1,oo
                       ;
50:    c000                   *=   $c000   ;startadresse 49152
                       ;
100:   c000           clear   =    $e544   ;bildschirm loeschen
110:   c000           icr     =    $dc00+13 ;interrupt-control-register
120:   c000           irmask  =    $d000+26 ;interruptmaske
130:   c000           rand    =    $d000+32 ;farbe bildrand
140:   c000           grund   =    rand+1  ;farbe bildhintergrund
150:   c000           screen  =    $d000+17 ;bildschirm-steuerregister
160:   c000           raster  =    screen+1 ;elektronenstrahlzeile
170:   c000           vector  =    $0314   ;interrupt-sprungadresse
180:   c000           timera  =    $dc00+4 ;16-bit-zaehler
190:   c000           timerb  =    timera+2 ;dito
200:   c000           flag    =    $fb     ;freie speicherstelle
210:   c000           cra     =    icr+1   ;steuerregister timer a
220:   c000           crb     =    cra+1   ;steuerregister timer b
230:   c000           line    =    $fd     ;freie speicherstelle
240:   c000           irequ   =    irmask-1 ;interrupt-register
250:   c000           irqoff  =    $ea81   ;rueckkehr vom interrupt
260:   c000           plot    =    $fff0   ;cursor setzen/holen
270:   c000           print   =    $ab1e   ;string ausgeben
280:   c000           chrcol  =    $0286   ;aktuelle zeichenfarbe
                       ;
                       ;*** programmvorbereitung ***
                       ;
1000:  c000  20 44 e5 init    jsr  clear
1010:  c003  a9 7f            lda  #%01111111 ;alle interruptmöglichkeiten
1020:  c005  8d 0d dc         sta  icr     ;loeschen
1030:  c008  8d 04 dc         sta  timera  ;zaehlwerk auf
1040:  c00b  8d 05 dc         sta  timera+1 ;32639 einstellen
1050:  c00e  a2 00            ldx  #0
1060:  c010  8e 1a d0         stx  irmask  ;kein vic-interrupt
1070:  c013  ca               dex
1080:  c014  86 fb            stx  flag    ;zaehler
1090:  c016  a9 0b            lda  #11
1100:  c018  8d 11 d0         sta  screen  ;bildschirm ausblenden
1110:  c01b  85 fd            sta  line    ;zwischenspeicher zeile
1120:  c01d  a9 0e            lda  #14     ;hellblaue farbe
1130:  c01f  8d 21 d0         sta  grund
1140:  c022  85 fe            sta  line+1  ;wie 1110
                       ;
                       ;*** interrupt-beispiel bundesflagge ***
                       ;
1150:  c024  a9 2c            lda  #<irq1  ;zeiger
1160:  c026  a0 c1            ldy  #>irq1  ;auf
1170:  c028  8d 14 03         sta  vector  ;neue
1180:  c02b  8c 15 03         sty  vector+1 ;interrupt-routine
1190:  c02e  a0 01            ldy  #1
1200:  c030  8c 12 d0         sty  raster  ;irr-ausloeser rasterzeile 1
1210:  c033  8c 1a d0         sty  irmask  ;irr-maske auf rasterzeilen
1220:  c036  88               dey
1230:  c037  a9 19            lda  #25
1240:  c039  20 15 c1         jsr  time    ;zeitverzoegerung
1250:  c03c  a6 fe            ldx  line+1
1260:  c03e  a9 02    loop1   lda  #2
1270:  c040  20 15 c1         jsr  time
1280:  c043  e8               inx
1290:  c044  86 fe            stx  line+1  ;eine zeile weiter
1300:  c046  e0 c8            cpx  #200    ;bis zeile 200
1310:  c048  90 f4            bcc  loop1   ;kleiner - dann weiter
1320:  c04a  a6 fd            ldx  line    ;dito obere zeile
1330:  c04c  a9 02    loop2   lda  #2
1340:  c04e  20 15 c1         jsr  time
1350:  c051  e8               inx
1360:  c052  86 fd            stx  line
1370:  c054  e0 73            cpx  #115
1380:  c056  90 f4            bcc  loop2
                       ;
                       ;*** titel-einblendung ***
                       ;
1390:  c058  a2 07            ldx  #7
1400:  c05a  a0 0d            ldy  #13
1410:  c05c  18               clc
1420:  c05d  20 f0 ff         jsr  plot
1430:  c060  a9 c8            lda  #<intro ;stringadresse low
1440:  c062  a0 c1            ldy  #>intro ;und high
1450:  c064  20 1e ab         jsr  print   ;titel drucken
1460:  c067  a9 96            lda  #150
1470:  c069  a0 00            ldy  #0
1480:  c06b  20 15 c1         jsr  time
1490:  c06e  a9 1b            lda  #27
1500:  c070  8d 11 d0         sta  screen  ;bildschirm oeffnen
1510:  c073  a9 c8            lda  #200
1520:  c075  20 15 c1         jsr  time
                       ;
                       ;*** interrrupt-beispiel gleitzeile ***
                       ;
1530:  c078  8c 1a d0         sty  irmask  ;interrupt aus
1540:  c07b  8c 20 d0         sty  rand    ;schwarze umrandung
1550:  c07e  20 44 e5         jsr  clear
1560:  c081  a2 04            ldx  #4      ;farbe purpur
1570:  c083  8e 21 d0         stx  grund
1580:  c086  a0 03            ldy  #3
1590:  c088  18               clc
1600:  c089  20 f0 ff         jsr  plot
1610:  c08c  a9 fe            lda  #<text
1620:  c08e  a0 c1            ldy  #>text
1630:  c090  20 1e ab         jsr  print
1640:  c093  a9 1e            lda  #30
1650:  c095  a0 00            ldy  #0
1660:  c097  20 15 c1         jsr  time
1670:  c09a  a9 58            lda  #<irq2
1680:  c09c  a0 c1            ldy  #>irq2
1690:  c09e  8d 14 03         sta  vector  ;neue
1700:  c0a1  8c 15 03         sty  vector+1 ;interrupt-routine
1710:  c0a4  a2 19            ldx  #25
1720:  c0a6  86 fd            stx  line
1730:  c0a8  8e 12 d0         stx  raster
1740:  c0ab  ad 19 d0         lda  irequ
1750:  c0ae  8d 19 d0         sta  irequ   ;interrupt-flag loeschen
1760:  c0b1  a0 01            ldy  #1
1770:  c0b3  8c 1a d0         sty  irmask
1780:  c0b6  88               dey
1790:  c0b7  a9 02    loop3   lda  #2
1800:  c0b9  20 15 c1         jsr  time
1810:  c0bc  e8               inx
1820:  c0bd  86 fd            stx  line    ;eine zeile weiter
1830:  c0bf  e0 aa            cpx  #170
1840:  c0c1  90 f4            bcc  loop3
1850:  c0c3  a9 64            lda  #100
1860:  c0c5  20 15 c1         jsr  time
                       ;
                       ;*** interrrupt-beispiel regenbogen ***
1870:  c0c8  20 44 e5         jsr  clear
1880:  c0cb  8c 1a d0         sty  irmask
1890:  c0ce  a9 73            lda  #<irq3
1900:  c0d0  a0 c1            ldy  #>irq3
1910:  c0d2  8d 14 03         sta  vector
1920:  c0d5  8c 15 03         sty  vector+1
1930:  c0d8  ad 19 d0         lda  irequ
1940:  c0db  8d 19 d0         sta  irequ
1950:  c0de  a0 00            ldy  #0
1960:  c0e0  84 fb            sty  flag
1970:  c0e2  c8               iny
1980:  c0e3  8c 12 d0         sty  raster
1990:  c0e6  8c 1a d0         sty  irmask
2000:  c0e9  a9 00            lda  #0
2010:  c0eb  20 15 c1         jsr  time
2020:  c0ee  a9 0f            lda  #15     ;hellgraue
2030:  c0f0  8d 86 02         sta  chrcol  ;zeichenfarbe
2040:  c0f3  a2 03            ldx  #3
2050:  c0f5  18               clc
2060:  c0f6  20 f0 ff         jsr  plot
2070:  c0f9  a9 dd    loop4   lda  #<name
2080:  c0fb  a0 c1            ldy  #>name
2090:  c0fd  20 1e ab         jsr  print   ;text ausgeben
2100:  c100  a9 14            lda  #20
2110:  c102  a0 00            ldy  #0
2120:  c104  20 15 c1         jsr  time
2130:  c107  ce 86 02         dec  chrcol  ;farbe - 1
2140:  c10a  10 ed            bpl  loop4   ;weiter bis einschl. 0 (=schwarz)
2150:  c10c  a9 01            lda  #1
2160:  c10e  a8               tay
2170:  c10f  20 15 c1         jsr  time
2180:  c112  4c 00 c0         jmp  init    ;auf ein neues
                       ;
                       ;*** warteschleife ***
                       ;
9010:  c115  8d 06 dc time    sta  timerb  ;zaehler low
9020:  c118  8c 07 dc         sty  timerb+1 ;und high laden
9030:  c11b  a9 11            lda  #%00010001 ;force-load/continuous/systemtakt
9040:  c11d  8d 0e dc         sta  cra      ;timer a starten
9050:  c120  a9 59            lda  #%01011001 ;force-load/one-shot/takt timer a
9060:  c122  8d 0f dc         sta  crb      ;timer b starten
9070:  c125  ad 0f dc wait    lda  crb      ;b-control lesen
9080:  c128  4a               lsr           ;bit 0 ins carry schieben
9090:  c129  b0 fa            bcs  wait     ;timer b laeuft noch
9100:  c12b  60               rts           ;zurueck wenn aus
                       ;
                       ;*** interrupt-routinen ***
                       ;
10010: c12c  a5 fb    irq1    lda  flag     ;sektorenflag
10020: c12e  f0 09            beq  rot
10030: c130  10 0f            bpl  gold
10040: c132  a9 00            lda  #0
10050: c134  aa               tax
10060: c135  a4 fd            ldy  line
10070: c137  d0 0e            bne  exit
10100: c139  a9 02    rot     lda  #2
10110: c13b  a2 01            ldx  #1
10120: c13d  a4 fe            ldy  line+1
10130: c13f  d0 06            bne  exit
10200: c141  a9 07    gold    lda  #7
10210: c143  a2 ff            ldx  #255
10220: c145  a0 01            ldy  #1
10500: c147  86 fb    exit    stx  flag    ;zeiger auf sektor
10510: c149  8c 12 d0         sty  raster  ;naechste interruptzeile
10520: c14c  8d 20 d0         sta  rand    ;farbe setzen
10530: c14f  ad 19 d0         lda  irequ
10540: c152  8d 19 d0         sta  irequ   ;irq-flag loeschen
10550: c155  4c 81 ea         jmp  irqoff  ;und aussprung
                       ;
11000: c158  ad 21 d0 irq2    lda  grund
11010: c15b  49 02            eor  #%00000010 ;farb-flipflop 4/6
11020: c15d  a8               tay
11030: c15e  29 02            and  #%00000010
11040: c160  f0 06            beq  oben    ;purpur an der reihe
11050: c162  a5 fd            lda  line
11060: c164  18               clc
11070: c165  69 15            adc  #21     ;+ 21 rasterzeilen
11080: c167  2c               .byte$2c     ;= bit (versteckter ladebefehl)
11090: c168  a5 fd    oben    lda  line
11100: c16a  8d 12 d0         sta  raster  ;zeile und
11110: c16d  8c 21 d0         sty  grund   ;farbe setzen
11120: c170  4c 4f c1         jmp  exit+8  ;aussprung
                       ;
12000: c173  c6 fb    irq3    dec  flag
12010: c175  a6 fb            ldx  flag    ;offset fuer tabellen
12020: c177  10 04            bpl  getpar
12030: c179  a2 0f            ldx  #low-color-1
12040: c17b  86 fb            stx  flag    ;offset maximal
12050: c17d  bd 98 c1 getpar  lda  color,x ;farbe holen
12060: c180  48               pha          ;zunaechst auf stapel
12070: c181  bc b8 c1         ldy  high,x  ;rasterzeile highbyte
12080: c184  bd a8 c1         lda  low,x   ;und lowbyte
12090: c187  aa               tax
12100: c188  68               pla          ;farbe zurueckholen
12110: c189  8e 12 d0         stx  raster  ;zeile low
12120: c18c  8c 11 d0         sty  screen  ;und high
12130: c18f  8d 20 d0         sta  rand    ;und randfarbe und
12140: c192  8d 21 d0         sta  grund   ;hintergrundfarbe setzen
12150: c195  4c 4f c1         jmp  exit+8  ;fertig
                      ;
                      ;*** interrupt-tabellen ***
                      ;
15000: c198 04 0b 05 color    .byte4,11,5,9
15010: c19c 03 0a 01          .byte3,10,1,14
15020: c1a0 07 0f 02          .byte7,15,2,13
15030: c1a4 00 08 06          .byte0,8,6,12
                      ;
16000: c1a8 01 0b fe low      .byte1,11,254,235
16010: c1ac db cb b3          .byte219,203,179,162
16020: c1b0 8a 7a 6a          .byte138,122,106,82
16030: c1b4 4b 3b 33          .byte75,59,51,33
                      ;
17000: c1b8 1b 9b 1b high     .byte27,155,27,27
17010: c1bc 1b 1b 1b          .byte27,27,27,27
17020: c1c0 1b 1b 1b          .byte27,27,27,27
17030: c1c4 1b 1b 1b          .byte27,27,27,27
                      ;
                      ;*** tabellen hauptprogramm ***
                      ;
20000: c1c8 0e 08 1f intro    .byte14,8,31
20010: c1cb c4 45 4d          .asc "Demo-Programm"
20020: c1d8 11 11 11          .byte17,17,17,17,17
20030: c1dd 0d 1d 1d name     .byte13,29,29,29,29
20040: c1e2 1d 1d 1d          .byte29,29,29,29,29
20050: c1e7 d2 c1 d3          .asc "RASTERZEILEN-Interrupt"
20060: c1fd 00                .byte0
                      ;
21000: c1fe 9c       text     .byte156
21010: c1ff d5 4e 44          .asc "Und nun    "
21020: c20a 11 11 11          .byte17,17,17
21030: c20d 53 45 48          .asc "sehen Sie    "
21040: c21a 11 11 11          .byte17,17,17
21050: c21d 4e 4f 43          .asc "noch den"
21060: c225 0d 11 11          .byte13,17,17,17,17,17
21070: c22b 1d 1d 1d          .byte29,29,29,29,29
21080: c230 1d 1d 1d          .byte29,29,29,29,29
21090: c235 d2 a0 c5          .asc "R E G E N B O G E N"
21100: c248 00                .byte0

end of assembly $c000 - $c248

no errors
Assemblerlisting des »Rasterzeilen-Interrupt«
PDF Diesen Artikel als PDF herunterladen
Mastodon Diesen Artikel auf Mastodon teilen
← Vorheriger ArtikelNächster Artikel →