Magic Disk 64

home to index to html: MD9403-KURSE-IRQ-KURS_5.1.html
             Interrupt-Kurs             
     "Die Hardware ausgetrickst..."     
                (Teil 5)                
----------------------------------------
Nachdem  wir  im  letzten Monat ja schon
kräftig mit schillernden Farb-  und  Si-
nuswellenrasterroutinen  um uns geworfen
haben, möchten wir uns  auch  in  dieser
Ausgabe  der  MD einem sehr trickreichen
Beispiel eines Raster-IRQs zuwenden: der
FLD-Routine.                            
1) FLD - EIN ZAUBERWORT FÖR RASTERFREAKS
Die  Abkürzung "FLD" steht für "Flexible
Line  Distance",  was  übersetzt  soviel
bedeutet  wie  "beliebig  verschiebbarer
Zeilenunterschied".   Diese,    zunächst
vielleicht  etwas  verwirrende, Bezeich-
nung steht für einen  Rastereffekt,  der
vom  Prinzip  und der Programmierung her
extrem  simpel  ist,  jedoch   ungeahnte
Mög-lichkeiten in sich birgt. Um zu wis-
sen, welcher Effekt damit  gemeint  ist,
brauchen Sie sich lediglich einmal anzu-
schauen, was passiert, wenn Sie  im  MD-
Hauptmenu  einen  neuen Text laden, oder
einen gelesenen Text  wieder  verlassen:
der  Textbildschirm scheint hier von un-
ten her hochgezogen, bzw. nach unten hin
weggedrückt zu werden. Und genau das tut
nun eine FLD-Routine. Hierbei sei darauf
hingewiesen, daß es sich dabei nicht  um
irgendeine Programmierakrobatik handelt,
bei  der  aufwendig  hin- und herkopiert
und rumgescrollt werden muß, sondern  um
eine  einfache,  ca. 150 Byte große, Ra-
sterroutine! Der Trick des Ganzen  liegt
wie  so  oft bei der Hardware des 64ers,
die wieder einmal beispielhaft  von  uns
"veräppelt" wird. Denn eigentlich sollte
sie  nicht  dazu in der Lage sein, einen
solchen Effekt zu erzeugen!             
Wie funktioniert nun diese Routine?  Wie
Sie vielleicht wissen, kann in den unte-
ren drei Bits von Register 17  des  VICs
($D011), ein vertikaler Verschiebeoffset
für  die Bildschirmdarstellung eingetra-
gen werden. In  der  Regel  benutzt  man
diese  Bits um ein vertikales Softscrol-
ling zu realisieren. Je nach dem welcher
Wert dort eingetragen wird  (von  0  bis
7),  kann die Darstellung des sichtbaren
Bildschirms um 0 bis 7 Rasterzeilen nach
unten verschoben werden. Lässt man diese
Werte nacheinander  durch  das  Register
laufen,  und  kopiert  man daraufhin den
Inhalt des Bildschirms von der 2.  Text-
zeile  in  die 1. Textzeile, so entsteht
ein Softscrolling nach unten. Der  Wert,
der  dabei  in den unteren drei Bits von
$D011 steht gibt dem VIC an, ab  welchem
vertikalen   Bildschirmoffset  er  damit
anfangen  soll,  die  nächste  Textzeile
aufzubauen. Wie wir aus dem letzten Kur-
steil noch  wissen,  geschieht  dies  ab
Rasterzeile 41 und jeweils in jeder ach-
ten, folgenden Zeile. Wird nun ein vert-
kaler   Verschiebeoffset  angegeben,  so
verzögert der VIC  diesen  Zeitpunkt  um
die  angegebene Anzahl Rasterzeilen (ma-
ximal 7). Steht in der Vertikalverschie-
bung  z.B.  der  Wert  1, so muß der VIC
also noch eine Rasterzeile  warten,  bis
er die nächste Charakterzeile aufzubauen
hat.                                    
Der  Trick der FLD-Routine liegt nun da-
rin, daß Sie in jeder Rasterzeile,  die-
sen  Charakterzeilenanfang  vor  dem Ra-
sterstrahl "herschiebt", so  daß  dieser
eigentlich nie die gesuchte Anfangszeile
erreichen  kann - zumindest nicht solan-
ge, wie unsere FLD-Routine  ihm  vortäu-
scht, noch nicht den Anfang dieser Zeile
erreicht zu haben! Wie einfach das alles
geht,  soll Ihnen folgendes Programmbei-
spiel verdeutlichen. Sie finden  es  auf
dieser  MD  unter  dem Namen "FLD-DEMO1"
und müssen es absolut (mit ",8,1") laden
und wie  alle  unsere  Programmbeispiele
mit "SYS4096" starten:                  
init:  sei         ;IRQs sperren        
       lda #$7f    ;CIA-Timer abschalten
       sta $dc0d   ; (SYS-IRQ)          
       lda $dc0d   ;Und CIA-ICR löschen 
       lda #$f8    ;Zeile $f8 ist IRQ-  
       sta $d012   ; Auslöser           
       lda $d011   ;7.Bit Rasterzeile   
       and #$7f    ; löschen            
       sta $d011   ; u. zurückschr.     
       lda #$01    ;VIC löst Raster-IRQs
       sta $d01a   ; aus                
       ldx #<(IRQ2);IRQ-Vektor auf      
       ldy #>(IRQ2); eigene Routine     
       stx $0314   ; verbiegen          
       sty $0315                        
       lda #$00    ;Zählregister        
       sta $02     ; löschen            
       lda #$ff    ;Leerbereich auf     
       sta $3fff   ; schwarz setzen     
       cli         ;IRQs freigeben      
verz:  rts         ;Und Ende            
---                                     
irq1:  lda #$10    ;Vert. Verschiebung  
       sta $d011   ;gleich 0            
       lda $02     ;Zähler laden        
       beq lab2    ;Wenn 0, überspringen
       ldx #$00    ;Zählreg. löschen    
lab1:  clc         ;Carry f.Add.löschen 
(!)    lda $d011   ;Verschiebung holen  
(!)    adc #$01    ; +1                 
(!)    and #$07    ; unt. Bits ausmask. 
(!)    ora #$10    ; Bit 4 setzen       
(!)    sta $d011   ; u. zurückschr.     
       dec $d019   ;VIC-IRQ freigeben   
       jsr verz    ;Verzögern...        
       jsr verz                         
       lda $d012   ;Strahlpos < Bild-   
       cmp #$f6    ; schrimende?        
       beq lab2    ;Ja, also überspr.   
       inx         ;Zähler+1            
       cpx $0002   ;Zähler=Endwert?     
       bne lab1    ;Nein, also weiter   
lab2:  lda #$f8    ;Rasterz. $f8 ist    
       sta $d012   ; nächster IRQ-Ausl. 
       dec $d019   ;VIC-IRQs freigeb.   
       lda #$78    ;IRQ-Vektor auf IRQ2-
       sta $0314   ; Routine verbiegen  
       ldx #$0e    ;Bildschirmfarben    
       ldy #$06    ; zurücksetzen       
       stx $d020                        
       sty $d021                        
       jmp $febc   ;IRQ beenden         
---                                     
irq2:  lda #$10    ;Vertikal-Versch.    
       sta $d011   ; init.              
       lda #$71    ;Rasterz. $71 ist    
       sta $d012   ; IRQ-Auslöser       
       dec $d019   ;VIC-IRQs freigeben  
       lda #$30    ;IRQ-Vektor auf IRQ1-
       sta $0314   ; routine verbiegen  
       lda $dc00   ;Portreg lesen       
       lsr         ;Akku in Carry rot.  
       bcs lab3    ;C=1? Wenn ja, weiter
       dec $02     ;Sonst Joystick hoch 
lab3:  lsr         ;Akku in Carry rot.  
       bcs lab4    ;C=1? Ja, also weiter
       inc $02     ;Sonst Joyst. runter 
lab4:  jmp $ea31   ;SYS-IRQ und Ende    
Die Beschreibung  der  Initialisierungs-
routine  können  wir  uns sparen, da wir
ihren Aufbau ja schon von  anderen  Pro-
grammbeispielen  her kennen. Wichtig ist
nur, daß wir hier  Rasterzeile  $F8  als
IRQ-Auslöser  festlegen,  und die zweite
IRQ-Routine ("IRQ2") in  den  IRQ-Vektor
eintragen. Ansonsten wird hier auch noch
der  FLD-Zeilenzähler  in  Speicherzelle
$02 gelöscht,  sowie  der  Wert  $FF  in
letzte   Adresse  des  VIC-Bereichs  ge-
schrieben. Die Bedeutung dieser  Adresse
kennen wir noch von unserer Borderrouti-
ne aus dem letzten Kursteil: ihr  Inhalt
wird  in  schwarzer Farbe immer an allen
Stellen auf dem Bildschirm angezeigt, an
denen wir den Rasterstrahl  mit  unseren
Interrupts austricksen, was ja auch hier
der  Fall  ist.  Möchten  wir an solchen
Stellen die Hintergrundfarbe  sehen,  so
müssen wir den Wert $00 hineinschreiben.
Die Routine "IRQ2" wird nun immer einmal
pro  Bildschirmaufbau  aufgerufen.   Sie
bereitet   die  eigentliche  FLD-Routine
vor, die ab  der  Rasterzeile  $71  aus-
gelöst  werden  soll. Gleichzeitig bein-
haltet diese Routine eine Joystickabfra-
ge,  mit  der  wir  das  Zählregister in
Adresse $02  ändern  können.  Auf  diese
Weise  kann  mit  dem  Joystick die FLD-
Lücke  ab  Rasterzeile  $71,   je   nach
Wunsch, vergrößert oder verkleinert wer-
den.  Abschließend  biegt   diese   IRQ-
Routine den IRQ-Vektor auf die eigentli-
che FLD-IRQ-Routine  ("IRQ1")  und  ruft
den System-IRQ auf, den wir in der Init-
Routine ja abgeschaltet hatten  und  nun
"von Hand" ausführen müssen.            
Hiernach ist nun "IRQ1" am Zug. Kern der
Routine  ist  die  Schleife zwischen den
beiden  Labels  "LAB1"  und  "LAB2".  Am
wichtigsten sind hierbei die fünf Befeh-
le die ich Ihnen  mit  Ausrufungszeichen
markiert  habe.  Hier  wird zunächst der
Inhalt des Registers $D011  gelesen,  in
dem  der  vertikale  Verschiebeoffset zu
finden ist, und 1 auf diesen  Wert  hin-
zuaddiert. Da dabei auch ein Öberlauf in
das  3.  Bit  des  Registers stattfinden
kann, das ja nicht mehr  zur  vertikalen
Verschiebung  herangezogen  wird, müssen
wir mit dem  folgenden  AND-Befehl  alle
Bits außer den unteren dreien ausmaskie-
ren, und mittels ORA, das 3. Bit  wieder
setzen, da es steuert, ob der Bildschirm
ein,  oder  ausgeschaltet sein soll, und
deshalb  immer  gesetzt  sein  muß.  An-
schließend  wird der neue Wert für $D011
wieder zurückgeschrieben. Da diese  Ver-
schiebungsänderung  nun  auch  in  jeder
folgenden Zeile auftreten soll,  solange
bis  der  Zeilenzähler  abgelaufen  ist,
müssen mit dem Rest der Routine  die  63
Taktzyklen,  die  der  Rasterstrahl  zum
Aufbau einer Rasterzeile braucht, verzö-
gert werden. Eine Unterscheidung in nor-
male Rasterzeilen  und  Charakterzeilen,
in denen der Prozessor vom VIC ja für 42
Taktzyklen   angehalten  wird,  und  die
Schleife deshalb weniger verzögern  muß,
braucht  diesmal nicht durchgeführt wer-
den,  da  wir  durch  das  "vor-uns-Her-
schieben"  der  nächsten  Charakterzeile
deren Aufbau ja solange verhindern,  bis
die  Schleife  der  FLD-Routine  beendet
ist. Dies ist dann der Fall, wenn entwe-
der  der  Zähler im X-Register bis auf 0
gezählt wurde, oder aber die Rasterzeile
$F6 erreicht wurde, ab  der  der  untere
Bildschirmrand beginnt.                 
Ab  dem  Label  "LAB2",  wird nun wieder
Rasterzeile $F8 für "IRQ2" als Interrup-
tauslöser festgelegt. Zusätzlich verbie-
gen wir den IRQ-Vektor auf diese Routine
zurück. Dabei wird in  unserem  Beispiel
lediglich das Low-Byte geändert, da bei-
de Routinen  ja  an  einer  Adresse  mit
$10xx anfangen, und somit die High-Bytes
der  beiden  Routinenadressen  immer den
Wert $10 haben. Zum Schluß  wird  wieder
auf  den  Teil der Betriebssystemroutine
($FEBC) gesprungen, der die Prozessorre-
gister  vom Stack zurückholt und den In-
terrupt beendet.                        
Die Art und Weise, wie wir hier die Ver-
tikalverschiebung  vor  dem Rasterstrahl
herschieben mag etwas umständlich  anmu-
ten.  Tatsächlich gibt es hier auch noch
andere Möglichkeiten, die  in  den  Bei-
spielprogrammen  "FLD-DEMO2",  und "FLD-
DEMO3" benutzt wurden. Sauberer ist  die
Lösung  des Zeilenproblems, wenn man das
Register, das die  aktuelle  Rasterzeile
enthält  ($D012),  als Zähler verwendet.
Wir müssen hier lediglich die  Rasterpo-
sition  auslesen,  ihren Wert um 1 erhö-
hen, die unteren drei Bits  ausmaskieren
und das 4. Bit in diesem Register wieder
setzen. Selbiges wird durch die folgende
Befehlsfolge durchgeführt:              
clc                                     
lda $d012                               
adc #$01                                
and #$07                                
ora #$10                                
sta $d011                               
Noch schneller geht das,  wenn  man  den
illegalen  Opcode  "ORQ"  verwendet.  Er
addiert 1 auf den Akku hinzu  und  vero-
dert  gleichzeitig  das Ergebnis mit dem
Operandenwert. Die Befehlsfolge ist dann
nur noch vier Zeilen lang:              
lda $d012                               
and #$07                                
orq #$10                                
sta $d011                               
Selbst wenn diese  Methode  kürzer  ist,
als  die  zuvorgenannte,  ist es dennoch
nicht ratsam sie zu verwenden, da  "ORQ"
wie  gesagt  ein illegaler, also inoffi-
zieller, Assemblerbefehl ist,  und  des-
halb von den meisten Assemblern und Dis-
assemblern  nicht  erkannt  wird.  Zudem
können   Laufzeitunterschiede  oder  gar
Fehlfunktionen bei verschiedenen Produk-
tionsversionen  des 6510-Prozessors vor-
kommen, so daß ein  Programm  mit  einem
solchen illegalen Opcode nicht auf jedem
C64  lauffähig sein muß. Wer es wirklich
kurz will, der sollte über eine  Tabelle
die  benötigten  Zeilendaten  holen, wie
das im  Beispiel  "FLD-DEMO3"  der  Fall
ist. Hier wurde eine Tabelle bei Adresse
$1200 abgelegt, die den jeweils entspre-
chenden Wert für jede  einzelne  Raster-
zeile  enthält.  Die  eigentlichen  FLD-
Befehle verkürzen  sich  damit  auf  die
beiden folgenden Zeilen:                
lda $1200,x                             
sta $d011                               
Die Lösung des Problems über eine Tabel-
le beinhaltet gleichzeitig auch noch den
Vorteil, daß wir viel flexibler die FLD-
Effekte einsetzen können. So ist es  da-
mit  sehr  einfach möglich, mehrere Cha-
rakterzeilen zu verschieben, wie das  im
"FLD-DEMO3"  der  Fall  ist. Dieses Bei-
spielprogramm beginnt übrigens ausnahms-
weise  an  Adresse  $1100,  weswegen  es
nicht wie sonst mit  "SYS4096",  sondern
durch  ein  "SYS4352"  aufgerufen werden
muß.                                    
Alles in allem sollten Sie sich die drei
Beispiele ruhig einmal mit einem  Disas-
sembler  oder  Speichermonitor anschauen
um ihre Funktionsweise zu verstehen. Mit
FLD erzielbare Effekte sind  sehr  viel-
seitig  und  sie sollten schon ein wenig
damit  herumexperimentieren.   Weiterhin
gibt  es  einige Rastereffekte die durch
eine FLD-Routine stark vereinfacht  pro-
grammiert werden können, oder sogar ohne
sie  gar  nicht  möglich wären, weswegen
ein gründliches Verständnis der  Materie
sehr  von  Vorteil bei anderen Rasteref-
fekten sein kann.                       
2) TIMINGPROBLEME UND TAKZYKLENMESSER   
Wie  wir  wieder  einmal bewiesen haben,
ist die Rasterprogrammierung eine Sache,
bei der es auf  absolut  exaktes  Timing
ankommt. Noch haariger wird das im näch-
sten Kursteil ersichtlich, wo wir  Ihnen
eine  Sideborder-Routine vorstellen wer-
den. Wird diese Routine auch  nur  einen
Taktzyklus  zu  früh  oder  zu spät aus-
geführt, so funktioniert sie schon nicht
mehr. Deswahlb wollen wir uns  nun  erst
wieder  ein wenig in die Theorie stürzen
und Verfahrensweisen zur Ermittlung  der
Laufzeit eines Programms vorstellen.    
Wie Sie mittlerweile nun oft genug  mit-
bekommen haben, braucht der Rasterstrahl
zum  Aufbau  einer  Rasterzeile genau 63
Taktzyklen. Innerhalb dieser Zeit müssen
wir  den   Prozessor   immer   irgendwie
beschäftigen,  damit wir rechtzeitig zum
Beginn  der  nächsten  Rasterzeile  eine
weitere Änderung vornehmen können. Hinzu
kommt,  daß wir bei eigeschaltemem Text-
modus und Rastereffekten  im  sichtbaren
Bildschirmbereich  beachten  müssen, daß
jede achte Rasterzeile, jeweils  am  Be-
ginn  einer  Charakterzeile, der VIC den
Prozessor für 42 Taktzyklen anhält,  da-
mit  er  die,  in den folgenden acht Ra-
sterzeilen darzustellenden, Zeichen  ge-
nerieren  kann.  Somit  bleiben  für den
Prozessor für solch eine Rasterzeile nur
noch 21 Taktzyklen  Rechenzeit.  Um  nun
ein  exaktes Timing zu erreichen müssten
wir eigentlich die Laufzeiten eines  je-
den   einzelnen  Befehls  einer  Raster-
Routine zusammenaddieren um herauszufin-
den, ob eine Routine schnell, bzw. lang-
sam genug, abgearbeitet wird.  Das  kann
unter  Umständen  eine  sehr  aufwendige
Sache werden, da hierbei ewig  lang  Be-
fehlstabellen  mit Zyklenangaben gewälzt
werden müssten, und  bei  jeder  kleinen
Änderung neue Verzögerungsbefehle in die
Routine eingefügt, oder aus ihr entfernt
werden müssten.                         
Damit Sie die  Zyklenzahlen  selbst  zur
Hand  haben, habe ich Ihnen am Ende die-
ses Kurses in einer Tabelle alle Prozes-
sor-Befehle in allen möglichen Adressie-
rungsarten aufgelistet. Um also von Hand
die Laufzeit einer Routine zu  berechnen
können Sie dort nachschlagen.           
Noch einfach geht das Abwägen der  Lauf-
zeit jedoch mit einem Programm. Wir kön-
nen uns  hier  die  Möglichkeit  zunutze
machen, daß mit den Timern der CIAs ein-
zelne Zyklen gezählt werden können.  Ich
habe Ihnen hierzu ein Zyklenmessprogramm
geschrieben,  das  es  Ihnen ermöglicht,
eine  eigene  Routine  bezüglich   ihrer
Laufzeit  zu  testen.  Es  heißt "Cycle-
count" und ist ebenfalls auf  dieser  MD
zu finden. Das Programm ist in der Lage,
Routinen  mit  einer  Dauer  von maximal
65490 Taktzyklen zu stoppen.  Laden  Sie
es  hierzu  mit  LOAD"CYCLECOUNT",8,1 in
den Speicher und schreiben Sie Low-  und
High-Byte  der  zu  testenden Routine in
die Adressen 828/829 ($033c/$033d).  Die
zu   messende   Routine  muß  mit  einem
"BRK"-Befehl beendet werden.  Rufen  Sie
nun  das  Programm  mit einem "SYS49152"
auf. Cyclecount gibt Ihnen daraufhin den
ermittelten Zyklen-Wert  auf  dem  Bild-
schirm  aus.  Das Programm benutzt dabei
den Timer A von  CIA-B  zum  Messen  der
Zyklen. Es initialisert diesen Timer mit
dem  Wert  $FFFF,  startet  ihn und ruft
daraufhin die zu testende  Routine  auf.
Zuvor  wird  der  BRK-Vektor bei Adresse
$0316/$0317 auf eine eigene Routine ver-
bogen.  Wird die zu testende Routine nun
mit einem BRK-Befehl  beendet,  so  wird
sofort zur Auswertungsroutine von Cycle-
count verzweigt, die  den  Timer  wieder
anhält  und  den  in  den Timerregistern
enhaltenen Wert von  $FFFF  subtrahiert.
Zudem müssen 45 Zyklen abgezogen werden,
die  hauptsächlich  zur  Ausführung  des
JMP-Befehls auf die zu testende  Routine
und durch den beendenden BRK-Befehl ver-
braucht  wurden,  und  nicht  mitgezählt
werden dürfen.                          
 (Anm.d.Red.: Bitte wählen Sie jetzt den
  2.Teil des Kurses aus dem Textmenu.)  
Valid HTML 4.0 Transitional Valid CSS!