Magic Disk 64

home to index to text: MD9312-KURSE-IRQ-KURS_2.2.txt

( 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 Joystick in Port 2 über den Bildschirm bewegt werden kann. Gleichzeitig ist jedoch weiterhin die normale Tastaturabfrage des C64 aktiv. Die beiden Prozesse laufen scheinbar gleichzeitig ab, weil wir einen zweiten Interrupt generiert haben, der den Joystick abfragt und entsprechend das Sprite bewegt. Kommen wir nun zur Programmbeschreibung:
In den ersten Zeilen, von $1000 bis $101 E wird zunächst einmal das Sprite initialisiert und die Bildschirmfarben auf die gängige Kombination hellblau/ dunkelblau gesetzt. Hiernach folgt die NMIund Timerinitialisierung. Da wir auch hier verhindern müssen, daß ein NMI auftritt, während wir den NMI-Vektor verändern, wird der Wert $7 F 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 Unterlauf des Timers stattfindet, so würde er mit sich mit einem falschen Anfangswert 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ählwert 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 64 ers zählen 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 ermitteln, 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 dividieren. 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 Joystickabfrage auch beschleunigen oder verlangsamen, denn das Sprite wird bei betätigtem Joystick immer um genau soviele 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 Startwert 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) . Hiernach wird das ICR von CIA-B ausgelesen.
Dadurch ermöglichen wir es diesem Chip beim nächsten Timerunterlauf erneut einen Interrupt auslösen zu können.
Gleichzeitig können wir dadurch abfragen, 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 handelt. Ist letzterer der Auslöser, so wird einfach die Rahmenfarbe um eins erhöht, und zum Ende des NMIs weitergeprungen. 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 Datenbits der Tastatur erscheinen, kann es passieren, daß Sie bei bestimmten Tastendrü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 wieder. Selbige wurden nämlich in der Betriebsystemsvorbeitung zum NMI abgeschaltet ( 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 Tastenkombination 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. Anschließ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. Probieren Sie ein wenig mit den Timerinterrupts herum, und versuchen Sie z. B. einen 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 $4 CC7 Taktzyklen ausgelöst werden soll und dann für eine kurze Zeit die Rahmenfarbe 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)

Valid HTML 4.0 Transitional Valid CSS!