(Fortsetzung Interruptkurs)
--- Initialisierung
1000: LDA #$01 ;Sprite 0
1002: STA $D015 ; einschalten und
1005: STA $D027 ; Farbe auf Weiß
1008: LDA #$3F ;Sprite-Pointer auf
100A: STA $07E8 ; $3F*$40=$0FC0
100D: LDA #$64 ;X-/Y-Position des
100F: STA $D000 ; Sprites auf
1012: STA $D001 ; 100/100 setzen
1015: LDX #$0E ;Rahmen- und Hinter-
1017: LDY #$06 ; grundfarbe auf
1019: STX $D020 ; Hellblau und
101C: STY $D021 ; Dunkelblau
101F: LDA #$7F ;CIA-B-Interrupt-Quellen
1021: STA $DD0D ; sperren
1024: LDA #$00 ;Timer A von CIA-B
1026: STA $DD0E ; anhalten.
1029: LDX #$48 ;Startadresse für neuen
102B: LDY #$10 ; NMI (=$1048) im
102D: STX $0318 ; Pointer bei $0318/0319
1030: STY $0319 ; ablegen.
1033: LDX #$49 ;Timerzählwert=$8049
1035: LDY #$80 ; (30 Mal pro Sekunde)
1037: STX $DD04 ; in Timerzählerregister
103A: STY $DD05 ; (Timer A) schreiben
103D: LDA #$81 ;Timer A als Interrupt-
103F: STA $DD0D ; quelle erlauben.
1042: LDA #$11 ;Timer starten (mit
1044: STA $DD0E ; FORCE-LOAD-Bit)
1047: RTS ;ENDE
--- Interruptroutine
1048: PHA ;Akku retten
1049: TXA ;X in Akku
104A: PHA ; und retten
104B: TYA ;Y in Akku
104C: PHA ; und retten
104D: LDA $DD0D ;ICR auslesen
1050: BNE $1058 ;Wenn<>0, dann CIA-NMI
1052: INC $D020 ;Sonst NMI von RESTORE-
;Taste. Rahmenfarbe erh.
1055: JMP $1073 ;Und auf Ende NMI spr.
1058: LDA $DC01 ;CIA-NMI: Joyport2 ausl.
105B: LSR ;Bit in Carry-Flag rot.
105C: BCS $1061 ;Wenn gelöscht Joy hoch
105E: DEC $D001 ;SpriteX-1
1061: LSR ;Bit in Carry-Flag rot.
1062: BCS $1067 ;Wenn gel., Joy runter
1064: INC $D001 ;SpriteX+1
1067: LSR ;Bit in Carry-Flag rot.
1068: BCS $106D ;Wenn gel. Joy rechts
106A: INC $D000 ;SpriteY+1
106D: LSR ;Bit in Carry-Flag rot.
106E: BCS $1073 ;Wenn gel. Joy links
1070: DEC $D000 :SpriteY-1
1073: PLA ;Y-Reg vom Stapel
1074: TAY ; holen
1075: PLA ;X-Reg vom Stapel
1076: TAX ; holen
1077: PLA ;Akku vom Stapel holen
1078: CLI ;IRQs wieder erlauben
1079: RTI ;RETURN FROM INTERRUPT
--- Eigenen NMI entfernen
107A: LDA #$7F ;Alle NMI-Quellen von
107C: STA $DD0D ; CIA-B sperren
107F: LDA #$00 ;Timer A von CIA-B
1081: STA $DD0E ; stoppen
1084: LDX #$47 ;Alten NMI-Vektor
1086: LDY #$FE ; (bei $FE47) wieder in
1088: STX $0318 ; $0318/$0319
108B: STY $0319 ; eintragen
108E: LDA #00 ;Sprite 0
1090: STA $D015 ; ausschalten
1093: RTS ;ENDE
Das Programm starten Sie mit SYS4096.
Hieraufhin sehen Sie einen Sprite-Pfeil
auf dem Bildschirm, der mit einem Joy-
stick in Port 2 über den Bildschirm be-
wegt werden kann. Gleichzeitig ist je-
doch weiterhin die normale Tastaturab-
frage des C64 aktiv. Die beiden Prozesse
laufen scheinbar gleichzeitig ab, weil
wir einen zweiten Interrupt generiert
haben, der den Joystick abfragt und ent-
sprechend das Sprite bewegt. Kommen wir
nun zur Programmbeschreibung:
In den ersten Zeilen, von $1000 bis
$101E wird zunächst einmal das Sprite
initialisiert und die Bildschirmfarben
auf die gängige Kombination hellblau/
dunkelblau gesetzt. Hiernach folgt die
NMI- und Timerinitialisierung. Da wir
auch hier verhindern müssen, daß ein NMI
auftritt, während wir den NMI-Vektor
verändern, wird der Wert $7F in das ICR
geschrieben. Dieser Wert löscht alle
Interruptquellen-Bits, womit kein NMI
mehr von CIA-B ausgelöst werden kann. Da
sie der einzige Chip ist, der NMIs
auslöst, können wir sicher sein, daß
tatsächlich keiner dieser Interrupts
aufgerufen wird. Beachten Sie bitte, daß
der SEI-Befehl hier wirkungslos wäre,
denn er sperrt lediglich die IRQs. NMIs
sind nicht abschaltbar, weswegen wir sie
auf diese umständliche Art und Weise
unterbinden müssen. Hiernach halten wir
Timer A an, indem wir einen Wert in CRA
schreiben, der das 0. Bit gelöscht hat,
woraufhin die CIA den Timer stoppt. Dies
müssen wir tun, damit der Timer korrekt
mit seinem Anfangszählwert gefüttert
werden kann. Würden wir nämlich das
Low-Byte geschrieben haben, das High-
Byte jedoch noch nicht, während ein Un-
terlauf des Timers stattfindet, so würde
er mit sich mit einem falschen Anfangs-
wert initialisieren.
In den folgenden Zeilen wird nun der
NMI-Vektor bei $0318/$0319 auf unsere
eigene NMI-Routine bei $1048 verbogen.
Hiernach wird der Timer mit seinem Zähl-
wert initialisiert. Er beträgt $8049.
Wie wir auf diesen Wert kommen, möchte
ich Ihnen nun erläutern: Wir hatten ja
schon festgestellt, daß wir den Timer
vorwiegend die Takzyklen des 64ers zäh-
len lassen möchten. Selbige sind der
Taktgeber für den Prozessor. Der Quarz
der sie erzeugt generiert zusammen mit
einigen Bausteinen um ihn herum einen
Takt von genau 985248.4 Impulsen pro
Sekunde, die auch in Herz (Hz) gemessen
werden. Dieser Wert erntspricht also
annähernd einem MHz. Um nun zu ermit-
teln, wieviele Taktzyklen gezählt werden
müssen, damit der Timer genau 30 Mal pro
Sekunde einen Interrupt erzeugt, müssen
wir diesen Wert lediglich durch 30 divi-
dieren. Das Ergebnis hiervon ist genau
32841.6, was $8049 entspricht! Um nun
z.B. 50 Interrupts pro Sekunde zu erzeu-
gen dividieren Sie einfach durch 50,
usw. Auf diese Weise können Sie die Joy-
stickabfrage auch beschleunigen oder
verlangsamen, denn das Sprite wird bei
betätigtem Joystick immer um genau so-
viele Pixel pro Sekunde verschoben, wie
Interrupts auftreten. Versuchen Sie die
Timerwerte doch einmal auf 100 oder nur
20 Interrupts pro Sekunde verändern!
Nachdem der Timer nun mit seinem Start-
wert gefüttert wurde, erlauben wir den
Timer-A-Interrupt durch Setzen des 0.
Bits im ICR. Hiernach muß der Timer nur
noch gestartet werden. Damit er gleich
beim richtigen Wert beginnt zu zählen,
ist hier außer dem START/STOP-Bit auch
noch das FORCE-LOAD-Bit gesetzt. Das 3.
Bit ist auf 0, damit der Timer im CONTI-
NOUS-Modus läuft. Ebenso wie das 5. Bit,
das ihm sagt, daß er Taktzyklen zählen
soll. Alles in allem wird also der Wert
$11 in CRA geschrieben. Von nun an läuft
der Timer und wird bei einem Unterlauf
einen Interrupt erzeugen, der unsere
NMI-Routine bei $1048 anspringen wird.
Kommen wir nun zu dieser Routine selbst.
Zu Anfang müssen wir erst einmal die
drei Prozessorregister retten, da wir ja
einen NMI programmiert haben, bei dem
uns die Betriebsystemsvorbeitungsroutine
diese Arbeit noch nicht abgenommen hat
(im Gegensatz zu der IRQ-Routine). Hier-
nach wird das ICR von CIA-B ausgelesen.
Dadurch ermöglichen wir es diesem Chip
beim nächsten Timerunterlauf erneut ei-
nen Interrupt auslösen zu können.
Gleichzeitig können wir dadurch abfra-
gen, ob der NMI tatsächlich von der CIA
kam. Dies ist zwar sehr wahrscheinlich,
da die CIA der einzige Chip ist, der
diese Art von Interrupt erzeugen kann,
jedoch ist die RESTORE-Taste auf der
Tastatur des C64 ebenso mit dem NMI-
Eingang des Prozessors verbunden. Das
heißt also, daß durch Drücken dieser
Taste ebenfalls ein NMI ausgelöst wird.
In dem Fall erhalten wir beim Auslesen
des ICR den Wert 0, da ja kein CIA-
Interrupt auftrat. Dadurch können wir
abfragen, ob es sich um unseren Timer-
NMI, oder einen RESTORE-Tastendruck han-
delt. Ist letzterer der Auslöser, so
wird einfach die Rahmenfarbe um eins
erhöht, und zum Ende des NMIs weiterge-
prungen. Handelt es sich um einen CIA-
NMI, so wird direkt zur Joystickabfrage
weiterverzweigt, in der wir Bitweise die
Portbits von CIA-A auswerten. Öbrigens:
da in dem gelesenen Register auch Daten-
bits der Tastatur erscheinen, kann es
passieren, daß Sie bei bestimmten Ta-
stendrücken ebenfalls das Sprite über
den Bildschirm zucken sehen. Dies ist
normal und handelt sich nicht um einen
Fehler!
Zum Abschluß der Routine holen wir die
drei Prozessorregister wieder vom Stapel
und erlauben mittels CLI die IRQs wie-
der. Selbige wurden nämlich in der Be-
triebsystemsvorbeitung zum NMI abge-
schaltet (sh. Teil1 dieses Kurses). Das
Programm wird diesmal nicht mit einem
Sprung auf die NMI-Behandlungsroutine
des Betriebssystems beendet, da diese
ausschließlich für die Behandlung der
RESTORE-Taste benutzt wird und demnach
bei einfachem Drücken der RUN-STOP-Taste
sofort einen BASIC-Warmstart auslösen
würde (so wird nämlich die Tastenkombi-
nation RUN-STOP/RESTORE abgefragt).
Am Ende des Programmbeispiels sehen Sie
noch eine Routine, die unsere NMI-
Routine wieder aus dem System entfernt.
Sie sperrt wieder alle NMI-Interrupts
von CIA-B und stoppt ihren Timer A. An-
schließend wird der NMI-Vektor bei
$0318/$0319 wieder auf die ursprüngliche
Adresse, nämlich $FE47, zurückgebogen.
Das war es nun für diesen Monat. Probie-
ren Sie ein wenig mit den Timerinter-
rupts herum, und versuchen Sie z.B. ei-
nen solchen Interrupt von Timer B auslö-
sen zu lassen. Prüfen Sie auch einmal,
was passiert, wenn Sie andere Timerwerte
benutzen. Ich empfehle Ihnen, eine Rou-
tine zu schreiben, die immer nach genau
$4CC7 Taktzyklen ausgelöst werden soll
und dann für eine kurze Zeit die Rahmen-
farbe verändert. Sie werden hierbei
schon einmal einen Vorgeschmack auf den
nächsten Kursteil bekommen, in dem wir
uns endlich dann den Rasterinterrupts
nähern werden.
(ub)