Interrupt-Kurs             
     "Die Hardware ausgetrickst..."     
                (Teil 3)                
 Herzlich  willkommen  zum  dritten  Teil
 unseres  Interruptkurses. In  diesem  Monat
 möchten  wir  endlich  in " medias  res" gehen  und  in  den  interessantesten  Teilbereich  der  Interruptprogrammierung  einsteigen: 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  Interrupptquelle  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  Computermonitors  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  Elektronenquelle, 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  Bildschirmrand  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  Gesamtbild  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  lesen, benötigt  man  eine  Schaltlogik, die
 die  Textoder  Grafikzeichen  aus  dem
 Speicher  des  Computers  in  sichtbare
 Bildpunkte  umwandelt. Diese  Umwandlung
 geschieht  durch  den  Video-Chip  des  C64, dem  sogenannten  VIC. Er  erzeugt  Steuersignale  für  den  Rasterstrahl, die  angeben  in  welcher  Rasterzeile  er  gerade
 arbeiten  soll, und  mit  welcher  Intensität  die  einzelnen  Bildpunkte  auf  dem Bildschirm  leuchten  sollen. Helle  Bildpunkte  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  einsetzen  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üssen, 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  geschieht  ü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öglichkeit, 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  insgesamt 313 Rasterzeilen  gibt  reicht  nämlich  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  anderen  Bits  dieses  Registers  ebenfalls  einige  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  Interruptauslö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  folgenden  drei  Befehlen  das 7 . Bit  in  Register $ D011 setzen, und  die  Nummer  der
 Rasterzeile  minus  dem  Wert 256 in  Register $ D012 schreiben:
LDA $D011 ;Reg.17 lesen ORA $80 ;7. Bit setzen STA $D011 ;Reg.17 zurückschreiben.
 Um  z. B. Rasterzeile 300 als  Interruptauslö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  Rasterzeile  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 $ D01 A) 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  definieren. 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  einen  Interrupt  auslösen  können. Sie  können  auf  diese  Weise  also  auch  sehr  komfortabel  eine  Kollision  über  den  Interrupt  abfragen. Bit 3 wird  nur  sehr  selten  benutzt  und  bringt  eigentlich  nur
 etwas  in  Zusammenhang  mit  einem  Lightpen. 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  entsprechende  Lightpen-Programm  nur  noch
 auswerten, an  welcher  Rasterposition  der
 Strahl  sich  gerade  befindet, um  herauszufinden, an  welcher  Stelle  des  Bildschirms  sich  der  Lightpen  befindet. Dies
 soll  uns  hier  jedoch  nicht  interessie- ren.
 Wir  müssen  also  lediglich  Bit 0 von $ D0-1 A  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  festgelegt  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  Prinzip  ein  alter  Hut, denn  der  VIC  ist  mit
 der  IRQ-Leitung  des  Prozessors  verbunden, 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üssen  wir  jedoch  beachten, daß  der  Betriebssystem- 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  Unterschied  machen, so  würde  uns  die  CIA
 ungehemmt ' dazwischenfunken' und  der
 Rasterinterrupt  würde  außer  Kontrolle
 geraten. Bevor  ich  mich  nun  aber  in  theroretischen  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  Aussta $ d012 ; löser  festlegen  lda #$07 ; Rahmenund  Hintergrundsta $ 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  ausfü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  Rasterzeilen $00,$70 und $ C0 einen  Interrupt
 auslösen, woraufhin  wir  die  Rahmenund
 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  beschriebene  Art  und  Weise  die  Rasterzeile 0 als  Interruptauslösende  Zeile
 fest. Hieraufhin  wird  der  Wert $01 in
 Register $ D01 A  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ückschreiben  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  Interruptanforderung  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  entspricht  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  Lesevorgang  wurden  die  dem  Wert  entsprechenden  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  Unterroutine " 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  Rasterzeile 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  Initialisierungsroutine  her  ja  noch  auf 0 ist. Danach
 werden  Rahmenund  Hintergrundfarbe
 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ückgeholt  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  Bildschirmfarben  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)