Magic Disk 64

home to index to html: MD9401-KURSE-IRQ-KURS_3.1.html
             Interrupt-Kurs             
     "Die Hardware ausgetrickst..."     
                (Teil 3)                
----------------------------------------
Herzlich  willkommen  zum  dritten  Teil
unseres Interruptkurses. In diesem Monat
möchten  wir endlich in "medias res" ge-
hen und in den interessantesten  Teilbe-
reich  der  Interruptprogrammierung ein-
steigen: den Raster-Interrupt.          
1) WAS IST EIN RASTER-INTERRUPT         
Raster-Interrupts  sind  Interrupts  die
vom Videochip des C64 ausgelöst  werden.
Er  ist die dritte hardwaremäßige Inter-
rupptquelle und bietet  uns  vielfältige
Möglichkeiten  um besonders interessante
und schöne Interrupt-Effekte erzeugen zu
können. Zunächst jedoch wollen wir  klä-
ren,  was  so  ein  Raster-Interrupt nun
eigentlich ist. Hierzu wollen wir zuerst
einmal die Funktionsweise  eines  Compu-
termonitors  oder  Fernsehers  in groben
Zügen besprechen:                       
Der Bildschirm, so wie Sie ihn jetzt vor
sich haben, ist an seiner Innenseite mit
einem  Stoff  beschichtet,  der, wenn er
von  Elektronen  getroffen   wird,   zum
Leuchten  angeregt  wird.  Innerhalb des
Monitors befindet sich nun eine Elektro-
nenquelle,   die,   durch   Magnetfelder
gebündelt, einen hauchdünnen Strahl  von
Elektronen  erzeugt.  Zusätzlich gibt es
noch zwei Ablenkmagnete, die den  Strahl
in   der   Horizontalen  und  Vertikalen
ablenken können, so  daß  jede  Position
auf dem Bildschirm erreicht werden kann.
Der  Elektronenstrahl wird nun durch ein
bestimmtes  Bewegungsschema  zeilenweise
über  den  Bildschirm  bewegt und bringt
selbigen zum Leuchten. Hierbei  ist  der
gesamte  Bildschirm  in  313  sogenannte
Rasterzeilen aufgeteilt. Jedesmal,  wenn
eine  Zeile von links nach rechts aufge-
baut wurde, wird der  Stahl  kurzfristig
abgeschaltet  und  am linken Bildschirm-
rand wieder angesetzt,  um  die  nächste
Zeile  zu  zeichnen.  Dies geschieht mit
einer unglaublichen Geschwindigkeit,  so
daß  für das sehr träge Auge ein Gesamt-
bild aus unzählligen  Leuchtpunkten  auf
dem Bidschirm entsteht. Insgesamt 25 Mal
pro Sekunde 'huscht' der Elektronenstahl
auf  diese  Weise  über  den Bildschirm,
wodurch 25 Bilder pro Sekunde  aufgebaut
werden.  Damit  nun  ein ganz spezielles
Videobild auf dem  Bildschirm  zu  sehen
ist,  z.B.  der Text, den Sie gerade le-
sen, benötigt man eine Schaltlogik,  die
die  Text-  oder  Grafikzeichen  aus dem
Speicher  des  Computers  in   sichtbare
Bildpunkte  umwandelt.  Diese Umwandlung
geschieht durch den Video-Chip des  C64,
dem  sogenannten VIC. Er erzeugt Steuer-
signale für den Rasterstrahl, die  ange-
ben  in  welcher  Rasterzeile  er gerade
arbeiten soll, und  mit  welcher  Inten-
sität  die  einzelnen Bildpunkte auf dem
Bildschirm leuchten sollen. Helle  Bild-
punkte  werden  dabei mit mehr, dunklere
mit weniger Elektronen 'beschossen'.  Da
der  VIC  nun auch die Steuersignale für
den Rasterstrahl erzeugt, weiß  er  auch
immer, wo sich selbiger gerade befindet.
Und das ist der Punkt an dem wir einset-
zen können, denn der VIC bietet uns  nun
die Möglichkeit, diese Strahlposition in
einem  seiner Register abzufragen. Damit
wir nun aber nicht ständig  prüfen  müs-
sen,  in  welcher  der  313 Rasterzeilen
sich der Strahl nun befindet, können wir
ihm auch gleich mitteilen, daß er  einen
Interrupt bei Erreichen einer bestimmten
Rasterzeile   auslösen  soll.  Dies  ge-
schieht   über   spezielle    Interrupt-
Register,  deren  Belegung ich Ihnen nun
aufführen möchte.                       
2) DIE RASTER-IRQ-PROGRAMMIERUNG        
Zunächst  einmal  brauchen  wir die Mög-
lichkeit,  die  gewünschte   Rasterzeile
festlegen  zu  können.  Dies wird in den
Registern 17 und 18 (Adressen $D011  und
$D012) des VICs angegeben. Da es ja ins-
gesamt 313 Rasterzeilen gibt reicht näm-
lich  ein  8-Bit-Wert nicht aus, um alle
Zeilen angeben  zu  können.  Aus  diesem
Grund  ist  ein zusätzliches neuntes Bit
in VIC-Register 17 untergebracht. Bit  7
dieses  Registers  gibt zusammen mit den
acht Bits aus VIC-Register  18  nun  die
gewünschte  Rasterzeile an. Da die ande-
ren Bits dieses Registers ebenfalls  ei-
nige Aufgaben des VICs ausfüllen, dürfen
wir  nur  durch  logische  Verknüpfungen
darauf zugreifen. Möchten wir also  eine
Rasterzeile von 0 bis 255 als Interrupt-
auslöser definieren, so muß das  7.  Bit
mit den folgenden drei Befehlen gelöscht
werden.   Die  Rasterzeilen-Nummer  wird
dann ganz normal in Register 18  ($D012)
geschrieben:                            
LDA $D011  ;Reg.17 lesen                
AND #$7F   ;7. Bit löschen              
STA $D011  ;Reg.17 zurückschreiben      
Wenn nun eine Rasterzeile größer als 255
in  die  beiden VIC-Register geschrieben
werden soll, so müssen wir mit den  fol-
genden drei Befehlen das 7. Bit in Regi-
ster $D011 setzen, und  die  Nummer  der
Rasterzeile  minus dem Wert 256 in Regi-
ster $D012 schreiben:                   
LDA $D011  ;Reg.17 lesen                
ORA $80    ;7. Bit setzen               
STA $D011  ;Reg.17 zurückschreiben.     
Um  z.B.  Rasterzeile 300 als Interrupt-
auslöser einzustellen,  müssen  wir  wie
eben  gezeigt das 7. Bit in $D011 setzen
und anschließend den Wert 300-256=44  in
$D012 ablegen.                          
Das alleinige Festlegen einer Rasterzei-
le als Interruptauslöser  genügt  jedoch
noch  nicht, den VIC dazu zu bringen den
Prozessor beim Erreichen dieser Zeile zu
unterbrechen. Wir müssen ihm  zusätzlich
noch  mitteilen,  daß er überhaupt einen
Interrupt auslösen soll.  Dies  wird  in
Register  26 (Adresse $D01A) festgelegt.
Es  ist  das  Interrupt-Control-Register
(ICR)  des  VIC  und  hat  eine ähnliche
Funktion wie das ICR der CIA, das wir im
letzten  Kursteil  schon   kennengelernt
hatten.                                 
Das  VIC-ICR  kennt  vier   verschiedene
Ereignisse,  die  den  VIC  zum Auslösen
eines  Interrupts  bewegen.  Jedes   der
Ereignisse  wird  durch  ein zugehöriges
Bit im ICR repräsentiert, das  lediglich
gesetzt werden muß, um das entsprechende
Ereignis als Interruptquelle zu definie-
ren. Hier die Belegung:                 
Bit  Interruptquelle ist                
----------------------------------------
 0   Rasterstrahl                       
 1   Kollision Sprites und Hintergrund  
 2   Kollision zwischen Sprites         
 3   Lightpen sendet Signal             
Wie Sie sehen ist für uns  hauptsächlich
Bit  0  von Bedeutung. Setzen wir es, so
löst der VIC  bei  Erreichen  der  zuvor
festgelegten Rasterzeile einen Interrupt
aus.  Sie  sehen  sicherlich  auch,  daß
Sprite-Kollisionen mit Hintergrundgrafik
und/oder mit anderen Sprites ebenso  ei-
nen  Interrupt auslösen können. Sie kön-
nen auf diese Weise also auch sehr  kom-
fortabel  eine Kollision über den Inter-
rupt abfragen. Bit 3 wird nur sehr  sel-
ten  benutzt  und  bringt eigentlich nur
etwas in Zusammenhang mit  einem  Light-
pen.  Selbiger sendet nämlich immer dann
ein Signal, wenn der Rasterstrahl gerade
an ihm vorübergezogen ist. Wird  so  nun
ein  Interrupt  ausgelöst,  muß das ent-
sprechende  Lightpen-Programm  nur  noch
auswerten, an welcher Rasterposition der
Strahl  sich gerade befindet, um heraus-
zufinden, an welcher  Stelle  des  Bild-
schirms sich der Lightpen befindet. Dies
soll  uns  hier jedoch nicht interessie-
ren.                                    
Wir müssen also lediglich Bit 0 von $D0-
1A  durch Schreiben des Wertes 1 setzen,
um Raster-Interrupts auslösen zu können.
3) DAS HANDLING DER RASTER-INTERRUPTS   
Hardwaremäßig haben wir nun alles  fest-
gelegt  und  den  VIC auf die Interrupt-
Erzeugung  vorbereitet.  Was  nun   noch
fehlt ist die Software die den Interrupt
bedient.  Auch dies ist für uns im Prin-
zip ein alter Hut, denn der VIC ist  mit
der  IRQ-Leitung  des Prozessors verbun-
den, weswegen wir einen Raster-Interrupt
im Prinzip wie  den  CIA1-Interrupt  des
Betriebssystems  abfragen können. Hierzu
brauchen wir natürlich erst einmal  eine
Initialisierungsroutine,  die  den  IRQ-
Vektor bei $0314/$0315 auf unsere eigene
Interrupt-Routine verbiegt. Hierbei müs-
sen  wir  jedoch  beachten,  daß der Be-
triebssystem-Interrupt, der von Timer  A
der  CIA1 ausgelöst wird, ebenfalls über
diesen Vektor springt. Das bedeutet, daß
unsere Routine  zwischen  diesen  beiden
Interrupts  unterscheiden  muß,  da  ein
Raster-IRQ anders  bedient  werden  muß,
als  ein  CIA-IRQ. Würden wir keinen Un-
terschied machen, so würde uns  die  CIA
ungehemmt   'dazwischenfunken'  und  der
Rasterinterrupt  würde  außer  Kontrolle
geraten. Bevor ich mich nun aber in the-
roretischen Fällen verliere,  sehen  Sie
sich  einmal  folgendes Beispielprogramm
an, anhand dessen wir die Lösung  dieses
Problems besprechen möchten:            
;*** Initialisierung                    
Init:                                   
  sei          ;IRQs sperren            
  lda #$00     ;Rasterzeile 0 als Inter-
  sta $d012    ; ruptauslöser festlegen 
  lda $d011    ; (höchstwertiges Bit    
  and #$7f     ;  der Rasterzeile       
  sta $d011    ;  löschen)              
  lda #$01     ;Rasterstrahl als Inter- 
  sta $d01a    ; ruptquelle festlegen)  
  lda #3       ;Zähler in $02 mit dem   
  sta $02      ; Wert 3 initialisieren) 
  ldx #<(irq)  ;IRQ-Vektor bei $0314/   
  ldy #>(irq)  ; $0315 auf eigene       
  stx $0314    ; Routine mit dem Namen  
  sty $0315    ; "IRQ" verbiegen.       
  cli          ;IRQs wieder erlauben    
  rts          ;ENDE!                   
;*** Interrupt-Routine                  
irq:                                    
  lda $d019    ;VIC-IRR lesen           
  sta $d019    ; und zurückschreiben    
  bmi raster   ;Wenn 7.Bit gesetzt, war 
               ; es ein Raster-IRQ      
  jmp $ea31    ;Sonst auf Betriebssystem
               ; Routine springen       
raster:                                 
  dec $02      ;Zähler runterzählen     
  lda $02      ;Zähler lesen            
  cmp #2       ;Zähler=2?               
  bne rast70   ;Nein, also weiter       
  lda #$70     ;Sonst Zeile $70 als neu-
  sta $d012    ; en Auslöser festlegen  
  lda #$00     ;Rahmen- und Hintergrund-
  sta $d020    ; farbe auf 'schwarz'    
  sta $d021    ; setzen                 
  jmp $febc    ;IRQ beenden             
rast70:                                 
  cmp #1       ;Zähler=1?               
  bne rastc0   ;Nein, also weiter       
  lda #$c0     ;Sonst Zeile $c0 als neu-
  sta $d012    ; en Auslöser festlegen  
  lda #$02     ;Rahmen- und Hintergrund-
  sta $d020    ; farbe auf 'rot'        
  sta $d021    ; setzen                 
  jmp $febc    ;IRQ beenden             
rastc0:                                 
  lda #$00     ;Wieder Zeile 0 als Aus- 
  sta $d012    ; löser festlegen        
  lda #$07     ;Rahmen- und Hintergrund-
  sta $d020    ; farbe auf 'gelb'       
  sta $d021    ; setzen                 
  lda #3       ;Zähler wieder auf 3     
  sta $02      ; zurücksetzen           
  jmp $febc    ;IRQ beenden             
Sie finden das Programm  auch  als  aus-
führbaren   Code   unter    dem    Namen
"RASTERDEMO1"  auf  dieser  MD.  Es  muß
absolut (mit  ",8,1")  geladen  und  mit
"SYS4096" gestartet werden. Möchten  Sie
es sich ansehen, so disassemblieren  Sie
mit einem Monitor ab Adresse $1000.     
Unser erster Raster-Interrupt  soll  nun
auf  dem  Bildschirm  eine  Schwarz-Rot-
Gold-Flagge darstellen. Zu diesem  Zweck
lassen wir den VIC in den  drei  Raster-
zeilen $00, $70 und $C0 einen  Interrupt
auslösen, woraufhin wir die Rahmen-  und
Hintergrundfarbe in eine der drei Farben
ändern. Gehen  wir  jedoch  Schritt  für
Schritt vor und schauen wir uns zunächst
einmal die Initialisierungsroutine an:  
Hier schalten wir  zunächst  einmal  die
IRQs mittels  des  SEI-Befehls  ab,  und
setzen anschließend auf  die  schon  be-
schriebene Art  und  Weise  die  Raster-
zeile 0 als Inter- ruptauslösende  Zeile
fest. Hieraufhin wird der  Wert  $01  in
Register $D01A  geschrieben,  womit  wir
die  Rasterinterrupts  erlauben.  Danach
wird noch  ein  Zähler  in  Adresse  $02
initialisert, den wir zum  Abzählen  der
Raster-Interrupts benötigen,  damit  wir
auch immer die  richtige  Farbe  an  der
richtigen Rasterposition setzen. Zuletzt
wird der IRQ-Vektor bei $0314/$0315  auf
unsere Routine verbogen und  nach  einem
abschließenden Freigeben der  IRQs  wird
die  Initialisierung  beendet.  Von  nun
an wird bei jedem IRQ, stammt er nun vom
VIC oder der CIA, die den Betriebssystem
IRQ erzeugt, auf  unsere  Routine  "IRQ"
gesprungen.                             
Damit der  Raster-Interrupt  nun  jedoch
nicht,  wie  oben  schon   angesprochen,
außer Kontrolle gerät, müssen  wir  hier
nun als erstes herausfinden, von welchem
der  beiden  Chips  der  IRQ   ausgelöst
wurde. Dies geschieht durch Auslesen des
Registers 25 des VICs  (Adresse  $D019).
Es    das     Interrupt-Request-Register
dieses Chips, in dem das 7.  Bit,  sowie
das entsprechende  Bit  des  ICR,  immer
dann gesetzt werden, wenn eines der vier
möglichen Interrupt-Ereignisse des  VICs
eingetreten ist. Durch das Lesen  dieses
Registers holen wir uns also seinen Wert
in den Akku. Das  Anschließende  zurück-
schreiben löscht  das  Register  wieder.
Dies  ist  notwendig,  da  der  VIC  den
soeben in diesem Register erzeugten Wert
solange  hält,  bis  ein  Schreibzugriff
darauf durchgeführt wird.  Dadurch  wird
sichergestellt, daß die  Interruptanfor-
derung  auch   tatsächlich   bei   einem
behandelnden  Programm  angelangt   ist.
Würden wir nicht schreiben, so würde der
VIC den nächsten  Interrupt  schlichtweg
ignorieren, da er den alten ja noch  für
nicht  abgearbeitet  interpretiert.  Der
Schreibzugriff auf das IRR des VICs ent-
spricht  im   Prinzip   also   derselben
Funktion, wie der  Lesezugriff  des  ICR
einer CIA, wenn von dort  ein  Interrupt
ausgelöst wurde.                        
Im Akku befindet sich nun also der  Wert
aus dem IRR. Gleichzeitig mit dem  Lese-
vorgang wurden die dem Wert entsprechen-
den   Flags   im   Statusregister    des
Prozessors gesetzt. Wir müssen nun  also
lediglich prüfen, ob das 7.  Bit  dieses
Wertes gesetzt ist,  um  herauszufinden,
ob es sich um einen vom  VIC  stammenden
Raster-IRQ  handelt,  oder  nicht.  Dies
geschieht  durch  den   folgenden   BMI-
Befehl, der nur dann verzweigt, wenn ein
negativer Wert das Ergebnis der  letzten
Operation war.  Da  ein  negativer  Wert
aber immer das 7. Bit gesetzt hat,  wird
also nur in diesem Fall auf  die  Unter-
routine "RASTER" verzweigt.  Im  anderen
Fall springen wir mit einem "JMP $EA31",
die Betriebssystem-IRQ-Routine an.      
War das 7. Bit nun also gesetzt, so wird
ab dem Label "RASTER"  fortgefahren,  wo
wir zunächst einmal  unseren  Zähler  in
$02 um 1 erniedrigen, und  ihn  dann  in
den Akku holen. Steht dort nun der  Wert
2, so wurde der  Interrupt  von  Raster-
zeile 0 ausgelöst, und wir setzen  Zeile
$70 als nächsten Auslöser fest.  Hierbei
müssen wir Bit 7 von $D011  nicht  extra
löschen, da es von der Initialisierungs-
routine her ja noch auf  0  ist.  Danach
werden Rahmen-  und  Hinter-  grundfarbe
auf 'schwarz' gesetzt und der  Interrupt
durch einen  Sprung  auf  Adresse  $FEBC
beendet. Hier befindet sich das Ende des
Betriebssystems-IRQ-Programms,  in   dem
die Prozessorregister  wieder  zurückge-
holt   werden,   und   mit   Hilfe   des
RTI-Befehls    aus     dem     Interrupt
zurückgekehrt wird.                     
Steht in unserem Zähler bei $02 nun  der
Wert 1, so wurde der Interrupt von Zeile
$70 ausgelöst. Hier setzen wir die Bild-
schirmfarben nun  auf  'rot'  und  legen
Zeile  $C0  als  Auslöser  des  nächsten
Raster-Interrupts fest.  Tritt  selbiger
auf, so steht weder 1 noch 2 in  unserem
Zählregister,   weswegen   zur   Routine
"RASTC0"  verzweigt  wird,  wo  wir  die
Rahmenfarbe  auf  'gelb'   setzen,   den
Zähler  wieder  mit   seinem   Startwert
initialisieren  und  Rasterzeile  0  als
nächsten  Interruptauslöser   festlegen,
womit sich der ganze Prozeß  wieder  von
vorne wiederholt.                       
  (Bitte laden Sie nun den 2.Teil dieses
        Artikels aus dem Textmenu)      
Valid HTML 4.0 Transitional Valid CSS!