(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)