Magic Disk 64

home to index to html: MD9312-KURSE-IRQ-KURS_2.2.html
       (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)
Valid HTML 4.0 Transitional Valid CSS!