Magic Disk 64

home to index to html: MD9404-KURSE-IRQ-KURS_6.1.html
             Interrupt-Kurs             
     "Die Hardware ausgetrickst..."     
                (Teil 6)                
----------------------------------------
Anhand  der FLD-Routine des letzen Kurs-
teils hatten wir  gesehen,  wie  einfach
man  die  Hardware unseres kleinen Brot-
kastens austricksen kann, und  sie  dazu
bewegt  Dinge  zu  tun, zu denen sie ei-
gentlich nicht in der Lage ist. So  soll
es  auch  in  den  folgenden  Teilen des
IRQ-Kurses sein, jedoch müssen  wir  uns
zuvor um ein Problem kümmern, mit dessen
Lösung  wir noch trickreichere Rasteref-
fekte programmieren und den Copper-  und
Blittermagiern  des  Amigas das Fürchten
lehren werden...                        
1) AUF GUTES TIMING KOMMT ES AN...      
Wie auch schon am Ende des letzten Teils
angesprochen,  ist der Schlüssel zu tol-
len Rasterinterrupts ein besonders exak-
tes  Timing.  Wie  schon am Beispiel der
FLD-Routine unschwer erkennbar war,  be-
steht  das eigentliche "Austricksen" des
Video-Chips meist aus gerade einer hand-
voll Befehlen. Wichtig ist nur, daß die-
se Befehle zum richtigen Zeitpunkt  aus-
geführt  werden.  Wie  wichtig  das ist,
werden wir später am Beispiel einer  Ra-
sterroutine  sehen, die in der Lage ist,
den linken und rechten  Rand  des  Bild-
schirms  abzuschalten. Wird sie auch nur
einen Taktzyklus zu früh  oder  zu  spät
ausgeführt,  so bewirkt sie absoult gar-
nichts. Nur wenn zu einem ganz  bestimm-
ten  Zeitpunkt  der Wert eines Registers
verändert wird,  funktioniert  sie  auch
wie  sie  soll! Damit wir solche Effekte
also auch realisieren können, werden wir
uns nun zwei Problemen widmen, die immer
noch Ungenauigkeitsfaktoren  in  unseren
Routinen  darstellen und eliminiert wer-
den müssen:                             
2) SYSTEMVEKTOREN SIND SCHNELLER        
Der  erste  Ungenauigkeitsfaktor ist das
Betriebssystem des C64. Wie Sie  ja  aus
den  ersten Kursteilen wissen, holt sich
der 6510-Prozessor bei einer  Interrupt-
anfrage  zunächst  einmal  eine der drei
Interrupt-Sprungadressen am Ende  seines
Adressierungsbereichs  (von $FFFA-$FFFF)
in den Programmzähler. Hier  stehen  die
jeweiligen Adressen der Betriebssystems-
routinen, die die entsprechende Art  von
Interrupt  (IRQ,  NMI oder Reset) bedie-
nen. Gerade beim IRQ ist  diese  Routine
etwas aufwendiger aufgebaut, da derselbe
Vektor   auch  für  softwaremäßige  BRK-
Interrupts benutzt wird, und die Routine
deshalb eine Unterscheidung treffen muß.
Dadurch stiehlt sie uns quasi Prozessor-
zeit,  wenn  wir davon ausgehen, daß der
BRK-Interrupt in der Regel nicht verwen-
det  wird,  da er ja einfacher durch ein
JMP programmiert werden kann. Erst  nach
der Unterscheidung verzweigt die Routine
dann über den Vektor $0314/$0315 auf die
eigentliche   IRQ-Routine   (bzw.   über
$0316/$0317 zur BRK-Routine).  Und  erst
an  dieser  Stelle  "klinken" wir unsere
eigenen Raster-IRQs  in  das  Interrupt-
system ein. Um nun die Verzögerung durch
das  Betriebssystem zu eliminieren, müs-
sen wir es umgehen. Es sollte eigentlich
also reichen, wenn wir die  Startadresse
unserer  Interruptroutine    im   Vektor
$FFFE/$FFFF eintragen, da  er  der  IRQ-
Vektor  ist. Hierbei stellt sich uns je-
doch ein weiteres Problem  in  den  Weg:
diese   Adressen   gehören  ja  zum  Be-
triebssystem-ROM und können nicht verän-
dert  werden,  da sie für alle Zeiten in
dem  ROM-Chip  eingebrannt  sind.   Aber
nicht  verzagen, denn "unter" diesem ROM
befindet sich auch noch echtes RAM,  und
das können wir verändern. Damit der Pro-
zessor sich dann den IRQ-Vektor auch von
dort holt, müssen wir das darüberliegen-
de  ROM sozusagen "ausblenden", was über
das  Prozessoradressregister  geschieht,
das in Speicherzelle 1 zu finden ist. Im
Normalfall steht hier der Wert 55 ($37),
der das Basic- und Betriebssystem-ROM in
den Adressbereichen $A000-$BFFF (Basic),
sowie  $E000-$FFFF  (System) einblendet.
Führen  wir  Schreibzugriffe  auf  diese
Bereiche  aus,  so  landen diese, selbst
bei eingeschaltetem ROM  im  darunterle-
genden  RAM.  Das Problem dabei ist nur,
daß wir  dann  die  geschriebenen  Werte
nicht  auslesen können, da wir immer nur
den Inhalt der ROM-Adresse lesen können.
Um das zu ändern, muß der Wert 53  ($35)
in das Prozessoradressregister geschrie-
ben werden. Dadurch werden  nämlich  die
beiden ROM-Bausteine deaktiviert und das
darunterliegende   RAM  kommt  zum  Vor-
schein, worauf der Prozessor  dann  auch
Zugriff hat.                            
Ändern  wir  nun   den  IRQ-Vektor   bei
$FFFE/$FFFF, und tritt dann ein IRQ auf,
so  wird  direkt  auf unsere IRQ-Routine
verzweigt, ohne daß der Umweg  über  das
Betriebssystem  gegangen  wird. Beachten
Sie hierbei jedoch, daß  durch  das  Ab-
schalten  des ROMs weder Basic- noch Be-
triebssystemsroutinen verfügbar sind, da
wir sie ja weggeschaltet haben.  Benutzt
Ihr eigenes Programm solche Routinen, so
müssen  Sie das ROM zuvor ins RAM kopie-
ren. Dies tun Sie, indem Sie einfach bei
eingeschaltetem ROM eine Adresse  ausle-
sen  und  gleich  wieder  in sie zurück-
schreiben. Beim Lesen erhalten Sie  dann
den Wert des ROMs, beim Schreiben schik-
ken Sie ihn ins RAM darunter. Jetzt kön-
nen Sie getrost das ROM abschalten, ohne
daß  Ihr  Rechner abstürzt. Als Beispiel
zum Kopieren der beiden ROMs ins  darun-
terliegende RAM können Sie sich das Pro-
gramm "COPYSYS" auf dieser MD anschauen,
das  wie  all  unsere  Beispielprogramme
absolut (mit ",8,1") geladen werden muß,
und  mit  SYS4096  (JMP $1000) gestartet
wird.                                   
In unseren Beispielen werden wir  aller-
dings  keinerlei Betriebssystemsroutinen
verwenden, weswegen  wir  uns  hier  das
Kopieren  einsparen.  Wichtig ist dabei,
daß wir die Interruptquellen  von  CIA-A
sperren,  damit  sie  uns mit ihren IRQs
nicht zwischen die Raster-IRQs "funkt". 
3) DAS "GLÄTTEN" VON INTERRUPTS         
Wenn Sie schon ein wenig mit Raster-IRQs
"herumgespielt"  haben,  so  wird  Ihnen
vielleicht  schon  einmal folgendes Pro-
blem aufgefallen sein: Möchten Sie einen
Interrupt programmieren, der  z.B.  ein-
fach nur die Hintergrundfarbe ändert, so
passiert  es manchmal, daß gerade an der
Stelle, an der die Farbe geändert  wird,
ein unruhiges Flackern zu sehen ist. Man
hat  den  Eindruck,  als würde die Farbe
mal ein paar Pixel  früher  oder  später
geändert  werden,  so  daß an der selben
Stelle des Bildschirms manchmal die  al-
te,  manchmal  die  neue  Farbe zu sehen
ist. Irgendwie  scheint  es  also  nicht
möglich zu sein, den Interrupt immer zur
selben Zeit auftreten zu lassen - obwohl
die  Interruptroutine  immer  einen kon-
stanten Wert an  Taktzyklen  verbraucht,
und  deshalb der Flackereffekt gar nicht
auftreten dürfte!                       
Tatsächlich  liegt  die  Ursache   allen
Öbels  nicht  beim Interrupt, sondern am
Hauptprogramm: Tritt nämlich eine Inter-
ruptanforderung am Prozessor auf, so muß
dieser zunächst einmal den aktuell bear-
beiteten Befehl zu Ende führen, bevor er
den  Interruptvektor  anspringen   kann.
Angenommen,  er  wäre  gerade  dabei den
Befehl  "LDA #$00"  auszuführen.  Dieser
Befehl  benötigt 2 Taktzyklen. Einen zum
Lesen und Dekodieren  des  Befehlsbytes,
und  einen  zum  Lesen des Operanden und
Laden des Akkus. Hat der  Prozessor  nun
gerade das Befehlsbyte gelesen und tritt
in  genau  diesem  Moment  der Interrupt
auf, so muß er zunächst noch den Operan-
den  lesen,  um anschließend in die IRQ-
Routine verzweigen  zu  können.  Selbige
wird  dadurch aber erst einen Taktzyklus
später, als eigentlich erforderlich  ge-
wesen wäre, ausgeführt. Noch größer wird
die  Verzögerung,  wenn  gerade z.B. ein
"STA $D021" (4 Taktzyklen!) oder gar ein
"ROR $1000,X" (7 Taktzyklen!) ausgeführt
wurde.                                  
Das Programm  "FLACKER"  auf  dieser  MD
veranschaulicht dieses Problem. Die Rou-
tine ist eine Kombination aus  FLD-  und
Borderroutine.  Mit dem FLD-Teil drücken
wir einen Teil des Bildschirms nach  un-
ten  und  stellen  im Zwischenraum einen
Rasterbalken dar. Der Border-Teil schal-
tet einfach den oberen und unteren Bild-
schirmrand weg und dient mehr  als  Bei-
spiel zur Kombination der beiden Raster-
effekte. Der Rasterbalken ist  nun  auch
in  der X-Richtung verschiedenfarbig, so
daß eine Zeile in der einen Hälfte  eine
Farbe  und  in  der  anderen Hälfte eine
zweite Farbe  enthält.  Dieser  "Raster-
split"  ist  übrigens nur durch die FLD-
Routine möglich, da diese ja verhindert,
daß eine  Charakterzeile  gelesen  wird,
die  den  Prozessor   für  42 Taktzyklen
(zwei Drittel der gesamten  Zeile  also)
anhält. Ohne FLD könnte zum Beginn jeder
Charakterzeile   die   Farbänderung  gar
nicht  rechtzeitig  (nämlich  nach   der
Hälfte)  stattfinden,  da  der Prozessor
zum erforderlichen  Zeitpunkt  ja  immer
noch  durch den VIC angehalten wäre. Au-
ßerdem hat die FLD-Routine den  Vorteil,
daß wir keinen Unterschied zwischen Cha-
rakter- und normalen Rasterzeilen machen
müssen, weshalb wir uns  die  verschach-
telten Schleifen sparen. Sie starten das
Programm  wie  immer  mit  "SYS4096" und
verlassen es durch einen Druck  auf  den
Feuerknopf.  Mit Joystickbewegungen nach
oben und unten können Sie  übrigens  die
Größe der FLD-Öffnung variieren.        
Kommen   wir   nun  zu  unserem  Problem
zurück: sieht man sich das  Beispielpro-
gramm  einmal  an, so wird man ein erhe-
bliches  Flackern  bemerken.  Das  liegt
daran,  daß  das Programm zur Demonstra-
tion gerade in den zeitkritischen Momen-
ten,   besonders  zeitintensive  Befehle
ausführt, weswegen der IRQ mit bis zu  7
Taktzyklen  Verzögerung  auftreten kann.
Da nun aber für die  weiteren  Beispiele
dieses  Kurses eine höhere Präzision er-
forderlich ist, müssen wir uns eine  Me-
thode  angewöhnen, mit der wir einen In-
terrupt "glätten" können.  Selbiges  tut
nämlich  das dritte Beispiel dieses Kur-
steils, mit dem  Namen  "LOESUNG".  Hier
das dokumentierte Listing:              
;*** Initialiserung ($1000)             
Init: sei       ;IRQ sperren            
      lda #$7f  ;Timer IRQ              
      sta $dc0d ;abschalten             
      bit $dc0d ;ICR löschen            
      lda #$f8  ;Rasterzeile $f8 als    
      sta $d012 ; IRQ-Auslöser festlegen
      lda $d011 ;Bit 7 löschen          
      and #$7f                          
      sta $d011                         
      lda #$01  ;Raster als IRQ         
      sta $d01a ; wählen                
      ldx #<Bord;Hard-IRQ-Vektoren      
      ldy #>Bord; auf eigene            
      stx $fffe ; Routine               
      sty $ffff ; umstellen             
      lda #$33  ;Zeilenabstand für FLD  
      sta $02   ; initialisieren        
      lda #$00  ;VIC-Byte löschen       
      sta $3fff                         
      lda #$35  ;ROM ausblenden         
      sta $01                           
      cli       ;Interrupts erlauben    
Hier  haben wir den Initialisierungsteil
vor uns. Wie üblich sperren wir zunächst
die IRQs  mittels  SEI-Befehl,  schalten
alle  von CIA-A möglichen Interruptquel-
len ab, und löschen mit Hilfe  des  BIT-
Befehsl eine evtl. noch gemeldete Inter-
ruptanfrage. Anschließend  wird  Raster-
zeie  $F8  als Interruptauslöser festge-
legt (mit Löschen des Hi-Bits in  $D011)
und  dem  VIC mitgeteilt, daß er Raster-
IRQs erzeugen soll. Nun  erst  wird  der
IRQ-Vektor  (wohlgemerkt  der bei $FFFE/
$FFFF und nicht der bei $0314/$0315) mit
der Adresse  der  Border-Routine  gefüt-
tert.  Zum  Schluß löschen wir dann noch
die letze VIC-Adresse, deren  Inhalt  ja
im  abgeschalteten  Border  und dem FLD-
Bereich angezeigt wird,  und  legen  den
FLD-Zähler  fest (Speicherzelle $02), so
daß er $33 (dez. 51) Zeilen öffnet.  Nun
erst  wird  das  ROM durch Schreiben von
$35 in die Speicherzelle 1 ausgeblendet,
damit der Prozessor bei einem  IRQ  auch
unseren   Vektor  bei  $FFFE/$FFFF  ans-
pringt, und die Interrupts werden wieder
erlaubt.  Die  Init-Routine  kehrt   nun
nicht   wieder   zur   normalen  Eingabe
zurück, da wir damit ja in eine  Routine
des  Basics  zurückspringen  würden, die
nach abschalten des ROMs nicht mehr vor-
handen  ist.  Stattdessen folgt nun eine
Hauptschleife, mit der wir  ständig  den
Joystickknopf abfragen. Hierbei erfüllen
die  ersten  sieben  Befehle  eigentlich
keinen Zweck. Es sind nur besonders zei-
tintenive  Befehle, die wir zur Öberprü-
fung, ob  unser  Glätten  auch  funktio-
niert,im  Programm  haben.  Sie sind ge-
folgt  von  einer  simplen  Abfrage  des
Feuerknopf-Bits von Joyport 2:          
;*** Hauptprogramm                      
wfire inc $03    ;Dummy-Befehle, die    
      inc $2000  ; absichtlich besonders
      ror $03    ; viel Rechenzeit      
      ror $2000  ; verbrauchen.         
      bit $03                           
      ldx #$00                          
      ror $2000,x                       
      lda $dc00  ;Joyport laden         
      and #$10   ;Firebutton-Bit isol.  
      bne wfire  ;Nicht gedr. -> weiter 
Wird  der  Feuerknopf  nun  gedrückt, so
müssen wir die ROMs wieder  einschalten,
dem  VIC  die IRQs verbieten und sie der
CIA wieder  erlauben,  um  das  Programm
verlassen  zu  können. Dies tut folgende
Endroutine:                             
      sei        ;IRQs sperren          
      lda #$37   ;ROMs einschalten      
      sta $01                           
      lda #$f0   ;VIC-IRQs sperren      
      sta $d01a                         
      dec $d019  ;ggf.VIC-IRQ-Anf.lösch.
      lda #$1b   ;Normaler Darstellungs-
      sta $d011  ; modus (wg. Border)   
      lda #$81   ;CIA-A darf Timer-IRQs 
      sta $dc0d  ; auslösen             
      bit $dc0d  ;ggf.CIA-IRQ-Anf.lösch.
      cli        ;IRQs freigeben        
      rts        ;und ENDE              
Kommen wir nun  zur  Borderroutine.  Sie
ist  die erste IRQ-Routine, die nach der
Initialisierung  (bei  Rasterzeile  $F8)
aufgerufen wird:                        
Bord  pha       ;Akku, X- u. Y-Reg.     
      txa       ; auf Stapel retten     
      pha                               
      tya                               
      pha                               
      lda #$10  ;24-Zeilen-Darst. an    
      sta $d011 ; (=Bordereffekt)       
      lda #$3d  ;nächsten IRQ bei 2.    
      sta $d012 ; Charakterzeile ausl.  
      dec $d019 ;VIC-ICR löschen        
      ldx #<FLD1;IRQ-Vektoren auf       
      ldy #>FLD1; erste                 
      stx $fffe ; FLD-Routine           
      sty $ffff ; verbiegen             
      jsr JoyCk ;Joystickabfrage        
      pla       ;Akku, X- u. Y-Reg.     
      tay       ; wieder vom Stapel     
      pla       ; holen                 
      tax                               
      pla                               
      rti       ;IRQ beenden.           
Die Routine macht eigentlich nichts wei-
ter, als  die  24-Zeilen-Darstellung  zu
aktivieren,  die  in  Rasterzeile $F8 ja
das Abschalten des Borders bewirkt,  die
Rasterzeile  des nächsten IRQs festzule-
gen ($3D=Startposition der 2. Charakter-
zeile-2),  den  Interrupt-Vektor auf die
Routine (FLD) für diese Zeile zu verbie-
gen, und den Joystick abzufragen (Unter-
routine, die hier nicht aufgeführt ist).
Beachten Sie, daß wir hier  die  Prozes-
sorregister  mit Hilfe der Transfer- und
Stapelbefehle von Hand retten  und  wie-
derherstellen  müssen. Gerade das Retten
war nämlich eine Aufgabe,  die  uns  das
Betriebssystem  freundlicherweise  schon
abgenommen hatte. Da es jetzt  ja  abge-
schaltet  ist,  müssen wir uns natürlich
selbst darum kümmern.                   
             (Anm. d. Red.:             
   Bitte wählen Sie jetzt den 2. Teil   
      des Kurses aus dem MD-Menu!)      



Valid HTML 4.0 Transitional Valid CSS!