Interrupt-Kurs             
     "Die Hardware ausgetrickst..."     
                (Teil 5)                
 Nachdem  wir  im  letzten  Monat  ja  schon
 kräftig  mit  schillernden  Farbund  Sinuswellenrasterroutinen  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, Bezeichnung  steht  für  einen  Rastereffekt, der
 vom  Prinzip  und  der  Programmierung  her
 extrem  simpel  ist, jedoch  ungeahnte Möglichkeiten  in  sich  birgt. Um  zu  wissen, welcher  Effekt  damit  gemeint  ist, brauchen  Sie  sich  lediglich  einmal  anzuschauen, was  passiert, wenn  Sie  im  MD-Hauptmenu  einen  neuen  Text  laden, oder
 einen  gelesenen  Text  wieder  verlassen:
 der  Textbildschirm  scheint  hier  von  unten  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  hinund  herkopiert
 und  rumgescrollt  werden  muß, sondern  um
 eine  einfache, ca.150 Byte  große, Rasterroutine! Der  Trick  des  Ganzen  liegt
 wie  so  oft  bei  der  Hardware  des 64 ers, 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  unteren  drei  Bits  von  Register 17 des  VICs($ D011), ein  vertikaler  Verschiebeoffset
 für  die  Bildschirmdarstellung  eingetragen  werden. In  der  Regel  benutzt  man
 diese  Bits  um  ein  vertikales  Softscrolling  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 . Textzeile  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  Kursteil  noch  wissen, geschieht  dies  ab
 Rasterzeile 41 und  jeweils  in  jeder  achten, folgenden  Zeile. Wird  nun  ein  vertkaler  Verschiebeoffset  angegeben, so
 verzögert  der  VIC  diesen  Zeitpunkt  um die  angegebene  Anzahl  Rasterzeilen ( maximal 7) . Steht  in  der  Vertikalverschiebung  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  darin, daß  Sie  in  jeder  Rasterzeile, diesen  Charakterzeilenanfang  vor  dem  Rasterstrahl " herschiebt", so  daß  dieser
 eigentlich  nie  die  gesuchte  Anfangszeile
 erreichen  kann - zumindest  nicht  solange, wie  unsere  FLD-Routine  ihm  vortäuscht, noch  nicht  den  Anfang  dieser  Zeile
 erreicht  zu  haben! Wie  einfach  das  alles
 geht, soll  Ihnen  folgendes  Programmbeispiel  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  Initialisierungsroutine  können  wir  uns  sparen, da  wir
 ihren  Aufbau  ja  schon  von  anderen  Programmbeispielen  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  geschrieben. Die  Bedeutung  dieser  Adresse
 kennen  wir  noch  von  unserer  Borderroutine  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 ausgelöst  werden  soll. Gleichzeitig  beinhaltet  diese  Routine  eine  Joystickabfrage, 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  werden. Abschließend  biegt  diese  IRQ-Routine  den  IRQ-Vektor  auf  die  eigentliche  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  Befehle  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  hinzuaddiert. 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  ausmaskieren, 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ß. Anschließend  wird  der  neue  Wert  für $ D011 wieder  zurückgeschrieben. Da  diese  Verschiebungsä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  normale  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  werden, da  wir  durch  das " voruns- Herschieben" 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  Interruptauslöser  festgelegt. Zusätzlich  verbiegen  wir  den  IRQ-Vektor  auf  diese  Routine
 zurück. Dabei  wird  in  unserem  Beispiel
 lediglich  das  Low-Byte  geändert, da  beide  Routinen  ja  an  einer  Adresse  mit
$10 xx  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  Prozessorregister  vom  Stack  zurückholt  und  den  Interrupt  beendet.
 Die  Art  und  Weise, wie  wir  hier  die  Vertikalverschiebung  vor  dem  Rasterstrahl
 herschieben  mag  etwas  umständlich  anmuten. 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  Rasterposition  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  verodert  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  inoffizieller, Assemblerbefehl  ist, und  deshalb  von  den  meisten  Assemblern  und  Disassemblern  nicht  erkannt  wird. Zudem
 können  Laufzeitunterschiede  oder  gar
 Fehlfunktionen  bei  verschiedenen  Produktionsversionen  des 6510- Prozessors  vorkommen, 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  entsprechenden  Wert  für  jede  einzelne  Rasterzeile  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  Tabelle  beinhaltet  gleichzeitig  auch  noch  den
 Vorteil, daß  wir  viel  flexibler  die  FLD-Effekte  einsetzen  können. So  ist  es  damit  sehr  einfach  möglich, mehrere  Charakterzeilen  zu  verschieben, wie  das  im
" FLD-DEMO3" der  Fall  ist. Dieses  Beispielprogramm  beginnt  übrigens  ausnahmsweise  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  Disassembler  oder  Speichermonitor  anschauen um  ihre  Funktionsweise  zu  verstehen. Mit
 FLD  erzielbare  Effekte  sind  sehr  vielseitig  und  sie  sollten  schon  ein  wenig
 damit  herumexperimentieren. Weiterhin
 gibt  es  einige  Rastereffekte  die  durch
 eine  FLD-Routine  stark  vereinfacht  programmiert  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  Rastereffekten  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ächsten  Kursteil  ersichtlich, wo  wir  Ihnen
 eine  Sideborder-Routine  vorstellen  werden. Wird  diese  Routine  auch  nur  einen
 Taktzyklus  zu  früh  oder  zu  spät  ausgefü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  mitbekommen  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  Textmodus  und  Rastereffekten  im  sichtbaren
 Bildschirmbereich  beachten  müssen, daß
 jede  achte  Rasterzeile, jeweils  am  Beginn  einer  Charakterzeile, der  VIC  den
 Prozessor  für 42 Taktzyklen  anhält, damit  er  die, in  den  folgenden  acht  Rasterzeilen  darzustellenden, Zeichen  generieren  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  herauszufinden, ob  eine  Routine  schnell, bzw. langsam  genug, abgearbeitet  wird. Das  kann
 unter  Umständen  eine  sehr  aufwendige
 Sache  werden, da  hierbei  ewig  lang  Befehlstabellen  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  dieses  Kurses  in  einer  Tabelle  alle  Prozessor- Befehle  in  allen  möglichen  Adressierungsarten  aufgelistet. Um  also  von  Hand
 die  Laufzeit  einer  Routine  zu  berechnen
 können  Sie  dort  nachschlagen.
 Noch  einfach  geht  das  Abwägen  der  Laufzeit  jedoch  mit  einem  Programm. Wir  können  uns  hier  die  Möglichkeit  zunutze
 machen, daß  mit  den  Timern  der  CIAs  einzelne  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 " Cyclecount" 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  Lowund
 High-Byte  der  zu  testenden  Routine  in
 die  Adressen 828/829($033 c/$033 d) . 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  Bildschirm  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  Cyclecount  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  verbraucht  wurden, und  nicht  mitgezählt
 werden  dürfen.
( Anm. d. Red. : Bitte  wählen  Sie  jetzt  den
2 . Teil  des  Kurses  aus  dem  Textmenu.)