Magic Disk 64

home to index to html: KURS-IRQ-KURS.html
             Interrupt-Kurs             
     "Die Hardware ausgetrickst..."     
                (Teil 1)                
----------------------------------------
Wer kennt sie nicht, die tollen Effekte,
die die großen Alchimisten der  Program-
mierkunst aus unserem guten alten "Brot-
kasten" herauszaubern: mehr als 8  Spri-
tes  gleichzeitig  auf  dem  Bildschirm,
Side-  und  Topborderroutinen,  die  die
Grenzen  des Bildschirmrahmens sprengen,
sich wellenförmig bewegende Logos,  Gra-
fikseiten,  die  sich in atemberaubender
Geschwindigkeit  zusammenfalten,   sowie
schillernd bunte 256-farbige Bilder. Man
könnte diese Liste noch bis ins Unendli-
che weiterführen und sie  würde  dennoch
die  vollständig  sein. In der Tat - was
manche Programmierer aus den Silizuimmo-
lekülen  der Hardware des C64 herauskit-
zeln hat schon so manch Einen zum  Stau-
nen  gebracht. Hätte Commodore zur Mark-
teinführung des C64 schon  gewusst,  was
für eine Wahnsinnsmaschine sie da gebaut
hatten, hätten sich die Entwickler wahr-
scheinlich selbst an den  Kopf  gefasst.
All  diese  Effekte sind nämlich absolut
unbeabsichtigt  unserer  "Dampfmaschine"
der  Computerwelt  eingepflanzt  worden,
sondern verdanken ihre  Existenz  einzig
und  allein  der  unermüdlichen Ausdauer
der Freaks und Coder, die in  nächtelan-
ger Herumtüftelei die Software dazu ent-
wickelten, die den  C64  weitaus  länger
leben ließ, als man je geglaubt hätte.  
"Rasterinterrupt"  heißt  das Zauberwort
das, einem "Sesam  öffne  Dich"  gleich,
dem  Programmierer das Tor zur wunderba-
ren Welt der Computer-Effekte  aufstößt.
Und  um genau diese Effekte soll sich in
diesem Kurs alles drehen.  Die  nächsten
Monate sollen Sie hier erfahren, wie man
sie  programmiert, und wir werden versu-
chen Ihnen das  Grundwissen  zu  vermit-
teln, neue Routinen selbst entwickeln zu
können.                                 
Im  ersten Teil dieses Kurses wollen wir
uns nun zunächst um die Grundlagen  küm-
mern, und den Fragen "Was ist ein Inter-
rupt?", "Wo kommt er her?" und "Was pas-
siert während eines Interrupts?" auf den
Grund  gehen. Leider müssen wir Sie auch
darauf hinweisen, daß zur Programmierung
von Interrupts  die  gute  Kenntnis  der
Maschinensprache sowie des Befehlssatzes
des  6510-Prozessors  (so wie er im 64er
seinen Dienst tut)  Grundbedingung  ist.
Desweiteren   sollten  Sie  einen  guten
Speichermonitor, bzw.  Disassembler  zur
Hand  haben, da wir der Einfachheit hal-
ber alle  Programmbeispiele  als  direkt
ausführbaren  Code der Magic Disk beifü-
gen werden. Ein gutes Hilfsmittel dieser
Art ist z.B. der, mittlerweise sehr weit
verbreitete, "SMON".                    
1) WAS SIND INTERRUPTS?                 
Das  Wort  "Interrupt" kommt aus dem En-
glischen und  bedeutet  "Unterbrechung".
Und  nichts  anderes  tut nun ein Inter-
rupt: er unterbricht den  Prozessor  bei
seiner  momentanen  Arbeit.  Das  tut er
natürlich  nicht  einfach  aus  heiterem
Himmel.  Vielmehr  hat der Programmierer
die  Möglichkeit  bestimmte  Bedingungen
anzugeben,  die einen Interrupt auslösen
sollen. Auf diese Weise  kann  man  ganz
genau  den  Zeitpunkt  bestimmen, zu dem
ein Interrupt auftreten soll.  Nun  wird
der  Prozessor  jedoch nicht nur einfach
angehalten, wenn er eine Interruptanfor-
derung  bekommt.  Da  das  Ganze ja auch
einen Nutzen haben soll, kann  natürlich
ein  ganz  bestimmtes  Programm  von ihm
abgearbeitet werden, das auf das  Inter-
ruptereignis reagieren soll.            
Bevor wir  uns  jedoch  in  diese  Dinge
stürzen,  wollen wir erst einmal klären,
welche Interrupts der 6510 versteht.  Er
kennt  insgesamt  vier Unterbrechungsty-
pen, die von ihm jeweils unterschiedlich
behandelt  werden.  Die   Unterscheidung
wird  ihm schon durch seinen hardwaremä-
ßigen Aufbau ermöglicht. Er verfügt näm-
lich  über  drei  Eingangsleitungen, die
die entsprechenden Interrupts bezeichnen
(der vierte Interrupt ist  ein  besonde-
rer,  den  wir  weiter  unten besprechen
werden). Die Chips um den Prozessor  he-
rum   sind  nun  mit  diesen  Interrupt-
Leitungen verbunden, und können  ihm  so
das Eintreten eines Unterbrechungsereig-
nisses mitteilen. Sie  sehen  also,  daß
Interrupts  hardwaremäßig ausgelöst wer-
den. Um nun eine Unterbrechung zu  einem
bestimmten Zeitpunkt auftreten lassen zu
können, sollte man sich ebenfalls in der
Programmierung  der  Hardware auskennen.
Und diese  wollen  wir  in  diesem  Kurs
natürlich auch ansprechen.              
Kommen wir nun jedoch zu den vier Inter-
rupttypen. Der Erste von ihnen ist  wohl
Einer  der  einfachsten. Es handelt sich
um den "RESET", der von keinem  externen
Chip, sondern vielmehr von einem "exter-
nen" Menschen ausgelöst wird.  Er  dient
dazu,  den Computer wieder in einen Nor-
malzustand zu versetzen. Da der  Prozes-
sor  selbst  nicht  merkt,  wann er Mist
gebaut hat und irgendwo hängt,  muß  ihm
der  Benutzer  das  durch  Drücken eines
RESET-Tasters   mitteilen.    Hieraufhin
springt  er dann automatisch in ein spe-
zielles Programm  im  Betriebssystem-ROM
und   stellt  den  Einschaltzustand  des
Rechners wieder  her.  Ein  Reset-Taster
ist  demnach  also direkt mit dem Reset-
Interrupteingang des Prozessors  verbun-
den.                                    
Der  zweite Interrupttyp heißt "NMI" und
kann ausschließlich von  CIA-B  des  C64
ausgelöst  werden. Dies ist ein speziel-
ler Chip, der die Kommunikation zwischen
externen Geräten  und  6510  ermöglicht.
Desweiteren  ist die 'RESTORE'-Taste di-
rekt mit dem NMI-Eingang des  Prozessors
verbunden. Drückt man sie, so wird eben-
falls  ein  NMI  ausgelöst.  Auch   hier
springt  der  6510  automatisch  in eine
spezielle Routine des ROMs und führt ein
NMI-Programm aus. Es prüft,  ob  zusätz-
lich   auch  noch  die  "RUN/STOP"-Taste
gedrückt wurde und verzweigt bei positi-
ver  Abfrage in die Warmstartroutine des
BASIC-Interpreters.                     
Der dritte Interrupt  ist  der  wohl  am
häufigsten  benutzte. Er heißt "IRQ" und
wird von CIA-A, dem Zwilling von  CIA-B,
sowie  dem  Videochip  (VIC)  ausgelöst.
Gerade weil  der  VIC  diesen  Interrupt
bedient, ist dies derjenige, mit dem wir
in  diesem Kurs am meisten arbeiten wer-
den.                                    
Der  vierte  und  letzte  Interrupt, ist
derjenige unter den  vieren,  der  keine
Leitung  zum  Prozessor  hat.  Das liegt
daran, daß er ein softwaremäßiger Inter-
rupt  ist,  mit  dem  sich der Prozessor
quasi selbst unterbricht. Er  wird  aus-
gelöst,   wenn   der   6510  die  "BRK"-
Anweisung ausführen soll. Demnach  heißt
er  auch  "BRK"-Interrupt.  Er  ist eine
sehr einfache Unterbrechung, die eigent-
lich  sehr selten verwendet wird, da sie
ja viel bequemer durch  ein  "JMP"  oder
"JSR"  ersetzt werden kann. Dennoch kann
er von Nutzen sein, z.B. wenn man  einen
Debugger  programmiert. Ebenso kann über
den BRK z.B.  in  einen  Speichermonitor
verzweigt  werden,  der  so die Register
oder einen bestimmten Speicherbereich zu
einem bestimmten Punkt im  Programm  an-
zeigen  kann.  Er unterscheidet sich von
den anderen Interrupts lediglich  darin,
daß er softwaremäßig ausgelöst wird.    
2) IRQ UND NMI - EIN UNGLEICHES PAAR    
Diese beiden Interrupttypen sind für uns
die Interresantesten, da mit  Ihnen  Un-
terbrechungsereignisse  der Hardware ab-
gefangen werden,  die  wir  ja  exzessiv
programmieren  werden.  Außer,  daß  sie
beide verschiende Quellen haben,  unter-
scheiden  Sie  sich  auch  noch in einem
weiteren Punkt: während  der  NMI  IMMER
ausgelöst  wird, wenn ein Unterbrechung-
sereignis eintritt, kann der  IRQ  "mas-
kiert", bzw. "abgeschaltet" werden. Dies
geschieht   über   den   Assemblerbefehl
"SEI", mit  dem  das  Interruptflag  des
Statusregisters gesetzt wird. Ist dieses
Flag  nun gesetzt, und es tritt ein IRQ-
Interruptereignis ein, so ignoriert  der
Prozessor schlichtweg das Auftreten die-
ser Unterbrechung. Dies tut er  solange,
bis  er  durch  den Asseblerbefehl "CLI"
die Instruktion erhält,  das  Interrupt-
Flag  wieder  zu löschen. Erst dann rea-
giert er wieder auf  eintretende  Unter-
brechungen.  Dies  ist ein wichtiger Um-
stand, den wir auch später  bei  unseren
IRQ-Routinen beachten müssen.           
3) INTERRUPTVEKTOREN - WEGWEISER FÖR DEN
   PROZESSOR                            
Was  geschieht  nun,  wenn der 6510 eine
der  oben  genannten  Interruptanforder-
ungen  erhält?  Er  soll  also seine mo-
mentane Arbeit unterbrechen, um  in  die
Interrupt-Routine  zu springen. Damit er
Nach Beendingung des  Interrupts  wieder
normal  fortfahren kann muß er jetzt ei-
nige Schitte durchführen:               
1) Ist  der  auftretende  Interrupt  ein
   BRK,  so  wird zunächst das dazugehö-
   rige Flag im Statusregister gesetzt. 
2) Anschließend werden High- und Lowbyte
   (in  dieser  Reihenfolge)  des   Pro-
   grammzählers, der die Speicheradresse
   des nächsten, abzuarbeitenden Befehls
   beinhaltet,  auf den Stack geschoben.
   Dadurch kann der 6510 beim Zurückkeh-
   ren  aus  dem  Interrupt  wieder  die
   ursprüngliche Programmadresse  ermit-
   teln.                                
3) Nun wird das Statusregister  auf  den
   Stack  geschoben,  damit es bei Been-
   dingung des  Interrupts  wiederherge-
   stellt werden kann, so daß es densel-
   ben Inhalt hat, als bei Auftreten der
   Unterbrechung.                       
4) Zuletzt holt sich der  Prozessor  aus
   den letzten sechs Bytes seines Adres-
   sierungsbereiches  (Adressen   $FFFA-
   $FFFF) einen von drei Sprungvektoren.
   Welchen  dieser drei Vektoren er aus-
   wählt hängt von der  Art  des  Inter-
   rupts ab, der ausgeführt werden soll.
   Hierzu eine Liste mit den Vektoren:  
   Adresse      Interrupt    Sprungadr. 
   ------------------------------------ 
   $FFFA/$FFFB  NMI          $FE43      
   $FFFC/$FFFD  RESET        $FCE2      
   $FFFE/$FFFF  IRQ/BRK      $FF48      
Da  diese  Vektoren  im ROM liegen, sind
sie schon mit bestimmten Adressen vorbe-
legt,  die  die  Betriebssystem-Routinen
zur Interruptverwaltung anspringen.  Wir
wollen  nun einmal einen Blick auf diese
Routinen werfen. Da der RESET keine  für
uns  nützliche  Unterbrechung darstellt,
wollen wir ihn jetzt  und  im  Folgenden
weglassen.  Kommen  wir  zunächst zu der
Routine für die IRQ/BRK-Interrupts.  Wie
Sie  sehen haben diese beiden Typen den-
selben Sprungvektor, werden also von der
selben Service-Routine bedient. Da diese
jedoch auch einen  Unterschied  zwischen
den  beiden  machen  soll  hat sie einen
speziellen Aufbau. Hier einmal ein  ROM-
Auszug ab der Adresse $FF48:            
FF48:  PHA          ;Akku auf Stapel    
FF49:  TXA          ;X in Akku und      
FF4A:  PHA          ; auf Stapel        
FF4B:  TYA          ;Y in Akku und      
FF4C:  PHA          ; auf Stapel        
FF4D:  TSX          ;Stackpointer nach  
FF4E:  LDA $0104,X  ;Statusreg. holen   
FF51:  AND #$10     ;BRK-Flag ausmas-   
                     kieren             
FF53:  BEQ $FF58    ;Nicht gesetzt also 
                     überspringen       
FF55:  JMP ($0316)  ;Gesetzt, also Öber 
                     Vektor $0316/$0317 
                     springen           
FF58:  JMP ($0314)  ;Öber Vektor $0314/ 
                     $0315 springen     
Dieses kleine Programm  rettet  zunächst
einmal alle drei Register, indem Sie sie
einzeln  in  den  Akku holt und von dort
auf  den  Stack  schiebt  ($FF48-$FF4C).
Dies  ist notwendig, da in der Interrup-
troutine die Register je  ebenfalls  be-
nutzt werden sollen, und so die ursprün-
glichen Inhalte beim Verlassen  des  In-
terrupts wiederhergestellt werden können
(durch   umgekehrtes   zurücklesen).  Ab
Adresse $FF4D wird nun eine  Unterschei-
dung  getroffen,  ob  ein BRK- oder IRQ-
Interrupt aufgetreten ist. Ist  ersteres
nämlich der Fall, so muß das BRK-Flag im
Statusregister,  das  bei  Auftreten der
Unterbrechung  vom  Prozessor  auf   den
Stack  geschoben  wurde,  gesetzt  sein.
Durch Zugriff auf  den  Stack,  mit  dem
Stackpointer  als  Index  in X, wird nun
das Statusregister in den  Akku  geholt.
Dies  ist  übrigens die Einzige Methode,
mit der das  Statusregister  als  Ganzes
abgefragt  werden  kann.  Natürlich geht
das immer nur aus  einem  Interrupt  he-
raus. Das BRK-Flag ist nun im 4. Bit des
Statusregisters   untergebracht.   Durch
eine UND-Verknüfung mit diesem Bit  kann
es  aus  dem Statusregisterwert isoliert
werden. Ist der  Akkuinhalt  nun  gleich
null,  so war das BRK-Flag nicht gesetzt
und es muß daher ein IRQ  vorliegen.  In
dem  Fall  wird  auf  den  JMP-Befehl an
Adresse $FF58 verzweigt. Im anderen Fall
wird der  JMP-Befehl  an  Adresse  $FF55
ausgeführt.                             
Wie  Sie sehen springen diese beiden Be-
fehle über einen indirekten Vektor,  der
beim  IRQ  in  den Adressen $0314/$0315,
beim BRK in $0316/$0317 liegt. Da  diese
Vektoren  im RAM liegen, können Sie auch
von uns verändert werden. Das  ist  also
auch der Punkt, an dem wir unsere Inter-
rupts  "Einklinken"  werden.  Durch  die
Belegung dieser Vektoren mit der Adresse
unser  eigenen  Interruptroutine  können
wir den Prozessor also zu dieser Routine
umleiten.                               
Werfen wir nun noch einen Blick auf  die
NMI-Routine,  die  durch  den Vektor bei
$FFFA/$FFFB angesprungen wird.  Sie  ist
noch  einfacher  aufgebaut  und  besteht
lediglich aus zwei Befehlen:            
FE43:  SEI          ;IRQs sperren       
FE44:  JMP  ($0318) ;Öber Vektor $0318/ 
                     $0319 springen     
Hier wird lediglich der IRQ geperrt  und
anschließend  über  den Vektor in $0318/
$0319 gesprungen. Die Akku,  X-  und  Y-
Regsiter  werden NICHT gerettet. Das muß
unsere Interruptroutine selbst tun.     
Zusammenfassend kann man also sagen, daß
beim Auslösen eines  Interrupts  jeweils
über  einen  der  drei  Vektoren, die im
Bereich von $0314-$0319 stehen, gesprun-
gen  wird.  Der angesprungene Vektor ist
dabei  interruptabhängig.  Hier  nochmal
eine  Öbersicht der Interruptvektoren im
genannten Bereich:                      
Adressen      Interrupt  Zieladr.       
---------------------------------       
$0314/$0315   IRQ        $EA31          
$0316/$0317   BRK        $FE66          
$0318/$0319   NMI        $FE47          
Diese  Vektoren  werden  bei einem Reset
mit  Standardwerten  vorbelegt.  Hierbei
wird  dann die jeweilige Standardroutine
des  Betriebssystems  angesprungen,  die
den  entsprechenden Interrupt bearbeiten
soll. Möchten wir eigene Interrupts ein-
binden,  so  müssen  wir  lediglich  die
Zieladresse der Vektoren auf den  Anfang
unserer Routine verbiegen.              
4) PROGRAMMBEISPIELE                    
Um  das Prinzip des Einbindens eines ei-
genen Interrupts kennenzulernen,  wollen
wir  nun  einmal einen eigenen Interrupt
programmieren. Es handelt sich dabei  um
den  einfachsten  von  allen,  den  BRK-
Interrupt. Hier einmal  ein  Programmli-
sting, daß Sie als ausführbares Programm
auch  auf  der Rückseite dieser MD unter
dem Namen "BRK-DEMO1" finden. Sie müssen
es absolut (mit ",8,1") in den  Speicher
laden, wo es dann ab Adresse $1000 (dez.
4096)  abgelegt  wird. Gestartet wird es
mit "SYS4096". Sie können es  sich  auch
mit Hilfe eines Disassemblers gerne ein-
mal ansehen und verändern:              
;*** Hauptprogramm                      
1000:  LDX #$1C  ;BRK-Vektor auf        
1002:  LDY #$10  ; $101C verbiegen.     
1004:  STX $0316                        
1007:  STY $0317                        
100A:  BRK       ;BRK auslösen          
100B:  NOP       ;Füll-NOP              
100C:  LDA #$0E  ;Rahmenfarbe auf dez.14
100E:  STA $D020 ; zurücksetzen         
1011:  LDX #$66  ;Normalen BRK-Vektor   
1013:  LDY #$FE  ; ($FE66) wieder       
1015:  STX $0316 ; einstellen           
1018:  STY $0317                        
101B:  RTS       ;Und ENDE!             
;*** Interruptroutine                   
101C:  INC $D020 ;Rahmenfarbe hochzählen
101F:  LDA $DC01 ;Port B lesen          
1022:  CMP #$EF  ;SPACE-Taste gedrückt? 
1024:  BNE LOOP  ;Nein, also Schleife   
1026:  PLA       ;Ja, also Y-Register   
1027:  TAY                              
1028:  PLA       ;X-Register            
1029:  TAX                              
102A:  PLA       ;u.Akku v.Stapel holen 
102B:  RTI       ;Interrupt beenden.    
In den Adressen $1000-$100A  setzen  wir
zunächst  einmal  Low-  und Highbyte des
BRK-Vektors auf Adresse $101C, wo unsere
BRK-Routine beginnt. Direkt danach  wird
mit  Hilfe des BRK-Befehls ein Interrupt
ausgelöst, der, wie wir ja  mittlerweile
wissen,  nach  Retten der Prozessorregi-
ster über den von  uns  geänderten  BRK-
Vektor auf die Routine ab $101C springt.
Selbige  tut nun nichts anderes, als die
Rahmenfarbe des Bildschirms um den  Wert
1 hochzuzählen, und anschließend zu ver-
gleichen, ob die 'SPACE'-Taste  gedrückt
wurde.  Ist dies nicht der Fall, so wird
wieder  zum  Anfang  verzweigt,  so  daß
ununterbrochen  die  Rahmenfarbe  erhöht
wird, was sich durch  ein  Farbschillern
bemerkbar  macht. Wird die 'SPACE'-Taste
nun endlich gedrückt, so kommen die fol-
genden  Befehle zum Zuge. Hier holen wir
die Prozessorregister, die von  der  Be-
triebssystem-Routine  in der Reihenfolge
Akku, X, Y auf dem Stack  abgelegt  wur-
den,  wieder  umgekehrt  zurück. Das ab-
schließende "RTI" beendet den Interrupt.
Diese Answeisung veranlasst den  Prozes-
sor  dazu, den alten Programmzähler, so-
wie das Statusregister wieder vom Stapel
zu holen, und an die Stelle im Hauptpro-
gramm  zurückzuspringen,  an der der BRK
ausgelöst wurde. Dies ist logischerweise
der Befehl direkt nach dem BRK-Kommando.
So sollte man normalerweise denken,  je-
doch nimmt BRK eine Sonderstellung dies-
bezüglich ein. Bei IRQs  und  NMIs  wird
tatsächlich  der Befehl nach dem zuletzt
bearbeiten  wieder  ausgeführt,   jedoch
wird  beim BRK der Offset 2 auf den Pro-
grammzähler hinzuaddiert,  weshalb  nach
Beendigung  des Interrupts ein Byte hin-
ter den BRK-Befehl verzweigt wird.  Dem-
nach  dient der NOP-Befehl, der nach dem
BRK kommt, lediglich dem  Angleichen  an
die  tatsächliche  Rücksprungadresse. Er
wird nie ausgeführt, da der Prozessor ja
an Adresse $100C weiterfährt.  Hier  nun
setzen  wir  die  Rahmenfarbe wieder auf
das  gewohnte  Hellblau  und  geben  dem
BRK-Vektor  wieder  seine  Ursprüngliche
Adresse zurück.  Würden  wir  das  nicht
tun,  so würde beim nächsten BRK-Befehl,
der irgendwo  ausgeführt  wird,  automa-
tisch  wieder zu unserem Bildschirmflak-
kern verzweigt werden. Hier jedoch würde
der Computer unter Umständen nicht  mehr
aus  dem  Interrupt zurückkehren können,
weil wir ja nicht wissen, welche Befehle
hinter dem auslösenden BRK standen (wenn
es  nicht  der  unseres  Programmes   an
Adresse $100A war).                     
 LADEN SIE NUN DEN 2. TEIL DES KURSES ! 
Um bei JEDEM BRK wieder in  einen  defi-
nierten Zustand zu gelangen, müssten wir
das   unterbrochene   Programm  gänzlich
stoppen und anschließend wieder zur  BA-
SIC-Eingabe  zurückkehren. Dies ist eine
sehr einfache Öbung. Wir  müssen  ledig-
lich  den  "Müll"  den Prozessor und Be-
triebssystem-BRK-Routine auf dem  Stapel
abgelegt haben, mit Hilfe von sechs auf-
einanderfolgenden   "PLA"-Befehlen   von
dort  wieder  wegholen, und anschließend
einen BASIC-Warmstart durchführen.  Noch
einfacher  und  sauberer geht es jedoch,
wenn wir den Stapelzeiger gleich nochmal
neu initialisieren. Hierbei werden  näm-
lich  auch  nicht  mehr benötigte Daten,
die evtl. vor  Auslösen  des  Interrupts
auf dem Stack abgelegt wurden, entfernt.
Demnach  kann jedes Programm mit folgen-
dem Interrupt abgebrochen werden:       
LDX #$FF   ;Stapelzeiger per X-Register 
TXS        ; zurücksetzen               
JMP $E386  ;BASIC-Warmstart anspringen. 
Es ist ein beliebiges Programm daß immer
am Ende eines Interrupts stehen kann. Es
bricht  zusätzlich auch den Interrupt ab
und kehrt zur BASIC-Eingabe zurück.     
Folgendes Programmbeispiel  nutzt  diese
Methode.  Zuvor  holt es sich jedoch die
vom Interrupt auf dem Stapel  abgelegten
Informationen, und gibt Sie in hexadezi-
maler Schreibweise  auf  dem  Bildschirm
aus.  Dadurch haben Sie eine ganz simple
Debugging-Kontrolle, mit der Sie  Fehler
in  eigenen  Programmen abfangen können.
Wird z.B.  ein  bestimmter  Programmteil
angesprungen,   der   einen   BRK-Befehl
enthält, so  wird  diese  Routine  ange-
sprungen, die Ihnen die Registerinhalte,
sowie  Zustand  des Programmzählers, des
Statusregisters  und  des  Stapelzeigers
zum  Zeitpunkt der Unterbrechung auf dem
Bildschirm ausgibt. Hier das Listing:   
;*** Hauptprogramm                      
1000:  LDX #$0B  ;BRK-Vektor auf        
1002:  LDY #$10  ; eigene Routine       
1004:  STX $0316 ; bei $100B            
1007:  STY $0317 ; verbiegen            
100A:  BRK       ;Interrupt auslösen    
;*** Interruptroutine                   
100B:  LDA #$69  ;Adresse Infotext      
100D:  LDY #$10  ; ($1069) laden        
100F:  JSR $AB1E ;Text ausgeben         
1012:  PLA       ;Inhalt Y-Register     
1013:  JSR $103E ; ausgeben             
1016:  PLA       ;Inhalt X-Register     
1017:  JSR $103E ; ausgeben             
101A:  PLA       ;Akkuinhalt            
101B:  JSR $103E ; ausgeben             
101E:  PLA       ;Statusregister        
101F:  JSR $103E ; ausgeben             
1022:  PLA       ;PC Low-Byte holen     
1023:  STA $02   ; u. zwischenspeich.   
1025:  PLA       ;PC High-Byte holen    
1026:  JSR $103E ; u. ausgeben          
1029:  LDA #$9D  ;'CRSR' left           
102B:  JSR $FFD2 ; ausgeben             
102E:  LDA $02   ;PC Low-Byte holen     
1030:  JSR $103E ; u. ausgeben          
1033:  TSX       ;Stapelzähler nach     
1034:  TXA       ; Akku übertragen      
1035:  JSR $103E ; u. ausgeben          
1038:  LDX #$FF  ;Stapelzähler          
103A:  TXS       ; initialiseren        
103B:  JMP $E386 ;BASIC-Warmstart       
Im  Bereich  von $1000-$1009 setzen wir,
wie gewohnt, den BRK-Interruptvektor auf
den Beginn unserer Routine, der in  die-
sem  Beispiel  bei $100B liegt. In $100A
wird ein BRK ausgelöst, der  die  Inter-
ruptroutine sofort anspringt. Diese gibt
nun, mit Hilfe der Betriebssystemroutine
"STROUT"  bei $AB1E, einen kleinen Info-
text auf dem  Bildschirm  aus.  Hiernach
holen  wir  uns Y- und X-Register, sowie
Akku und Statusregister vom  Stapel  (in
dieser  Reihenfolge),  und  zwar  in dem
Zustand, den sie zum Zeitpunkt, als  die
Unterbrechung  eintrat,  innehatten. Die
Werte werden dabei einzeln mit der  Rou-
tine bei $103E auf dem Bildschirm ausge-
geben. Diese Routine wandelt den Akkuin-
halt  in  eine hexadezimale Zahl um, die
auf dem Bildschirm angezeigt wird.  Hin-
ter  dieser Zahl gibt sie zusätzlich ein
Leezeichen aus, das als  optische  Tren-
nung  zwischen  diesem, und dem nächsten
Wert dienen soll. Die  genaue  Beschrei-
bung  erspare  ich  mir  hier, da sie ja
eigentlich nichts mit unseren Interrupts
zu tun hat.                             
Im  Bereich  von  $1022-$1032  wird  nun
zunächst das  Low-Byte  des  alten  Pro-
grammzählers  vom  Stapel  geholt und in
der  Speicherzelle  $02  zwischengespei-
chert.  Hieraufhin  wird  das  High-Byte
geladen und auf dem Bildschirm  ausgege-
ben. Abschließend wird das Low-Byte wie-
der aus $02 herausgeholt  und  ebenfalls
ausgegeben.  Zwischen  den beiden Zahlen
schicken  wir  noch  ein  'CRSR  links'-
Zeichen  auf den Bildschirm, da das Lee-
zeichen, das durch unsere Ausgaberoutine
zwischen High- und Lowbyte steht,  nicht
erscheinen soll. Die beiden Werte sollen
ja zusammenhängend ausgeben werden.     
Abschließend wird  der  Stapelzeiger  in
den Akku transferiert und ebenfalls aus-
gegeben. Da wir zu diesem  Zeitpunkt  ja
schon  alle  Daten, die durch den Inter-
rupt auf den Stapel gelegt wurden,  wie-
der  von dort entfernt haben, entspricht
der Stapelzeiger genau dem Wert, der zum
Zeitpunkt  der  Unterbrechung  vorhanden
war.                                    
Abschließend  wird  der Stapelzeiger wie
oben beschrieben zurückgesetzt, und  das
Programm  verzweigt  auf den BASIC-Warm-
start, womit wir den Interrupt ohne Ver-
wendung von "RTI", unter Korrektur aller
ggf.  erfolgten  Änderungen  am  Stapel,
verlassen hätten.                       
Versuchen Sie doch jetzt einmal BRKs von
anderen Adressen aus (durch "POKE Adres-
se,0:SYS  Adresse", oder in eigenen Pro-
grammen auszulösen. Sie  werden  hierbei
immer  wieder  zu  unserer kleinen Regi-
steranzeige  gelangen,  die  das  System
automatisch  wieder  ins  BASIC  zurück-
führt.  Benutzen  Sie  jedoch  nach  dem
erstmaligen  Initialiseren des Programms
nach Möglichkeit keinen  Speichermonitor
mehr,  da  diese  Hilfsprogramme nämlich
ähnlich arbeiten wie unser Programm, und
somit den  von  uns  eingestellten  BRK-
Vektor auf ihre eigenen Routinen verbie-
gen.                                    
Experimentieren Sie einmal ein wenig mit
den  BRK-Unterbrechungen,  um  sich  die
Eigenarten von Interrupts  im  Algmeinen
anzueignen.  Im nächsten Kursteil werden
wir uns dann um die Ansteuerung der NMI-
und IRQ-Unterbrechungen  kümmern,  bevor
wir uns dann im dritten Teil endlich den
interessantesten Interrupts, den Raster-
IRQs nämlich, zuwenden werden.          
                                 (ub/ih)
             Interrupt-Kurs             
     "Die Hardware ausgetrickst..."     
                (Teil 2)                
----------------------------------------
Im zweiten Teil unseres  Interruptkurses
wollen wir uns um die Programmierung von
IRQ- und NMI-Interrupts kümmern. Hierbei
soll  es  vorrangig um die Auslösung der
Beiden durch die  beiden  CIA-Chips  des
C64 gehen.                              
1) DER BETRIEBSSYSTEM-IRQ               
Um  einen  einfachen  Anfang  zu machen,
möchte ich Ihnen zunächst eine sehr sim-
ple Methode aufzeigen, mit der Sie einen
Timer-IRQ  programmiereren können. Hier-
bei machen wir uns zunutze, daß das  Be-
triebssystem  selbst schon standardmäßig
einen solchen Interrupt über  den  Timer
des  CIA-A  direkt  nach dem Einschalten
des Rechners installiert hat. Die Routi-
ne  die  diesen Interrupt bedient, steht
bei Adresse $EA31 und ist vorrangig  für
das Cursorblinken und die Tastaturabfra-
ge verantwortlich. Wichtig ist, daß  der
Timer  der CIA diesen IRQ auslöst. Hier-
bei handelt es sich um eine Vorrichtung,
mit der frei definierbare Zeitintervalle
abgewartet werden können. In Kombination
mit einem Interrupt kann so  immer  nach
einer  bestimmten  Zeitspanne ein Inter-
ruptprogramm  ausgeführt   werden.   Die
Funktionsweise  eines  Timers wollen wir
etwas   später   besprechen.   Vorläufig
genügt  es  zu wissen, daß der Betriebs-
system-IRQ von einem  solchen  Timer  im
sechzigstel-Sekunden-Takt      ausgelöst
wird. Das heißt, daß 60 Mal pro  Sekunde
das  Betriebssystem-IRQ-Programm abgear-
beitet wird. Hierbei haben wir  nun  die
Möglichkeit,   den  Prozessor  über  den
IRQ-Vektor bei $0314/$0315 auf eine  ei-
gene  Routine  springen  zu lassen. Dazu
muß dieser  Vektor  ledeiglich  auf  die
Anfangsadresse unseres eigenen Programms
verbogen  werden.  Hier  einmal ein Bei-
spielprogramm:                          
1000: SEI       ;IRQs sperren           
1001: LDX #$1E  ;IRQ-Vektor bei         
1003: LDY #$10  ; $0314/$0315 auf eigene
1005: STX $0314 ; Routine bei $101E     
1008: STY $0315 ; verbiegen             
100B: LDA #00   ;Interruptzähler in Adr.
100D: STA $02   ; $02 auf 0 setzen      
100F: CLI       ;IRQs wieder erlauben   
1010: RTS       ;ENDE                   
---                                     
1011: SEI       ;IRQs sperren           
1012: LDX #$31  ;IRQ-Vektor bei         
1014: LDY #$EA  ; $0314/$0315 wieder    
1016: STX $0314 ; auf normale IRQ-Rout. 
1019: STY $0315 ; zurücksetzen.         
101C: CLI       ;IRQs wieder erlauben   
101D: RTS       ;ENDE                   
---                                     
101E: INC $02   ;Interruptzähler +1     
1020: LDA $02   ;Zähler in Akku holen   
1022: CMP #30   ;Zähler=30?             
1024: BNE 102E  ;Nein, also weiter      
1026: LDA #32   ;Ja, also Zeichen in    
1028: STA $0427 ; $0427 löschen         
102B: JMP $EA31 ;Und SYS-IRQ anspringen 
---                                     
102E: CMP #60   ;Zähler=60?             
1030: BNE 103B  ;Nein, also weiter      
1032: LDA #24   ;Ja, also "X"-Zeichen in
1034: STA $0427 ; $0427 schreiben       
1037: LDA #00   ;Zähler wieder auf      
1039: STA $02   ; Null setzen           
103B: JMP $EA31 ;Und SYS-IRQ anspringen 
Sie finden dieses Programm übrigens auch
als ausführaren Code auf dieser MD unter
dem  Namen  "SYSIRQ-DEMO". Sie müssen es
mit "...,8,1" laden und können  es  sich
mit  einem  Disassembler  anschauen. Ge-
startet wird es  mit  SYS4096  (=$1000).
Was  Sie  daraufhin  sehen, ist ein "X",
das in der rechten, oberen Bildschirmek-
ke  im Sekundentakt vor sich hin blinkt.
Wollen wir nun klären wie  wir  das  zu-
stande gebracht haben:                  
Bei Adresse $1000-$1011 wird die  Inter-
ruptroutine  vorbereitet  und der Inter-
ruptvektor auf  selbige  verbogen.  Dies
geschieht  durch  Schreiben des Low- und
Highbytes der Startadresse unserer eige-
nen  IRQ-Routine  bei  $101E in den IRQ-
Vektor  bei  $0314/$0315.  Beachten  Sie
bitte,  daß  ich vor dieser Initialisie-
rung zunächst einmal alle IRQs mit Hilfe
des SEI-Befehls gesperrt habe. Dies  muß
getan werden, um zu verhindern, daß wäh-
rend des Verbiegens des IRQ-Vektors  ein
solcher  Interrupt  auftritt. Hätten wir
nämlich gerade erst das Low-Byte  dieser
Adresse  geschrieben, wenn der Interrupt
ausgelöst wird, so würde  der  Prozessor
an  eine  Adresse  springen, die aus dem
High-Byte des alten und dem Low-Byte des
neuen Vektors bestünde. Da dies  irgend-
eine Adresse im Speicher sein kann, wür-
de der Prozessor sich  höchstwahrschein-
lich  zu diesem Zeitpunkt verabschieden,
da er  nicht  unbedingt  ein  sinnvolles
Programm  dort  vorfindet.  Demnach  muß
also unterbunden werden, daß  solch  ein
unkontrollierter   Interrupt   auftreten
kann, indem der IRQ mittels SEI  einfach
gesperrt wird.                          
Bei $100B-$100F setzen wir nun noch  die
Zeropageadresse 2 auf Null. Sie soll der
IRQ-Routine  später  als Interruptzähler
dienen.  Anschließend  werden  die  IRQs
wieder  mittels  CLI-Befehl  erlaubt und
das Programm wird beendet.              
Durch das Verbiegen des Interruptvektors
und dadurch, daß schon ein Timer-IRQ von
Betriebssystem installiert  wurde,  wird
unser  Programm bei $101E nun 60 Mal pro
Sekunde aufgerufen.  Die  Anzahl  dieser
Aufrufe  sollen  nun zunächst mitgezählt
werden. Dies   geschieht   bei   Adresse
$101E,  wo wir die Zähleradresse bei $02
nach jedem Aufruf um  1  erhöhen.  Unser
"Sekunden-X" soll nun einmal pro Sekunde
aufblinken,  wobei es eine halbe Sekunde
lang sichtbar  und  eine  weitere  halbe
Sekunde unsichtbar sein soll. Da wir pro
Sekunde  60  Aufrufe  haben,  müssen wir
logischerweise nach  30  IRQs  das  "X"-
Zeichen löschen und es nach 60 IRQs wie-
der setzen. Dies geschieht  nun  in  den
folgenden Zeilen. Hier holen wir uns den
IRQ-Zählerstand zunächst in den Akku und
vergleichen,  ob  er  schon  bei 30 ist.
Wenn ja, so wird ein  Leerzeichen  (Code
32)   in  die  Bildschirmspeicheradresse
$0427 geschrieben. Anschließend  springt
die  Routine an Adresse $EA31. Sie liegt
im Betriebssystem-ROM  und  enthält  die
ursprüngliche        Betriebssystem-IRQ-
Routine, die ja weiterhin arbeiten soll.
Ist die 30 nicht erreicht, so wird  nach
$102E weiterverzweigt, wo wir prüfen, ob
der Wert 60 im Zähler enthalten ist. Ist
dies  der  Fall, so wird in die obig ge-
nannte   Bildschirmspeicheradresse   der
Bildschirmcode  für  das  "X"  (=24) ge-
schrieben. Gleichzeitig wird der  Zähler
in Speicherstelle 2 wieder auf 0 zurück-
gesetzt, damit der  Blinkvorgang  wieder
von  Neuem  abgezählt  werden kann. Auch
hier wird  am  Ende  auf  die  Betriebs-
system-IRQ-Routine      weiterverzweigt.
Ebenso, wenn keiner der  beiden  Werteim
Zähler  stand. Dieser nachträgliche Auf-
ruf des System-IRQs hat  zwei  Vorteile:
zum  Einen  werden die Systemfunktionen,
die von dieser Routine behandelt werden,
weiterhin  ausgeführt.  Das  heißt,  daß
obwohl  wir einen eigenen Interrupt lau-
fen haben, der Cursor und  die  Tastatu-
rabfrage weiterhin aktiv sind. Zum Ande-
ren brauchen wir uns dabei auch nicht um
das zurücksetzen der Timerregister (mehr
dazu weiter unten) oder das  Zurückholen
der  Prozessorregister  (sie  wurden  ja
beim Auftreten des IRQs auf  dem  Stapel
gerettet - sh. Teil1 dieses Kurses) küm-
mern, da das  alles  ebenfalls  von  der
System-Routine abgehandelt wird.        
Bleiben nun  nur  noch  die  Zeilen  von
$1011 bis $101E zu erläutern. Es handelt
sich  hierbei  um  ein Programm, mit der
wir unseren  Interrupt  wieder  aus  dem
System  entfernen.  Es  wird hierbei wie
bei der Initialisierung  des  Interrupts
vorgegangen.  Nach  Abschalten  der IRQs
wird die alte Vektoradresse $EA31 wieder
in $0314/$0315 geschrieben. Dadurch wer-
den  die  IRQs wieder direkt zur System-
IRQ-Routine  geleitet.  Sie  werden  nun
mittels  CLI  erlaubt  und  das Programm
wird beendet.                           
2) DIE PROGRAMMIERUNG DER TIMER         
Einen Interrupt auf  die  obig  genannte
Weise  in  das System "einzuklinken" ist
zwar eine ganz angenehme Methode, jedoch
mag es vorkommen, daß Sie für  spezielle
Problemstellungen damit garnicht auskom-
men. Um zum Beispiel einen NMI  zu  pro-
grammieren,  kommen Sie um die Initiali-
sierung des Timers nicht herum,  da  das
Betriebssystem  diesen  Interrupt  nicht
verwendet. Deshalb wollen wir nun einmal
anfangen, in die Eingeweide der Hardware
des C64 vorzustoßen um die Funktionswei-
se der CIA-Timer zu ergründen.          
Zunächst  einmal  sollte erwähnt werden,
daß die  beiden  CIA-Bausteine  einander
gleichen  wie ein Ei dem Anderen. Unter-
schiedlich ist  lediglich  die  Art  und
Weise,  wie  sie  im C64 genutzt werden.
CIA-A ist haupsächlich mit der Tastatur-
abfrage  beschäftigt  und übernimmt auch
die Abfrage der Gameports, wo  Joystick,
Maus  und  Paddles angeschlossen werden.
Sie kann die IRQ-Leitung des  Prozessors
ansprechen,  weswegen  sie zur Erzeugung
solcher Interrupts harangezogen wird.   
CIA-B  hingegen  steuert die Peripherie-
geräte, sowie den  Userport.  Zusätzlich
hierzu erzeugt sie die Interruptsignale,
die  einen  NMI auslösen. Je nach dem ob
wir nun IRQs oder NMIs erzeugen möchten,
müssen wir also entweder auf CIA-A, oder
CIA-B zurückgreifen. Hierbei  sei  ange-
merkt,  daß  wir  das natürlich nur dann
tun  müssen,  wenn  wir  einen  timerge-
steuerten  Interrupt programmieren möch-
ten. Innerhalb der  CIAs  gibt  es  zwar
noch  eine ganze Reihe weiterer Möglich-
keiten einen Interrupt zu erzeugen,  je-
doch wollen wir diese hier nicht anspre-
chen. Hier muß ich Sie auf  einen  schon
vor längerer Zeit in der MD erschienenen
CIA-Kurs,  in  dem  alle  CIA-Interrupt-
quellen  ausführlich  behandelt  wurden,
verweisen.  Wir  wollen  uns  hier  aus-
schließlich  auf  die   timergesteuerten
CIA-Interrupts konzentrieren.           
Beide  CIAs  haben  nun jeweils 16 Regi-
ster, die aufgrund der  Gleichheit,  bei
beiden  Bausteinen dieselbe Funktion ha-
ben. Einziger Unterschied ist,  daß  die
Register  von  CIA-A  bei $DC00, und die
von CIA-B bei  $DD00  angesiedelt  sind.
Diese  Basisadressen  müssen Sie also zu
dem entsprechenden, hier genannten,  Re-
gisteroffset  hinzuaddieren, je nach dem
welche CIA Sie ansprechen  möchten.  Von
den  16  Registern einer CIA sind insge-
samt  7  für   die   Timerprogrammierung
zuständig. Die anderen werden zur Daten-
ein- und -ausgabe, sowie eine  Echtzeit-
uhr  verwandt  und sollen uns hier nicht
interessieren.                          
In  jeder  der beiden CIAs befinden sich
nun zwei 16-Bit-Timer, die man mit Timer
A und B  bezeichnet.  Beide  können  ge-
trennt voneinander laufen, und getrennte
Interrupts  erzeugen, oder aber zu einem
einzigen 32-Bit-Timer kombiniert werden.
Was tut nun so ein Timer?  Nun,  prinzi-
piell  kann man mit ihm bestimmte Ereig-
nisse zählen, und  ab  einer  bestimmten
Anzahl  dieser  Ereignisse von der dazu-
gehörigen CIA einen  Interrupt  auslösen
lassen.  Hierzu hat jeder der beiden Ti-
mer zwei Register, in denen  die  Anzahl
der zu zählenden Ereignisse in Low/High-
Byte-Darstellung  geschrieben  wird. Von
diesem Wert aus zählt der Timer  bis  zu
einem Unterlauf (Zählerwert=0), und löst
anschließend  einen  Interrupt aus. Hier
eine Liste mit den 4 Zählerregistern:   
Reg. Name  Funktion                     
----------------------------------------
 4   TALO  Low-Byte Timerwert A         
 5   TAHI  High-Byte Timerwert A        
 6   TBLO  Low-Byte Timerwert B         
 7   TBHI  High-Byte Timerwert B        
Schreibt man nun  einen  Wert  in  diese
Timerregister,  so  wird selbiger in ein
internes "Latch"-Register übertragen und
bleibt dort bis zu  nächsten  Schreibzu-
griff  auf  das  Register  erhalten. Auf
diese Weise kann der Timer nach einmali-
gem  Herunterzählen,  den  Zähler wieder
mit  dem   Anfangswert   initialisieren.
Liest  man  ein solches Register aus, so
erhält man immer den  aktuellen  Zähler-
stand.  Ist  der  Timer  dabei nicht ge-
stoppt, so  bekommt  man  jedesmal  ver-
schiedene Werte.                        
Zusätzlich gibt es zu jedem  Timer  auch
noch  ein Kontrollregister, in dem fest-
gelegt wird, welche  Ereignisse  gezählt
werden  sollen. Weiterhin sind hier Kon-
trollfunktionen untergebracht, mit denen
man den Timer z.B. starten  und  stoppen
kann. Auch hier gibt es einige Bits, die
für  uns  irrelevant  sind, weswegen ich
sie hier nicht nenne. Das  Kontrollregi-
ster  für  Timer A heißt "CRA" und liegt
an Registeroffset 14, das  für  Timer  B
heißt  "CRB"  und ist im CIA-Register 15
untergebracht. Hier nun die  Bitbelegung
von CRA:                                
Bit 0 (START/STOP)                      
Mit diesem Bit schalten Sie den Timer an
(=1) oder aus (=0).                     
Bit 3 (ONE-SHOT/CONTINOUS)              
Hiermit wird bestimmt, ob der Timer  nur
ein einziges Mal zählen, und dann anhal-
ten soll (=1), oder aber nach jedem  Un-
terlauf  wieder  mit  dem Zählen vom An-
fangswert aus beginnen soll.            
Bit 4 (FORCE LOAD)                      
Ist  dieses Bit bei einem Schreibvorgang
auf das Register gesetzt,  so  wird  das
Zählregister,  unabhängig,  ob es gerade
läuft oder nicht, mit dem Startwert  aus
dem Latch-Register initialisiert.       
Bit 5 (IN MODE)                         
Dieses Bit bestimmt,  welche  Ereignisse
Timer  A  zählen soll. Bei gesetztem Bit
werden positive Signale  am  CNT-Eingang
der  CIA  gezählt.  Da das jedoch nur im
Zusammenhang mit einer  Hardwareerweite-
rung  einen Sinn hat, lassen wir das Bit
gelöscht. In dem Fall  zählt  der  Timer
nämlich  die  Taktzyklen  des  Rechners.
Dies ist generell auch  unsere  Arbeits-
grundlage,  wie  Sie  weiter unten sehen
werden.                                 
Kommen wir nun zur Beschreibung von  CRB
(Reg.  15).  Dieses Register ist weitge-
hend identisch mit  CRA,  jedoch  unter-
scheiden  sich  Bit 5 und 6 voneinander.
Diese beiden Bits bestimmen nämlich  ZU-
SAMMEN, die Zählerquelle für Timer B (IN
MODE).  Aus  den vier möglichen Kombina-
tionen sind jedoch nur zwei für uns  in-
teressant.  Setzt  man beide Bits auf 0,
so zählt Timer B wieder Systemtaktimpul-
se.  Setzt man Bit 6 auf 1 und Bit 5 auf
0, so  werden  Unterläufe  von  Timer  A
gezählt.  Auf  diese  Art und Weise kann
man beide Timer miteinander koppeln, und
somit Zählerwerte verwenden, die  größer
als  $FFFF sind (was der Maximalwert für
ein 16-Bit-Wert ist).                   
Nun wissen wir also, wie man die  beiden
Timer  initialisieren kann, und zum Lau-
fen bringt. Es fehlt nun  nur  noch  ein
Register,  um  die  volle Kontrolle über
die CIA-Timer zu haben. Es heißt "Inter-
rupt-Control-Register"  ("ICR")  und ist
in Register 13 einer CIA  untergebracht.
Mit  ihm  wird  angegeben,  welche  CIA-
Ereignisse einen Interrupt erzeugen sol-
len.  Auch hier sind eigentlich nur drei
Bits für uns von Bedeutung. Die  Restli-
chen steuern andere Interruptquellen der
CIA,  die  uns  im  Rahmen dieses Kurses
nicht interessieren sollen.             
Es sei angemerkt, daß der Schreibzugriff
auf dieses Register etwas  anders  funk-
tioniert  als  sonst.  Will  man nämlich
bestimmte Bits setzen, so muß Bit 7  des
Wertes, den wir schreiben möchten, eben-
falls gesetzt sein.  Alle  anderen  Bits
werden  dann  auch  im  ICR gesetzt. Die
Bits, die im  Schreibwert  auf  0  sind,
beeinflussen  den  Registerinhalt nicht.
So kann z.B. Bit 0 im ICR schon  gesetzt
sein.  Schreibt  man  nun  den Binärwert
10000010 (=$81) in das Register, so wird
zusätzlich noch Bit  1  gesetzt.  Bit  0
bleibt  davon  unberührt,  und ebenfalls
gesetzt  (obwohl   es   im   Schreibwert
gelöscht  ist!).  Umgekehrt,  werden bei
gelöschtem 7. Bit  alle  gesetzten  Bits
des  Schreibwertes  im  ICR gelöscht. Um
also Bit 0 und 1 zu löschen  müsste  der
Binärwert  00000011  geschrieben werden.
Näheres dazu finden Sie  in  einem  Bei-
spiel weiter unten.                     
Die nun für uns relevanten Bits sind die
schon angesprochenen Bits 0,  1  und  7.
Die  Funktion  des  7. Bits sollte Ihnen
jetzt ja klar sein. Bit 0  und  1  geben
an, ob Timer A oder Timer B (oder beide)
einen  Interrupt  auslösen  sollen.  Sie
müssen das entsprechende  Bit  lediglich
auf die oben beschriebene Art setzen, um
einen entsprechenden Interrupt zu erlau-
ben. Um z.B. einen Timer-A-Unterlauf als
Interruptquelle  zu  definieren,  müssen
Sie  den  Wert $81 in das ICR schreiben.
Für  einen  Timer-B-Unterlauf  $82.  Für
beide Timer als Interruptquelle $83.    
Das ICR hat nun noch eine weitere  Funk-
tion.  Tritt  nämlich ein Interrupt auf,
so wissen wir als Programmierer ja  noch
nicht,   ob   es  tatsächlich  ein  CIA-
Interrupt war, da es  auch  moch  andere
Interruptquellen  als  nur die CIA gibt.
Um nun zu überprüfen, ob  der  Interrupt
von  einer CIA stammt, kann das ICR aus-
gelesen werden. Ist in diesem  Wert  nun
das  7.  Bit  gesetzt, so heißt das, das
eines der erlaubten  Interruptereignisse
eingetreten  ist.  Wenn wir wissen möch-
ten, um welches Ereignis es  sich  dabei
genau handelt, brauchen wir nur die Bits
zu  überprüfen,  die die Interruptquelle
angeben. Ist Bit 0 gesetzt,  so  war  es
Timer A, der den Interrupt auslöste, ist
Bit  1 gesetzt, so kam die Unterbrechung
von Timer B. Das Auslesen  des  ICR  hat
übrigens  noch  eine  weitere  Funktion:
solange in diesem Register ein Interrupt
gemeldet ist, werden weitere  Interrupt-
ereignisse  ignoriert. Erst wenn das Re-
gister ausgelesen  wird,  wird  der  CIA
signalisiert,  daß  der Interrupt verar-
beitet wurde  und  neue  Unterbrechungen
erlaubt  sind. Auf diese Weise kann ver-
hindert werden, daß während der Abarbei-
tung  eines  Interrupts noch ein zweiter
ausgelöst wird, was womöglich das gesam-
te Interruptsystem durcheinander bringen
könnte.  Sie  müssen  also,  egal ob Sie
sicher sind, daß der Interrupt  von  der
CIA kam, oder nicht - das ICR immer ein-
mal pro Interrupt  auslesen,  damit  der
Nächste  ausgelöst werden kann. Beachten
Sie dabei auch, daß Sie das Register mit
dem Auslesen gleichzeitig auch  löschen!
Sie können den gelesenen Wert also nicht
zweimal über das Register abfragen!     
Nach  all  der trockenen Theorie, wollen
wir einmal in die Praxis  übergehen  und
uns  einem  Programmbeispiel widmen. Wir
wollen einmal ein  Sprite  mittels  Joy-
stick  in Port 2 über den Bildschirm be-
wegen. Die Abfrage desselben soll im NMI
geschehen, wobei wir CIA-B 30 Mal  einen
Timerinterrupt pro Sekunde auslösen las-
sen. Timer A soll für diese Aufgabe her-
halten.  Hier  das  Programmlisting  des
Beispiels, das Sie auf  dieser  MD  auch
unter dem Namen "NMI-SPR-DEMO" finden:  
   (Anm.d.Red.: Bitte laden Sie jetzt   
         Teil 2 dieses Artikels)        
       (Fortsetzung Interruptkurs)      
--- Initialisierung                     
1000: LDA #$01  ;Sprite 0               
1002: STA $D015 ; einschalten und       
1005: STA $D027 ; Farbe auf Weiß        
1008: LDA #$3F  ;Sprite-Pointer auf     
100A: STA $07E8 ; $3F*$40=$0FC0         
100D: LDA #$64  ;X-/Y-Position des      
100F: STA $D000 ; Sprites auf           
1012: STA $D001 ; 100/100 setzen        
1015: LDX #$0E  ;Rahmen- und Hinter-    
1017: LDY #$06  ; grundfarbe auf        
1019: STX $D020 ; Hellblau und          
101C: STY $D021 ; Dunkelblau            
101F: LDA #$7F  ;CIA-B-Interrupt-Quellen
1021: STA $DD0D ; sperren               
1024: LDA #$00  ;Timer A von CIA-B      
1026: STA $DD0E ; anhalten.             
1029: LDX #$48  ;Startadresse für neuen 
102B: LDY #$10  ; NMI (=$1048) im       
102D: STX $0318 ; Pointer bei $0318/0319
1030: STY $0319 ; ablegen.              
1033: LDX #$49  ;Timerzählwert=$8049    
1035: LDY #$80  ; (30 Mal pro Sekunde)  
1037: STX $DD04 ; in Timerzählerregister
103A: STY $DD05 ; (Timer A) schreiben   
103D: LDA #$81  ;Timer A als Interrupt- 
103F: STA $DD0D ; quelle erlauben.      
1042: LDA #$11  ;Timer starten (mit     
1044: STA $DD0E ; FORCE-LOAD-Bit)       
1047: RTS       ;ENDE                   
--- Interruptroutine                    
1048: PHA       ;Akku retten            
1049: TXA       ;X in Akku              
104A: PHA       ; und retten            
104B: TYA       ;Y in Akku              
104C: PHA       ; und retten            
104D: LDA $DD0D ;ICR auslesen           
1050: BNE $1058 ;Wenn<>0, dann CIA-NMI  
1052: INC $D020 ;Sonst NMI von RESTORE- 
                ;Taste. Rahmenfarbe erh.
1055: JMP $1073 ;Und auf Ende NMI spr.  
1058: LDA $DC01 ;CIA-NMI: Joyport2 ausl.
105B: LSR       ;Bit in Carry-Flag rot. 
105C: BCS $1061 ;Wenn gelöscht Joy hoch 
105E: DEC $D001 ;SpriteX-1              
1061: LSR       ;Bit in Carry-Flag rot. 
1062: BCS $1067 ;Wenn gel., Joy runter  
1064: INC $D001 ;SpriteX+1              
1067: LSR       ;Bit in Carry-Flag rot. 
1068: BCS $106D ;Wenn gel. Joy rechts   
106A: INC $D000 ;SpriteY+1              
106D: LSR       ;Bit in Carry-Flag rot. 
106E: BCS $1073 ;Wenn gel. Joy links    
1070: DEC $D000 :SpriteY-1              
1073: PLA       ;Y-Reg vom Stapel       
1074: TAY       ; holen                 
1075: PLA       ;X-Reg vom Stapel       
1076: TAX       ; holen                 
1077: PLA       ;Akku vom Stapel holen  
1078: CLI       ;IRQs wieder erlauben   
1079: RTI       ;RETURN FROM INTERRUPT  
--- Eigenen NMI entfernen               
107A: LDA #$7F  ;Alle NMI-Quellen von   
107C: STA $DD0D ; CIA-B sperren         
107F: LDA #$00  ;Timer A von CIA-B      
1081: STA $DD0E ; stoppen               
1084: LDX #$47  ;Alten NMI-Vektor       
1086: LDY #$FE  ; (bei $FE47) wieder in 
1088: STX $0318 ; $0318/$0319           
108B: STY $0319 ; eintragen             
108E: LDA #00   ;Sprite 0               
1090: STA $D015 ; ausschalten           
1093: RTS       ;ENDE                   
Das  Programm  starten  Sie mit SYS4096.
Hieraufhin sehen Sie einen  Sprite-Pfeil
auf  dem  Bildschirm, der mit einem Joy-
stick in Port 2 über den Bildschirm  be-
wegt  werden  kann. Gleichzeitig ist je-
doch weiterhin die  normale  Tastaturab-
frage des C64 aktiv. Die beiden Prozesse
laufen  scheinbar  gleichzeitig ab, weil
wir einen  zweiten  Interrupt  generiert
haben, der den Joystick abfragt und ent-
sprechend das Sprite bewegt. Kommen  wir
nun zur Programmbeschreibung:           
In  den ersten  Zeilen,  von  $1000  bis
$101E  wird  zunächst  einmal das Sprite
initialisiert und  die  Bildschirmfarben
auf  die  gängige  Kombination hellblau/
dunkelblau gesetzt. Hiernach  folgt  die
NMI-  und  Timerinitialisierung.  Da wir
auch hier verhindern müssen, daß ein NMI
auftritt,  während  wir  den  NMI-Vektor
verändern,  wird der Wert $7F in das ICR
geschrieben.  Dieser  Wert  löscht  alle
Interruptquellen-Bits,  womit  kein  NMI
mehr von CIA-B ausgelöst werden kann. Da
sie  der  einzige  Chip  ist,  der  NMIs
auslöst,  können  wir  sicher  sein, daß
tatsächlich  keiner  dieser   Interrupts
aufgerufen wird. Beachten Sie bitte, daß
der  SEI-Befehl  hier  wirkungslos wäre,
denn er sperrt lediglich die IRQs.  NMIs
sind nicht abschaltbar, weswegen wir sie
auf  diese  umständliche  Art  und Weise
unterbinden müssen. Hiernach halten  wir
Timer  A an, indem wir einen Wert in CRA
schreiben, der das 0. Bit gelöscht  hat,
woraufhin die CIA den Timer stoppt. Dies
müssen  wir tun, damit der Timer korrekt
mit  seinem  Anfangszählwert   gefüttert
werden  kann.  Würden  wir  nämlich  das
Low-Byte geschrieben  haben,  das  High-
Byte  jedoch noch nicht, während ein Un-
terlauf des Timers stattfindet, so würde
er mit sich mit einem falschen  Anfangs-
wert initialisieren.                    
In den folgenden  Zeilen  wird  nun  der
NMI-Vektor  bei  $0318/$0319  auf unsere
eigene NMI-Routine bei  $1048  verbogen.
Hiernach wird der Timer mit seinem Zähl-
wert initialisiert.  Er  beträgt  $8049.
Wie  wir  auf diesen Wert kommen, möchte
ich Ihnen nun erläutern: Wir  hatten  ja
schon  festgestellt,  daß  wir den Timer
vorwiegend die Takzyklen des 64ers  zäh-
len  lassen  möchten.  Selbige  sind der
Taktgeber für den Prozessor.  Der  Quarz
der  sie  erzeugt generiert zusammen mit
einigen Bausteinen um  ihn  herum  einen
Takt  von  genau  985248.4  Impulsen pro
Sekunde, die auch in Herz (Hz)  gemessen
werden.  Dieser  Wert  erntspricht  also
annähernd einem MHz. Um  nun  zu  ermit-
teln, wieviele Taktzyklen gezählt werden
müssen, damit der Timer genau 30 Mal pro
Sekunde  einen Interrupt erzeugt, müssen
wir diesen Wert lediglich durch 30 divi-
dieren.  Das  Ergebnis hiervon ist genau
32841.6, was $8049  entspricht!  Um  nun
z.B. 50 Interrupts pro Sekunde zu erzeu-
gen dividieren  Sie  einfach  durch  50,
usw. Auf diese Weise können Sie die Joy-
stickabfrage  auch  beschleunigen   oder
verlangsamen,  denn  das Sprite wird bei
betätigtem Joystick immer um  genau  so-
viele  Pixel pro Sekunde verschoben, wie
Interrupts auftreten. Versuchen Sie  die
Timerwerte doch einmal auf 100 oder nur 
20 Interrupts pro Sekunde verändern!    
Nachdem  der Timer nun mit seinem Start-
wert gefüttert wurde, erlauben  wir  den
Timer-A-Interrupt  durch  Setzen  des 0.
Bits im ICR. Hiernach muß der Timer  nur
noch  gestartet  werden. Damit er gleich
beim richtigen Wert beginnt  zu  zählen,
ist  hier  außer dem START/STOP-Bit auch
noch das FORCE-LOAD-Bit gesetzt. Das  3.
Bit ist auf 0, damit der Timer im CONTI-
NOUS-Modus läuft. Ebenso wie das 5. Bit,
das ihm sagt, daß er  Taktzyklen  zählen
soll.  Alles in allem wird also der Wert
$11 in CRA geschrieben. Von nun an läuft
der Timer und wird bei  einem  Unterlauf
einen  Interrupt  erzeugen,  der  unsere
NMI-Routine bei $1048 anspringen wird.  
Kommen wir nun zu dieser Routine selbst.
Zu Anfang müssen  wir  erst  einmal  die
drei Prozessorregister retten, da wir ja
einen  NMI  programmiert  haben, bei dem
uns die Betriebsystemsvorbeitungsroutine
diese Arbeit noch nicht  abgenommen  hat
(im Gegensatz zu der IRQ-Routine). Hier-
nach wird das ICR von CIA-B  ausgelesen.
Dadurch  ermöglichen  wir es diesem Chip
beim nächsten Timerunterlauf erneut  ei-
nen   Interrupt   auslösen   zu  können.
Gleichzeitig können wir  dadurch  abfra-
gen,  ob der NMI tatsächlich von der CIA
kam. Dies ist zwar sehr  wahrscheinlich,
da  die  CIA  der  einzige Chip ist, der
diese Art von Interrupt  erzeugen  kann,
jedoch  ist  die  RESTORE-Taste  auf der
Tastatur des C64  ebenso  mit  dem  NMI-
Eingang  des  Prozessors  verbunden. Das
heißt also,  daß  durch  Drücken  dieser
Taste  ebenfalls ein NMI ausgelöst wird.
In dem Fall erhalten wir  beim  Auslesen
des  ICR  den  Wert  0,  da ja kein CIA-
Interrupt auftrat.  Dadurch  können  wir
abfragen,  ob  es sich um unseren Timer-
NMI, oder einen RESTORE-Tastendruck han-
delt.  Ist  letzterer  der  Auslöser, so
wird einfach  die  Rahmenfarbe  um  eins
erhöht,  und zum Ende des NMIs weiterge-
prungen. Handelt es sich um  einen  CIA-
NMI,  so wird direkt zur Joystickabfrage
weiterverzweigt, in der wir Bitweise die
Portbits von CIA-A auswerten.  Öbrigens:
da in dem gelesenen Register auch Daten-
bits der Tastatur  erscheinen,  kann  es
passieren,  daß  Sie  bei bestimmten Ta-
stendrücken ebenfalls  das  Sprite  über
den  Bildschirm  zucken  sehen. Dies ist
normal und handelt sich nicht  um  einen
Fehler!                                 
Zum Abschluß der Routine holen  wir  die
drei Prozessorregister wieder vom Stapel
und  erlauben  mittels CLI die IRQs wie-
der. Selbige wurden nämlich in  der  Be-
triebsystemsvorbeitung   zum  NMI  abge-
schaltet (sh. Teil1 dieses Kurses).  Das
Programm  wird  diesmal  nicht mit einem
Sprung  auf  die  NMI-Behandlungsroutine
des  Betriebssystems  beendet,  da diese
ausschließlich für  die  Behandlung  der
RESTORE-Taste  benutzt  wird und demnach
bei einfachem Drücken der RUN-STOP-Taste
sofort  einen  BASIC-Warmstart  auslösen
würde  (so wird nämlich die Tastenkombi-
nation RUN-STOP/RESTORE abgefragt).     
Am  Ende des Programmbeispiels sehen Sie
noch  eine  Routine,  die  unsere   NMI-
Routine  wieder aus dem System entfernt.
Sie sperrt  wieder  alle  NMI-Interrupts
von  CIA-B und stoppt ihren Timer A. An-
schließend  wird  der   NMI-Vektor   bei
$0318/$0319 wieder auf die ursprüngliche
Adresse, nämlich $FE47, zurückgebogen.  
Das war es nun für diesen Monat. Probie-
ren Sie ein wenig  mit  den  Timerinter-
rupts  herum, und versuchen Sie z.B. ei-
nen solchen Interrupt von Timer B auslö-
sen  zu  lassen. Prüfen Sie auch einmal,
was passiert, wenn Sie andere Timerwerte
benutzen. Ich empfehle Ihnen, eine  Rou-
tine  zu schreiben, die immer nach genau
$4CC7 Taktzyklen ausgelöst  werden  soll
und dann für eine kurze Zeit die Rahmen-
farbe  verändert.  Sie  werden   hierbei
schon  einmal einen Vorgeschmack auf den
nächsten Kursteil bekommen, in  dem  wir
uns  endlich  dann  den Rasterinterrupts
nähern werden.                          
                                    (ub)
             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)      
4) DIE PROBLEME BEI RASTER-IRQS         
Wenn Sie unser eben  beschriebenes  Bei-
spielprogramm einmal gestartet und ange-
schaut  haben,  so  wird  Ihnen   sicher
aufgefallen  sein,  daß  die  Farbbalken
nicht ganz sauber auf dem Bildschirm  zu
sehen  waren.  Bei  jedem   Farbübergang
flackerte der Bildschirm am linken Rand.
Dies ist eines der größten Probleme  bei
der     Raster-Interrupt-Programmierung.
Dadurch nämlich,  daß  der  Rasterstrahl
mit einer Wahnsinnsgeschwindigkeit  über
den Bildschirm  huscht  können  minimale
Verzögerungen  im  Programm,   wie   zum
Beispiel ein weiterer Befehl, bevor  die
Farbänderung      geschrieben      wird,
verheerende  Folgen  haben.  In  unserem
Beispiel  werden   die   beiden   Farben
nämlich genau dann geändert,  wenn  sich
der Rasterstrahl gerade am Übergang  vom
Bildschirmrahmen  zum  Bildschirmhinter-
grund befindet.  Dadurch  entstehen  die
kleinen Farbsteifen am linken  Rand,  wo
die  Rahmenfarbe  schon  auf  den  neuen
Wert, die Hintergrundfarbe  jedoch  noch
auf dem  alten  Wert  steht.  Erst  wenn
letztere ebenfalls geändert  wurde,  ist
das Bild so  wie  gewünscht.  Zusätzlich
benötigt    unsere     Routine     immer
Unterschiedlich viel Rechenzeit, da  sie
einige Branch-Befehle  enthält,  die  je
nach Zutreffen der abgefragten Bedingung
2 oder 3 Taktzyklen dauern, weswegen die
Farbstreifen  unruhig  hin-   und   her-
springen. Zusätzlich kann es  passieren,
daß kurzfristig  einer  der  drei  Farb-
bereiche  nicht  richtig   eingeschaltet
wird,  und   dann   der   ganze   Balken
flackert. Dieser Fall  tritt  dann  ein,
wenn  Raster-  und  CIA-Interrupt   kurz
hintereinander auftreten und ein  gerade
bearbeiteter   Raster-IRQ   durch    den
CIA-IRQ nochmals unterbrochen wird.  Die
Routine   kann   also   noch   erheblich
verbessert  werden!  Stellen  Sie   doch
einmal die Farbänderung jeweils  an  den
Anfang der entsprechenden  Unterroutine,
und  sperren  Sie  gleichzeitig  mittels
"SEI" das Auftreten weiterer  IRQs.  Die
Flackereffekte  sollten   sich   dadurch
schon  um   einiges   vermindern,   aber
dennoch   werden    Sie    nicht    ganz
verschwinden. Das liegt hauptsächlich an
unserer     Verzweigungsroutine,     die
zwischen VIC- und CIA-IRQ unterscheidet.
Da  sie  für  den   Rasterstrahl   nicht
unerheblich Zeit verbraucht  wird  immer
irgendwo       eine       Assynchronität
festzustellen sein.                     
Sauberer  soll  das  nun   mit   unserem
nächsten Programmbeispiel gelöst werden.
Hier  wollen  wir   ganz   einfach   den
Betriebssystems-IRQ   von   CIA1    ganz
unterbinden,  und  ihn  quasi  über  den
Raster-IRQ 'simulieren'. Zusätzlich soll
die Abfrage nach  der  Rasterzeile,  die
den Interrupt auslöste wegfallen, so daß
wenn unsere Routine  angesprungen  wird,
immer  gleich   die   gewünschte   Farbe
eingestellt   werden   kann.   Da    wir
daraufhin auch  keine  Abfragen  mittels
Branch-Befehlen        in        unserem
Interrupt-Programm   haben,   wird   die
Routine immer mit der gleichen  Laufzeit
ablaufen,  weswegen  unruhiges   Zittern
wegfallen wird. Hier  nun  zunächst  das
Beispielprogramm.  Sie  finden  es   auf
dieser MD als  ausführbaren  Code  unter
dem Namen "RASTERDEMO2". Es wird  ebenso
geladen und gestartet wie  unser  erstes
Beispiel:                               
;*** Initialisierung                    
Init:                                   
  sei          ;IRQs sperren            
  lda #$7f     ;Alle Bits im CIA-ICR    
  sta $dc0d    ;löschen (CIA-IRQs)      
  lda $dc0d    ;CIA-ICR löschen         
  lda #$00     ;Rasterzeile 0 als Inter-
  sta $d012    ; ruptauslöser festlegen 
  lda $d011    ;Bit 7 in $D011          
  and #$7f     ; löschen.               
  sta $d011                             
  lda #$01     ;Rasterstrahl als Inter- 
  sta $d01a    ; rupt-quelle definieren 
  ldx #<(irq1) ;IRQ-Vektor bei $0314/   
  ldy #>(irq1) ; $0315 auf erste        
  stx $0314    ; Interrupt-             
  sty $0315    ; Routine verbiegen      
  cli          ;IRQs wieder freigeben   
  rts          ;ENDE!                   
;*** Erster Interrupt                   
irq1:                                   
  lda #$70     ;Zeile $70 als Auslöser  
  sta $d012    ; für nächsten IRQ festl.
  dec $d019    ;VIC-IRR löschen         
  ldx #<(irq2) ;IRQ-Vektor auf          
  ldy #>(irq2) ; zweite Interrupt-      
  stx $0314    ; Routine                
  sty $0315    ; verbiegen              
  lda #$00     ;Rahmen u. Hintergrund   
  sta $d020    ; auf 'schwarz'          
  sta $d021    ; setzen                 
  jmp $febc    ;IRQ beenden             
;*** Zweiter Interrupt                  
irq2:                                   
  lda #$C0     ;Zeile $C0 als Auslöser  
  sta $d012    ; für nächsten IRQ festl.
  dec $d019    ;VIC-IRR löschen         
  ldx #<(irq3) ;IRQ-Vektor auf          
  ldy #>(irq3) ; dritte Interrupt-      
  stx $0314    ; Routine                
  sty $0315    ; verbiegen              
  lda #$00     ;Rahmen u. Hintergrund   
  sta $d020    ; auf 'rot'              
  sta $d021    ; setzen                 
  jmp $febc    ;IRQ beenden             
;*** Dritter Interrupt                  
irq2:                                   
  lda #$C0     ;Wieder Zeile 0 als Aus- 
  sta $d012    ; löser für IRQ festl.   
  dec $d019    ;VIC-IRR löschen         
  ldx #<(irq1) ;IRQ-Vektor wieder auf   
  ldy #>(irq) ; erste Interrupt-        
  stx $0314    ; Routine                
  sty $0315    ; verbiegen              
  lda #$00     ;Rahmen u. Hintergrund   
  sta $d020    ; auf 'gelb'             
  sta $d021    ; setzen                 
  jmp $ea31    ;Betr.sys.-IRQ anspringen
Die Initialisierungsroutine dieses  Bei-
spiels unterscheidet sich kaum  von  dem
des ersten Programms.  Wir  setzen  auch
hier Zeile 0 als Auslöser für den ersten
IRQ und verbiegen den IRQ-Vektor auf die
erste IRQ-Routine mit dem Namen  "IRQ1".
Ganz am Anfang  jedoch  unterbinden  wir
alle   CIA1-IRQs,   indem   wir    durch
Schreiben des Wertes $7F alle  Bits  des
CIA-ICRs löschen,  womit  wir  alle  von
CIA1  auslösbaren  IRQs   sperren.   Das
anschließende Auslesen  des  ICRs  dient
dem 'freimachen'  der  CIA,  falls  dort
noch eine Interruptanforderung vorliegen
sollte, die nach Freigeben der  IRQs  ja
sofort  ausgeführt  und  so  in   unsere
Raster-IRQ-Routine1 springen würde.     
Die Interrupt-Routine  selbst  wird  nun
wirklich nur dann aufgerufen, wenn  sich
der Rasterstrahl in  Zeile  0  befindet.
Deshalb können  wir  hier  gleich,  ohne
große Abfragen  durchführen  zu  müssen,
Zeile $70 als nächsten Interruptauslöser
festlegen   und   die   Bildschirmfarben
ändern. Damit  hier  nun  ebenfalls  die
richtige  Routine   angesprungen   wird,
verbiegen wir den  IRQ-Vektor  noch  auf
die Routine  "IRQ2",  die  speziell  den
Interrupt bei Zeile $70 behandeln  soll.
Sie verfährt  genauso  mit  dem  dritten
Interruptauslöser, Rasterzeile $C0,  für
die die Routine  "IRQ3"  zuständig  ist.
Sie  biegt  den  IRQ-Vektor  nun  wieder
zurück auf "IRQ1", womit das  ganze  von
vorne beginnt.  Eine  Neuheit  ist  hier
übrigens das Löschen  des  VIC-IRRs.  Um
selbiges zu tun, hatten wir  in  letztem
Beispiel ja einen Schreibzuriff auf  das
Register durchführen  müssen.  Dies  tun
wir hier mit dem Befehl "DEC $D019".  Er
hat  den  Vorteil  daß  er  kürzer   und
schneller ist als  einzelnes  ein  Lesen
und Schreiben des  Registers  und  zudem
kein   Prozessorregister   in   Anspruch
nimmt, in das wir lesen müssen.         
Wie Sie  nun  sehen  werden  ist  unsere
Flagge    nun     nicht     mehr     von
Flackererffekten     gebeutelt.      Die
Farbbalken sind sauber durch horizontale
Linien voneinander  getrennt.  Versuchen
Sie jetzt doch einmal  ein  paar  andere
Farbkombinationen, oder vielleicht  auch
mehr  Rasterbalken  zu  erzeugen.   Oder
kombinieren Sie doch einmal  Hiresgrafik
mit Text. Die Möglichkeiten solch  recht
einfacherer   Raster-IRQs   sind    sehr
vielseitig, und warten Sie  erst  einmal
ab,wenn wir zu komplizierteren  Routinen
kommen. Dies wird im nächsten Monat dann
schon  der  Fall  sein,  wenn  wir   per
Rasterstrahl    die     Bildschirmränder
abschalten werden. Bis dahin  viel  Spaß
beim Rasterprogrammieren!               
                                    (ub)
        Interrupt-Kurs - Teil 4         
     "Die Hardware ausgetrickst..."     
Herzlich  Willkommen  zum  vierten  Teil
unseres IRQ-Kurses.  In  dieser  Ausgabe
möchten  wir uns mit sehr zeitkritischen
Rastereffekten  beschäftigen  und   kurz
zeigen,  wie  man den oberen und unteren
Bildschirmrand mit Hilfe  von  Rasterin-
terrupts verschwinden lassen kann.      
1) DIE TOP- UND BOTTOM-BORDER-ROUTINE   
Wie  Sie  ja  sicherlich wissen, ist der
Bildschirm des C64 auf  einen  sichbaren
Bereich  von  320x200  Pixeln, oder auch
40x25 Textzeichen beschränkt.  Der  Rest
des  Bildschirms ist von dem meist hell-
blauen  Bildschirmrahmen  verdeckt   und
kann  in der Regel nicht genutzt werden.
Werfen Sie jetzt jedoch einen Blick  auf
diese Zeilen, so werden Sie feststellen,
daß  das  Magic-Disk-Hauptprogramm genau
25 Textzeilen,  die  maximale  vertikale
Anzahl  also,  darstellt und trotzdem am
oberen Bildschirmrand die  Seitennummer,
sowie  am unteren Bildschirmrand das MD-
Logo zu sehen sind. Ganz  offensichtlich
haben  wir  es geschafft, den Bildschirm
auf  wundersame  Weise  zu   vergrößern!
Tatsächlich   schaltet   das   MD-Haupt-
programm den oberen  und  unteren  Bild-
schirmrand  schlichtweg  ab,  so daß wir
auch hier noch etwas  darstellen  können
und  auf  diese Weise mehr Informationen
auf  einer   Bildschirmseite   abzulesen
sind.  Dies ist wieder einmal nichts an-
deres als  ein  Rastertrick.  Noch  dazu
einer  der  simpelsten  die es gibt. Wie
einfach er  zu  programmieren  ist  soll
folgendes  kleines  Rasterprogramm  ver-
deutlichen. Sie finden es auf dieser  MD
unter  dem Namen "BORDERDEMO" und müssen
es wie immer mit ",8,1" laden  und  mit-
tels "SYS4096" starten:                 
INIT: SEI         ;IRQ sperren          
      LDA #$7F    ;Timer-IRQ            
      STA $DC0D   ; abschalten          
      LDA $DC0D   ;ICR löschen          
      LDA #$F8    ;Rasterzeile $F8 als  
      STA $D012   ; Interrupt-Auslöser  
      LDA $D011   ; festlegen (incl. dem
      AND #$7F    ; Löschen des HI-Bits)
      STA $D011                         
      LDA #$01    ;Raster als IRQ-      
      STA $D01A   ; Quelle wählen       
      LDX #<(IRQ) ;IRQ-Vektoren auf     
      LDY #>(IRQ) ; eigene Routine      
      STX $0314   ; verbiegen           
      STY $0315   ;                     
      LDA #$00    ;Letzte VIC-Adr. auf  
      STA $3FFF   ; 0 setzen            
      LDA #$0E    ;Rahmen- u. Hinter-   
      STA $D020   ; grundfarben auf     
      LDA #$06    ; hellblau/blau       
      STA $D021   ; setzen              
      LDY #$3F    ;Sprite-Block 13      
      LDA #$FF    ; ($0340) mit $FF     
LOOP2 STA $0340,Y ; füllen              
      DEY                               
      BPL LOOP2                         
      LDA #$01    ;Sprite 0             
      STA $D015   ; einschalten         
      STA $D027   ; Farbe="Weiß"        
      LDA #$0D    ;Spritezeiger auf     
      STA $07F8   ; Block 13 setzen     
      LDA #$64    ;X- und Y-Pos.        
      STA $D000   ; auf 100/100         
      STA $D001   ; setzen              
      CLI         ;IRQs freigeben       
      RTS         ;ENDE                 
IRQ   LDA $D011   ;Bildschirm           
      AND #$F7    ; auf 24 Zeilen       
      STA $D011   ; Umschalten          
      DEC $D019   ;VIC-ICR löschen      
      LDX #$C0    ;Verzögerungs-        
LOOP1 INX         ; schleife            
      BNE LOOP1                         
      LDA $D011   ;Bildschirm           
      ORA #$08    ; auf 25 Zeilen       
      STA $D011   ; zurückschalten      
      INC $D001   ;Sprite bewegen       
END   JMP $EA31   ;Weiter zum SYS-IRQ   
Wie Sie sehen, besteht  die  eigentliche
IRQ-Routine, die den  Border  abschaltet
nur aus  einer  handvoll  Befehlen.  Die
Initialisierung der Routine sollte Ihnen
noch aus dem  letzten  Kursteil  bekannt
sein. Wir  sperren  hier  zunächst  alle
IRQs und verhindern, daß CIA-A ebenfalls
IRQs auslöst, damit  unser  Rasterinter-
rupt nicht gestört  wird.  Als  nächstes
wird Rasterzeile $F8  als  Interruptaus-
löser festgelegt, was  auch  einen  ganz
bestimmten Grund  hat,  wie  wir  weiter
unten sehen werden. Nun sagen  wir  noch
dem VIC, daß  er  Rasterinterrupts  aus-
lösen soll, und verbiegen den IRQ-Vektor
bei $0314/$0315 auf unsere  eigene  Rou-
tine namens  "IRQ".  Die  nun  folgenden
Zeilen dienen  lediglich  "kosmetischen"
Zwecken. Wir  setzen  hier  Rahmen-  und
Hintergrundfarben auf die  Standardwerte
und  schalten  Sprite  0  ein,  das  von
unserer Interruptroutine in  der  Verti-
kalen pro IRQ um einen  Pixel  weiterbe-
wegt werden soll. Zudem wird der Sprite-
block, der dieses Sprite darstellt,  mit
$FF gefüllt, damit wir ein schönes  Qua-
drat auf dem Bildschirm sehen und keinen
Datenmüll.  Nach   Freigabe   der   IRQs
mittels "CLI" wird dann wieder  aus  dem
Programm  zurückgekehrt.  Von   nun   an
arbeitet  unsere  kleine,   aber   feine
Raster-IRQ-Routine.   Damit   Sie    sie
verstehen, müssen wir nun ein  wenig  in
die Funktionsweise des VIC einsteigen:  
Normalerweise zeigt  uns  der  Videochip
des C64, wie oben schon erwähnt, ein  25
Text-, bzw. 200 Grafikzeilen hohes Bild.
Nun können wir die  Bildhöhe  mit  Hilfe
von Bit 3 in Register 17 des VICs auf 24
Textzeilen reduzieren. Setzen wir es auf
"1",  so  werden  25  Textzeilen  darge-
stellt, setzen wir es auf "0", so  sehen
wir lediglich 24 Textzeilen. Im  letzte-
ren  Fall  werden  dann   jeweils   vier
Grafikzeilen  des  oberen  und   unteren
Bildschirmrandes  vom   Bildschirmrahmen
überdeckt. Diese Einschränkung  ist  vor
allem  bei  der   Programmierung   eines
vertikalen Soft-Scrollers von Bedeutung.
Effektiv zeichnet der VIC nun  also  den
oberen Bildschirmrand vier  Rasterzeilen
länger und den unteren  vier  Rasterzei-
len  früher.  Um  nun  den   Rahmen   zu
zeichnen kennt die Schaltlogik  des  VIC
zwei  Rasterzeilen,  die  er   besonders
behandeln  muß.  Erreicht   er   nämlich
Rasterzeile $F7, ab der  der  Bildschirm
endet, wenn die  24  Textzeilen-Darstel-
lung aktiv ist, so prüft er,  ob  Bit  3
von Register 17 gelöscht ist.  Wenn  ja,
so beginnt er den Rand zu zeichnen, wenn
nein, so fährt er normal fort.  Erreicht
er dann Rasterzeile $FB,  die  das  Ende
eines 25-zeiligen Bildschirms darstellt,
wird nochmals geprüft, ob das obige  Bit
auf 0 ist. Wenn ja, so weiß der VIC, daß
er mit dem Zeichnen  des  Rahmens  schon
begonnen hat. Wenn nein, so  beginnt  er
erst jetzt damit. Mit unserem  Interrupt
tricksen wir den armen Siliziumchip  nun
aus. Unsere Routine wird immer  in  Ras-
terzeile $F8  angesprungen,  also  genau
dann, wenn der VIC die 24-Zeilen-Prüfung
schon vorgenommen hat. Da  die  Darstel-
lung auf 25  Zeilen  war,  hat  er  noch
keinen Rand  gezeichnet.  Unsere  Inter-
ruptroutine schaltet  nun  aber  auf  24
Zeilen  um  und  gaukelt  dem  VIC   auf
diese Weise vor, er hätte schon mit  dem
Zeichnen des Randes begonnen, weshalb er
nicht nocheinmal beginnen muß, und somit
ohne zu  zeichnen  weitermacht.  Dadurch
erscheinen  unterer  und  oberer   Bild-
schimrand in der  Hintergrundfarbe,  und
es ist kein  Rahmen  mehr  sichtbar.  In
diesen  Bereichen  kann  man  nun   zwar
keinen  Text  oder  Grafik   darstellen,
jedoch  sind  Sprites,  die  sich   hier
befinden durchaus sichtbar!  Sie  werden
normalerweise nämlich einfach vom Rahmen
überdeckt, sind aber dennoch  vorhanden.
Da der Rahmen nun aber  weg  ist,  sieht
man  auch  die  Sprites,  wie  das  sich
bewegende Sprite  0  unserer  Interrupt-
routine beweist!                        
Wichtig an unserer Routine ist nun noch,
daß wir vor Erreichen des  oberen  Bild-
randes die Darstellung nocheinmal auf 25
Zeilen zurückschalten, damit  der  Trick
beim nächsten Rasterdurchlauf nocheinmal
klappt. Hierbei darf  natürlich  frühes-
tens dann umgeschaltet werden, wenn  der
Rasterstrahl an der  zweiten  Prüf-Posi-
tion, Rasterzeile $FB, schon vorbei ist.
Dies wird durch die kleine Verzögerungs-
schleife bewirkt, die genau 4 Rasterzei-
len wartet, bevor mit dem anschließenden
ORA-Befehl Bit 3 in Register 17 des  VIC
wieder gesetzt  wird.  Am  Ende  unseres
Interrupts bewegen wir das  Sprite  noch
um eine Y-Position weiter und verzweigen
zum   Betriebssystem-IRQ,   damit    die
Systemaufgaben trotz abgeschalteter  CIA
dennoch ausgeführt  werden.  Die  inter-
ruptauslösende  Rasterzeile  muß   nicht
nochmal neu eingestellt werden,  da  wir
diesmal nur eine Rasterzeile haben,  die
jedesmal wenn sie  erreicht  wird  einen
Interrupt auslöst.                      
Wollen wir nun noch  klären,  warum  wir
bei  der  Initialisierung  eine   0   in
Adresse $3FFF geschrieben haben. Wie Sie
vielleicht wissen, kann  der  VIC  Spei-
cherbereiche von lediglich  16KB  adres-
sieren, aus denen er  sich  seine  Daten
holt. Im Normalfall ist das der  Bereich
von $0000-$3FFF.  Die  letzte  Speicher-
zelle seines Adressbereichs hat nun eine
besondere Funktion. Der Bit-Wert, der in
ihr steht, wird nämlich in allen Spalten
der Zeilen des nun nicht  mehr  überdek-
kten Bildschirmrandes dargestellt -  und
zwar immer in schwarzer Farbe. Durch das
Setzen dieser Zelle auf 0 ist hier  also
gar  nichts  sichtbar.   Schreiben   wir
jedoch  bei  aktivierter   Borderroutine
mittels   "POKE16383,X"   andere   Werte
hinein, so werden je nach Wert mehr oder
weniger  dicke,  vertikale   Linien   in
diesem Bereich  sichtbar.  Durch  Setzen
aller Bits mit Hilfe des Wertes 255 (mit
Rahmenfarbe=schwarz), können  wir  sogar
einen scheinbar vorhandenen  Bildschirm-
rand simulieren!                        
Vielleicht fällt Ihnen nun auch noch ein
interessanter Nebeneffekt  auf:  nachdem
wir die oberen und  unteren  Bildschirm-
grenzen  abgeschaltet  haben,  gibt   es
Spritepositionen, an  denen  das  Sprite
zweimal zu sehen ist. Nämlich sowohl  im
oberen, als auch  im  unteren  Teil  des
Bildschirms. Das liegt  daran,  daß  das
PAL-Signal,  welches  der  VIC   erzeugt
313 Rasterzeilen  kennt,  wir  aber  die
Y-Position eines  Sprites  nur  mit  256
verschiedenen  Werten  angeben   können.
Dadurch stellt der VIC das Sprite an den
Y-Positionen zwischen 0 und 30 sowohl am
unteren, als auch am  oberen  Rand  dar.
Bei eingeschalteten Rändern fiel  dieser
Nebeneffekt nie auf,  da  diese  Sprite-
positionen normalerweise im unsichtbaren
Bereich des Bildschirms liegen,  wo  sie
vom Bildschirmrahmen überdeckt werden.  
Bleibt noch zu erwähnen, daß wir mit ei-
nem ähnlichen Trick auch die  seitlichen
Ränder des Bildschirms verschwinden las-
sen  können,  nur  ist  das  hier   viel
schwie-  riger,  da  es  auf  ein   sehr
genaues Timing ankommt.  Wie  man  damit
umgeht  müssen  wir  jetzt   erst   noch
lernen, jedoch werde ich in den nächsten
Kursteilen auf dieses Problem nocheinmal
zu sprechen kommen.                     
2) EINZEILEN-RASTER-EFFEKTE             
Kommen wir nun zu dem oben schon erwähn-
ten Timing-Problem. Vielleicht haben Sie
nach  Studieren  des  letzten  Kursteils
einmal versucht  einzeilige  Farbraster-
effekte zu programmieren. Das heißt also
daß Sie  gerade  eine  Zeile  lang,  die
Bildschirmfarben wechseln, und sie  dann
wieder  auf  die  Normalfarbe  schalten.
Hierzu wären dann zwei Raster-Interrupts
notwendig,  die  genau  aufeinander   zu
folgen haben  (z.B.  in  Zeile  $50  und
$51).  Wenn  Sie  verschucht  haben  ein
solches Rasterprogramm zu schreiben,  so
werden Sie bestimmt eine Menge  Probleme
dabei gehabt haben, da  die  Farben  nie
genau   eine   Rasterzeile   lang    den
gewünschten  Wert  enthielten,   sondern
mindestens   eineinhalb   Zeilen    lang
sichtbar  waren.  Dieses   Problem   hat
mehrere  Ursachen,   die   hauptsächlich
durch  die  extrem  schnelle  Gesschwin-
digkeit  des   Rasterstahls   entstehen.
Selbiger bewegt sich nämlich in genau 63
Taktzyklen einmal von links nach rechts.
Da innerhalb  von  63  Taktzyklen  nicht
allzu viele Instruktionen vom  Prozessor
ausgeführt  werden  können,  kann  jeder
Befehl zuviel eine zeitliche Verzögerung
verursachen, die  eine  Farbänderung  um
mehrere Pixel nach rechts verschiebt, so
daß die Farbe nicht am Anfang der Zeile,
sondern erst  in  ihrer  Mitte  sichtbar
wird. Da ein IRQ  nun  aber  verhältnis-
mäßig viel Rechenzeit benötigt,  bis  er
abgearbeitet ist, tritt  ein  Raster-IRQ
in der nächsten Zei- le  meist  zu  früh
auf, nämlich noch bevor  der  erste  IRQ
beendet   wurde!   Dadurch   gerät   das
Programm natürlich  vollkommen  aus  dem
Takt  und  kann  ggf.  sogar  abstürzen!
Noch dazu muß  ein  weiterer,  hardware-
mäßiger Umstand beachtet werden: hat man
normale Textdarstellung  auf  dem  Bild-
schirm eingeschaltet,  so  muß  der  VIC
nämlich  jedes  mal  zu   Beginn   einer
Charakterzeile die 40  Zeichen  aus  dem
Video-RAM lesen, die er in den folgenden
acht Rasterzeilen darzustellen hat,  und
sie entsprechend in ein Videosignal  um-
wandeln. Um diesen Vorgang durchzuführen
hält er den Prozessor für eine Zeit  von
genau 42 Taktzyklen an, damit  er  einen
ungestörten Speicherzugriff machen kann.
Eine Charakterzeile  ist  übrigens  eine
der 25 Textzeilen. Da der Bildschirm  in
der Regel bei Rasterzeile  $32  beginnt,
und jede achte Rasterzeile  ein  solcher
Zugriff durchgeführt  werden  muß,  sind
all  diese  Zeilen  besonders  schwierig
über einen Raster-IRQ programmierbar, da
erst nach dem VIC-Zugriff ein Raster-IRQ
bearbeitet werden kann, der jedoch durch
den Zugriff schon viel zu spät eintritt,
da die  Zeile  in  der  Zwischenzeit  ja
schon zu zwei Dritteln aufgebaut  wurde.
Hier  muß  man  sich  eines   speziellen
Tricks  behelfen.  Um  selbigen   besser
erläutern zu können, wollen wir uns  das
folgende Beispielprogramm  einmal  etwas
näher anschauen:                        
;************************************** 
INIT  SEI         ;IRQs sperren         
      LDA #$7F    ;CIA-A-IRQs           
      STA $DC0D   ; unterbinden         
      LDA $DC0D                         
      LDA #$82    ;Rasterzeile $82 als  
      STA $D012   ; Interruptauslöser   
      LDA $D011   ; festlegen (incl.    
      AND #$7F    ; Löschen des         
      STA $D011   ; HI-Bits             
      LDA #$01    ;Rasterstrahl ist     
      STA $D01A   ; IRQ-Auslöser        
      LDX #<(IRQ) ;IRQ-Vektor auf eigene
      LDY #>(IRQ) ; Routine verbiegen   
      STX $0314                         
      STY $0315                         
      CLI         ;IRQs freigeben       
VERZ  RTS         ;ENDE                 
;************************************** 
IRQ   DEC $D019   ;VIC-IRQ freigeben    
      JSR VERZ    ;Verzögern...         
      JSR VERZ                          
      NOP                               
      LDY #$00    ;Farb-Index init.     
LOOP1 LDX #$08    ;Char-Index init.     
LOOP2 LDA $1100,Y ;Farbe holen          
      STA $D020   ; und im Rahmen-und   
      STA $D021   ; Hintergrund setzen  
      INY         ;Farb-Index+1         
      DEX         ;Char-Index-1         
      BEQ LOOP1   ;Wenn Char=0 verzweig.
      LDA VERZ    ;Sonst verzögern...   
      JSR VERZ                          
      JSR VERZ                          
      JSR VERZ                          
      CPY #$48    ;Farb-Index am Ende?  
      BCC LOOP2   ;Nein also weiter     
      LDA #$0E    ;Sonst Rahmen/Hinter- 
      STA $D020   ; grund auf           
      LDA #$06    ; Standardfarben      
      STA $D021   ; zurücksetzen        
      JMP $EA31   ;IRQ mit SYS-IRQ beend
Besten laden Sie das Programm einmal und
starten es mit "SYS4096". Sie sehen  nun
einen schönen Rasterfarbeneffekt auf dem
Bildschirm, wo wir ab Rasterzeile $83 in
jeder Zeile die Rahmen- und Hintergrund-
farbe ändern. Ich habe  hierbei  Farbab-
stufungen benutzt,  die  schöne  Balken-
effekte  erzeugen.   Die   entsprechende
Farbtabelle liegt ab  Adresse  $1100  im
Speicher und kann  natürlich  von  Ihnen
auch verändert werden.  Kommen  wir  nun
zur Programmbeschreibung. Die  Initiali-
sierungsroutine sollte Ihnen keine  Pro-
bleme bereiten. Wir schalten  wie  immer
die CIA ab, sagen dem VIC, daß er  einen
Raster-IRQ generieren soll,  legen  eine
Rasterzeile (hier Zeile  $82)  als  IRQ-
Auslöser fest, und  verbiegen  die  IRQ-
Vektoren  auf  unser  eigenes  Programm.
Etwas seltsam mag Ihnen nun die  eigent-
liche IRQ-Routine vorkommen. Nachdem wir
mit dem  DEC-Befehl  dem  VIC  bestätigt
haben, daß der IRQ  bei  uns  angekommen
ist,   folgen   nun   drei,    scheinbar
sinnlose, Befehle. Wir springen nämlich 
das   Unterprogramm   "VERZ"   an,   das
lediglich aus einem RTS-Befehl  besteht,
und somit  direkt  zu  unserem  Programm
zurückverzweigt. Zusätzlich  dazu  folgt
noch ein NOP-Befehl der ebensowenig tut,
wie die  beiden  JSRs  zuvor.  Der  Sinn
dieser Instruktionen liegt lediglich  in
einer  Zeitverzögerung,  mit   der   wir
abwarten, bis der Rasterstrahl  am  Ende
der Zeile $82 angelangt ist. Wir  hätten
hier auch jeden anderen Befehl verwenden
können, jedoch ist es mit JSRs  am  ein-
fachsten zu verzögern,  da  ein  solcher
Befehl, ebenso  wie  der  folgende  RTS-
Befehl, jeweils 6 Taktzyklen verbraucht.
Durch  einen  JSR-Befehl  vergehen  also
genau 12  Taktzyklen,  bis  der  nächste
Befehl abgearbeitet wird.  Da  ein  NOP-
Befehl, obwohl  er  nichts  macht,  zwei
Taktzyklen zur Bearbeitung benötigt, und
wir zwei JSRs verwenden,  verzögern  wir
also um insgesamt 26  Taktzyklen.  Genau
diese  Verzögerung  ist  dem  DEC-Befehl
zuvor und den folgenden  LDX-  und  LDY-
Befehlen   notwendig,   um   soviel   zu
verzögern, daß sich der Rasterstrahl bis
ans Ende  der  Rasterzeile  bewegt  hat.
Hinzu kommt daß wir  die  42  Taktzyklen
hinzurechnen müssen,  die  der  VIC  den
Prozessor sowieso schon gestoppt hat, da
Rasterzeile  $82  eine  der  schon  oben
angesprochenen Charakterzeilen darstellt
($82-$32=$50/8=10 - ohne Rest!).        
 Laden Sie nun bitte Teil 2 des Kurses! 
Ich hoffe, Sie nun nicht unnötig mit dem
Gerede von Taktzyklenzahlen verwirrt  zu
haben. Im Endeffekt kommt es darauf  an,
das Ende der entsprechenden  Rasterzeile
abgewartet zu haben. Wieviel Verzögerung
dazu  notwendig  ist,  muß  nicht   groß
berechnet werden, sondern  wird  in  der
Regel einfach ausprobiert. Sie fügen der
IRQ-Routine einfach so viele Verzögerun-
gen hinzu, bis eine  Farbänderung  genau
in einer Zeile liegt, und nicht irgendwo
mitten in der Rasterzeile anfängt.      
Beachten Sie bitte, daß Sie die Verzöge-
ung   für   eine    Nicht-Charakterzeile
erweitern müssen, da  in  diesen  Zeilen
dem   Prozessor   ja   42    zusätzliche
Taktzyklen zur Verfügung stehen!        
Kommen wir nun zu den folgenden Instruk-
tionen. Auch hier  haben  wir  es  nicht
einfach mit irgendeinem Programm zu tun,
sondern mit einer sorgfältigen Folge von
Befehlen, die  genau  darauf  abgestimmt
ist, immer solange zu dauern, bis  genau
eine Rasterzeile beendet  ist.  Wie  ich
zuvor erwähnte sind das immer  genau  63
Taktzyklen pro Rasterzeile, in denen der
Prozessor  irgendwie  beschäftigt   sein
muß, damit die nächste Farbänderung  zum
richtigen Zeitpunkt eintritt. Wie  immer
funkt uns jede achte Rasterzeile der VIC
dazwischen,  der  den   Prozessor   dann
wieder für  42  Takte  anhält,  weswegen
unsere Routine jede achte Rasterzeile   
nicht mehr und nicht weniger als        
63-42=21 Taktzyklen dauern darf! Da die 
nun folgende Beschreibung etwas haarig  
wird, und schnell in arithmetisches     
Taktzyklenjonglieren ausartet, hier no- 
cheinmal die Farbänderungsschleife aus  
unserem Beispielprogramm, wobei ich hier
die Kommentare durch die Zyklenzahlen je
Befehl ersetzt habe:                    
      LDY #$00    ;2                    
LOOP1 LDX #$08    ;2                    
LOOP2 LDA $1100,Y ;4                    
      STA $D020   ;4                    
      STA $D021   ;4                    
      INY         ;2                    
      DEX         ;2                    
      BEQ LOOP1   ;2 oder 3             
      LDA VERZ    ;4                    
      JSR VERZ    ;12                   
      JSR VERZ    ;12                   
      JSR VERZ    ;12                   
      CPY #$48    ;2                    
      BCC LOOP2   ;2 oder 3             
Der LDY-Befehl am Anfang ist  eigentlich
weniger wichtig, ich habe  ihn  nur  der
Vollständigkeit halber  aufgeführt.  Wir
haben hier zwei verschachtelte Schleifen
vor uns. Die eine, mit dem Namen "LOOP1"
wird immer nur  jede  achte  Rasterzeile
aufgerufen,  nämlich  dann,  wenn   eine
Charakterzeile beginnt.  Diese  Schleife
wird über das X-Register indiziert.  Die
zweite Schleife wird vom Y-Register  ge-
steuert, das gleichzeitig  Indexregister
für unsere Farbtabelle  bei  $1100  ist.
Wichtig ist nun der zeitliche Ablauf der
beiden Schleifen.  Wie  wir  ja  wissen,
müssen wir in einer  Charakterzeile  mit
unserem  Programm  21   und   in   einer
normalen   Rasterzeile   63   Taktzyklen
verbrauchen.  Da  wir  uns  beim  ersten
Schleifendurchlauf genau in  Rasterzeile
$83 befinden, beginnt die Schleife  also
zunächst in einer  normalen  Rasterzeile
(eine Zeile nach einer  Charakterzeile).
Hier wird  die  Schleife  ab  dem  Label
"LOOP2" bis zum Ende ("BCC LOOP2") abge-
arbeitet. Wenn Sie jetzt die  Taktzyklen
am Rand innerhalb dieses  Bereichs  auf-
addieren, so vergehen bis zum BCC-Befehl
genau 60 Zyklen. Der BCC-Befehl hat  nun
eine  ganz  besondere   Funktion.   Alle
Branch-Befehle verbrauchen  nämlich  bei
nicht zutreffender Abfragebedingung  nur
zwei Taktzyklen (so auch beim  zuvorigen
BEQ-Befehl der das X-Register  abfrägt).
Trifft die Bedingung  zu,  so  wie  auch
beim abschließenden  BCC,  so  muß  ver-
zweigt  werden,  was   einen   weiteren,
dritten Taktzyklus  in  Anspruch  nimmt.
Dadurch sind also  genau  60+3=63  Takt-
zyklen verstrichen,  wenn  die  Schleife
das nächste Mal  durchlaufen  wird.  Und
das ist genau die Zeit die vergehen muß,
bis der  Rasterstrahl  in  der  nächsten
Zeile ist, wo die Farbe erneut  geändert
werden kann. Kommt der Strahl nun wieder
in eine  Chakterzeile,  so  ist  das  X-
Register auf Null heruntergezählt. Durch
die zutreffende Abfragebedingung im BEQ-
Befehl dauert die Verzweigung  nun  drei
Takte. Vom Label "LOOP2" bis zu dem BEQ-
Befehl verbrauchen wir  also  nach  Adam
Riese nun 19 Taktzyklen. Da der  Branch-
Befehl zum Label "LOOP1" verzweigt,  und
der dortige LDX-Befehl wiederum 2 Zyklen
benötigt, sind genau 21  Takte  verstri-
chen, wenn sich der Prozessor wieder  am
Startpunkt, "LOOP2"  nämlich,  befindet.
Und das ist wieder genau  die  Zeit  die
verstreichen  musste,   damit   in   der
Charakterzeile der  Rasterstrahl  wieder
am Anfang der folgenden Zeile steht! Sie
sehen also,  wie  sehr  es  auf  genaues
Timing hier ankommt!  Fügen  Sie  dieser
Kerschleife auch nur einen Befehl hinzu,
oder entfernen Sie einen, so  gerät  das
gesamte  Timing  ausser  Kontrolle   und
unsere  Farbbalken  erscheinen  verzerrt
auf dem  Bildschirm.  Probieren  Sie  es
ruhig einmal aus!                       
Zum Abschluß  des  Raster-IRQs  schalten
wir    nun    wieder    die     normalen
Bildschirmfarben ein und verzweigen  zum
Betriebssystems-IRQ.                    
3) WEITERE PROGRAMMBEISPIELE            
Außer  den  beiden  bisher  besprochenen
Programmen finden Sie auf dieser MD noch
drei weitere  Beispiele,  die  lediglich
Variationen   des   letzten    Programms
darstellen. Alle drei werden  wie  immer
mit ",8,1"  geladen  und  mit  "SYS4096"
gestartet.    "RASTCOLOR2"    entspricht
haargenau "RASTCOLOR1", nur daß ich hier
am Ende eine Routine  hinzugefügt  habe,
die  die  Farbtabelle  um  jeweils  eine
Zeile weiterrotiert.  Das  Ergebnis  des
Ganzen sind rollende und nicht  stehende
Farbbalken.                             
Die  Programme  "RASTSINUS1"  und   "-2"
funktionieren   nach   einem   ähnlichen
Prinzip.  Hier  wird  jedoch  nicht  die
Farbe in  den  angegebenen  Rasterzeilen
verändert,   sondern   der   horizontale
Verschiebeoffset.   Dadurch   kann   der
entsprechende   Bildbereich   effektvoll
verzerrt     werden.     Starten     Sie
"RASTSINUS1"  und  fahren  Sie  mit  dem
Cursor in die  untere  Bildschirmhälfte,
so werden dort alle Buchstaben  in  Form
einer Sinuskurve verzerrt.  "RASTSINUS2"
geht noch  einen  Schritt  weiter.  Hier
werden die Werte der  Sinustabelle,  wie
auch schon bei "RASTCOLOR2" am Ende  der
Interruptroutine gerollt,  weswegen  der
gerasterte Bereich,  wasserwellenähnlich
hin- und  her"schlabbert".  Schauen  Sie
sich  die  Programme  ruhig  einmal  mit
Hilfe  eines  Speichermonitors  an,  und
versuchen Sie ein paar Änderungen  daran
vorzunehmen. Im nächsten Kursteil werden
wir noch ein wenig mehr  mit  Taktzyklen
herumjonglieren und  uns  mit  FLD-  und
Sideborder-Routinen beschäftigen.       
                                    (ub)
             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.)  
3) DIE ZYKLENTABELLE                    
Kommen wir nun noch zu der  Tabelle  mit
den  Zyklendauern der einzelnen Befehle.
Da viele darunter mit  unterschiedlichen
Adressierungsarten verwendbar sind, habe
ich   Ihnen   zwei  Einzeltabellen  auf-
geführt, in denen  ähnliche  Befehle  zu
Gruppen  zusammengefasst  wurden. Kommen
wir hierbei zunächst zu  den  impliziten
Befehlen.  Sie  bestehen  lediglich  aus
einem Befehlswort und  benötigen  keinen
Operanden:                              
Befehl Zyklen                           
-------------                           
 ASL     2                              
 LSR     2                              
 ROL     2                              
 ROR     2                              
 CLC     2                              
 SEC     2                              
 CLD     2                              
 SED     2                              
 CLI     2                              
 SEI     2                              
 CLV     2                              
 NOP     2                              
 RTS     6                              
 RTI     6                              
 BRK     7                              
 TAX     2                              
 TAY     2                              
 TXA     2                              
 TYA     2                              
 TXS     2                              
 TSX     2                              
 PLA     4                              
 PHA     3                              
 PLP     4                              
 PHP     3                              
 INX     2                              
 DEX     2                              
 INY     2                              
 DEY     2                              
Es folgen nun die Befehle, die  entweder
direkt,  oder über Speicheradressen eine
Operation  mit  den   Prozessorregistern
durchführen.  Die Bitschiebebefehle kom-
men hier nochmals vor, da sie  auch  mit
Adressierung  verwendbar sind. Die Spal-
ten der Tabelle stehen (von  links  nach
rechts)  für: "IMMediate", wenn der Ope-
rand  ein  konstanter  Wert  ist   ("LDA
#00"),  "ABSolut", für dirkete Speicher-
adressierung ("LDA $1000"),  "ABSolut,X"
("LDA $1000,X"), "ABSolut,Y", "ZeroPage"
("LDA $02"), "ZeroPage,X", "ZeroPage,Y",
Zeropage  indirekt-implizit "(zp,X)" und
Zeropage implizit-idirekt "(zp),Y". Alle
Zyklenangaben, die mit  einem  "*"  mar-
kiert  sind  verlängern  sich  um  einen
Taktzyklus, wenn bei dieser Adressierung
eine Bereichsüberschreitung stattfindet,
was bedeutet, daß  wenn  die  Summe  des
Offsetregisters  und des Basiswertes das
High-Byte überschreitet, ein  Takt  mehr
benötigt  wird,  als angegeben. Dies ist
z.B. bei dem Befehl  "LDA  $10FF,X"  der
Fall.  Dann,  wenn nämlich im X-Register
ein Wert größer oder gleich 1 steht:    
Bef Imm Abs Abs Abs ZP ZP ZP (,X) (,Y)  
            ,X  ,Y    ,X  ,Y            
----------------------------------------
BIT  -   4   -   -  3  -  -   -    -    
CPX  2   4   -   -  3  -  -   -    -    
CPY  2   4   -   -  3  -  -   -    -    
CMP  2   4   4*  4* 3  4  -   6    5*   
ADC  2   4   4*  4* 3  4  -   6    5*   
SBC  2   4   4*  4* 3  4  -   6    5*   
AND  2   4   4*  4* 3  4  -   6    5*   
ORA  2   4   4*  4* 3  4  -   6    5*   
EOR  2   4   4*  4* 3  4  -   6    5*   
INC  -   6   7   -  5  6  -   -    -    
DEC  -   6   7   -  5  6  -   -    -    
LDA  2   4   4*  4* 3  4  -   6    5*   
LDX  2   4   -   4* 3  -  4   -    -    
LDY  2   4   4   -  3  4* -   -    -    
STA  -   4   5   5  3  4  -   6    6    
STX  -   4   -   -  3  -  4   -    -    
STY  -   4   -   -  3  4  -   -    -    
ASL  -   6   7   -  5  6  -   -    -    
LSR  -   6   7   -  5  6  -   -    -    
ROL  -   6   7   -  5  6  -   -    -    
ROR  -   6   7   -  5  6  -   -    -    
JMP  -   3   -   -  -  -  -   -    -    
JSR  -   6   -   -  -  -  -   -    -    
Zudem kennt der JMP-Befehl auch noch die
indirekte Adressierung, über einen Low-/
High-Bytezeiger  (z.B.  "JMP  ($A000)"),
der 5 Taktzyklen verbraucht.            
Es  fehlen  jetzt  nur  noch die Branch-
Befehle, die jedoch nicht in einer eige-
nen  Tabelle  erscheinen  müssen, da sie
immer 2 Taktzyklen verbrauchen.  Es  sei
denn,  die vom Befehl abgefragte Beding-
ung trifft zu. In diesem Fall  wird  ein
weiterer,  dritter  Takt in Anspruch ge-
nommen.                                 
Das war es dann wieder für diesen Monat.
Im nächsten Kursteil werden wir uns wei-
terhin  ein  wenig  mit  Timingproblemen
beschäftigen  müssen,  und  uns ansehen,
wie man IRQs "glättet".  Dies  soll  uns
dann  als Grundlage für den nächsten Ra-
ster-Effekt dienen:  einer  Routine  zum
Abschalten  der  seitlichen  Ränder  des
Bildschirms.                            
                                    (ub)
             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!)      
    Interruptkurs (Teil6 - 2.Hälfte)    
----------------------------------------
Kommen  wir  nun zur ersten FLD-Routine.
In ihr wird der Interrupt geglättet, was
eine besonders trickreiche Angelegenheit
ist. Sehen Sie sich  hierzu  einmal  den
Sourcecode an:                          
;*** FLD-Routine mit Glättung ($1100)   
FLD1  pha        ;Akku, X- u. Y-Reg.    
      txa        ; retten               
      pha                               
      tya                               
      pha                               
      dec $d019  ;neue IRQs erlauben    
      inc $d012  ;nächte Raster.=Ausl.  
      lda #<FLD2 ;Lo-Byte von FLD-IRQ2- 
      sta $fffe  ;Routine setzen        
      cli        ;IRQs erlauben         
WIRQ  nop        ;13 NOPs               
      nop        ; (innerhalb dieser    
      nop        ;  Schleife wird der   
      nop        ;  Interrupt ausge-    
      nop        ;  löst werden!!)      
      nop                               
      nop                               
      nop                               
      nop                               
      nop                               
      nop                               
      nop                               
      nop                               
      jmp QIRQ                          
FLD2  pla        ;Programmzähler und    
      pla        ; Statusreg. gleich    
      pla        ; wieder v. Stack holen
      nop        ;19 NOPs zum Verzögern 
      nop        ; bis zum tatsächlichen
      nop        ; Charakterzeilen-     
      nop        ; anfang               
      nop                               
      nop                               
      nop                               
      nop                               
      nop                               
      nop                               
      nop                               
      nop                               
      nop                               
      nop                               
      nop                               
      nop                               
      nop                               
      nop                               
      nop                               
      lda $d012  ;Den letzten Zyklus    
      cmp $d012  ; korrigieren          
      bne cycle                         
cycle ...        ;eigentlicher IRQ      
Hier  werden  Ihnen  einige  Dinge etwas
merkwürdig vorkommen (vor allem die vie-
len NOPs). Beginnen wir von Anfang an:  
In der Borderroutine hatten wir die  Ra-
sterzeile  festgelegt,  in der die FLD1-
Routine angesprungen werden  soll.  Dies
war  Zeile  61 ($3D), die genau zwei Ra-
sterzeilen  vor  der  eigentlichen  IRQ-
Rasterzeile  liegt.  In diesen zwei Zei-
len, die wir den  IRQ  früher  ausgelöst
haben, werden wir ihn jetzt glätten. Wie
in  jedem  Interrupt retten wir zunächst
die Prozessorregister.  Daran  anschlie-
ßend   wird  das  Low-Byte  der  Routine
"FLD2" in das Low-Byte  des  IRQ-Vektors
geschrieben, und die nächste Rasterzeile
(durch  den INC-Befehl) als nächster In-
terruptauslöser festgelegt. Beachten Sie
hierbei, daß die diese Routine  dasselbe
High-Byte  in der Adresse haben muß, wie
die erste FLD-Routine. Das kann man  da-
durch  erzielen,  daß  "FLD1"  an  einer
Adresse mit 0-Lowbyte  ablegt  wird  und
sofort  danach  die Routine "FLD2" folgt
(im Beispiel ist FLD1 an Adresse $1100).
Um einen neuen Interrupt zu ermöglichen,
müssen noch das ICR des VIC und das  In-
terrupt-Flag   des  Prozessors  gelöscht
werden (beachten Sie, daß letzteres  vom
Prozessor  automatisch bei Auftreten des
IRQs gesetzt wurde).                    
Es  folgt  nun  der  eigentliche  "Glät-
tungs-teil".  Hierzu lassen wir den Pro-
zessor  ständig   durch   eine   Endlos-
Schleife  mit  NOP-Befehlen  laufen. Da-
durch wird sichergestellt, daß  der  Ra-
ster-IRQ  der  nächsten  Rasterzeile  in
jedem Fall während der Ausführung  eines
NOP-Befehls  auftritt.  Da dieser Befehl
nur 2 Taktzyklen  verbraucht,  kann  die
Verzögerung  des  Interupts nur 0 oder 1
Taktzyklen lang sein. Diesen  einen  Zy-
klus  zu korrigieren ist nun die Aufgabe
des zweiten FLD-IRQs. Nachdem  er  ange-
sprungen  wurde  holen wir gleich wieder
die, vom Prozessor automatisch  gerette-
te,  Programmzähleradresse  und das Sta-
tusregister vom Stapel, da sie  nur  zum
ersten  FLD-Interrupt  gehören,  in  den
nicht mehr  zurückgekehrt  werden  soll.
Danach  folgen  19  NOP-Befehle, die nur
der Verzögerung dienen, um das Ende  der
Rasterzeile  zu  erreichen.  Die letzten
drei Befehle  sind  die  Trickreichsten!
Sie   korrigieren  den  einen  möglichen
Verzögerungs-Zyklus.    Zum     besseren
Verständnis sind sie hier nochmal aufge-
listet:                                 
      lda $d012  ;Den letzten Zyklus    
      cmp $d012  ; korrigieren          
      bne cycle                         
cycle ...        ;eigentlicher IRQ      
Obwohl  diese  Folge  recht unsinnig er-
scheint, hat sie es ganz schön in  sich:
Wir laden hier zunächst den Akku mit dem
Inhalt  von Register $D012, das die Num-
mer der aktuell bearbeiteten Rasterzeile
beinhaltet, und vergleichen  ihn  sofort
wieder  mit diesem Register. Danach wird
mit Hilfe des BNE-Befehls auf  die  Fol-
geadresse verzweigt, was noch unsinniger
erscheint.                              
Der  LDA-Befehl  befindet sich nun durch
die NOP-Verzögerung genau an  der  Kippe
zur  nächsten Rasterzeile, nämlich einen
Taktzyklus bevor  diese  Zeile  beginnt.
Sollte  nun  der FLD2-IRQ ohne den einen
Taktzyklus  Zeitverzögerung   ausgeführt
worden  sein,  so  enthält der Akku z.B.
den Wert 100. Der  CMP-Befehl  ist  dann
ein  Vergleich  mit dem Wert 101, da der
Rasterstahl nach dem LDA-Befehl schon in
die nächste Zeile  gesprungen  ist.  Da-
durch  sind  die  beiden Werte ungleich,
womit das Zero-Flag  gelöscht  ist,  und
der  Branch tatsächlich ausgeführt wird.
Beachten Sie nun, daß ein  Branch-Befehl
bei  zutreffender  Bedingung  durch  den
Sprung einen Taktzyklus mehr  Zeit  ver-
braucht,  als bei nicht zutreffender Be-
dingung (3 Zyklen!).  War  der  FLD2-IRQ
allerdings   mit  dem  einem  Taktzyklus
Verzögerung  aufgetreten,  so  wird  der
LDA-Befehl  genau  dann ausgeführt, wenn
Register  $D012  schon  die  Nummer  der
nächsten  Rasterzeile enthält, womit der
Akku den Wert 101 beinhaltet. Durch  den
Vergleich  mit  dem  Register,  das dann
immer noch den Wert  101  enthält,  wird
das  Zero-Flag  gesetzt,  da  die beiden
Werte identisch sind. Dadurch trifft die
Bedingung des BNE-Befehls nicht zu,  und
er  verbraucht  nur  2  Taktzyklen! Dies
gewährleistet, daß in beiden Fällen  im-
mer  die  gleiche  Zyklenzahl verbraucht
wird! War der FLD2-IRQ ohne Verzögerung,
so verbracht die  Routine  einen  Zyklus
mehr,  als  wenn  er  mit  einem  Zyklus
Verspätung auftrat!                     
Hiermit hätten wir den IRQ also  geglät-
tet  und können die eigentliche FLD- und
Farbsetz-Routine ausführen. Beachten Sie
für Folgebeispiele, daß wir  in  Zukunft
auf  diese  Weise die IRQs immer glätten
werden müssen, um saubere Ergebnisse  zu
erzielen. Hierzu wird immer wieder diese
Routine  verwandt, wobei das eigentliche
IRQ-Programm dann nach dem Branch-Befehl
eingesetzt wird. Gleichmäßiger kann  man
Raster-IRQ   nun   wirklich  nicht  mehr
ausführen!                              
Nach  dem  Glätten folgt die eigentliche
Interruptroutine, und zwar  direkt  nach
dem Label "Cycle". Sie setzt Rasterzeile
$F8  als Interruptauslöser fest und ver-
biegt den IRQ-Vektor wieder auf die Bor-
derroutine,   womit  der  Kreislauf  von
Neuem beginnt.  Gleichzeitig  setzt  Sie
die  Darstellung  auf  25 Zeilen zurück,
damit  der  Bordereffekt  auch  funktio-
niert.  Anschließend wird der FLD-Effekt
durchgeführt, indem der Zeilenanfang vor
dem Rasterstrahl hergeschoben wird. Wäh-
renddessen  werden  die Vorder- und Hin-
tergrundfarbe nach zwei Farbtabellen bei
$1200 und $1300 verändert.  Der  Raster-
split  wird  durch ausreichende Verzöge-
rung bis zur Mitte einer Rastezeile  er-
zeugt. Zum Schluß des IRQs wird noch bis
zum  Ende der Rasterzeile verzögert, und
die Standardfarben zurückgesetzt,  bevor
die  ursprünglichen  Inhalte der Prozes-
sorregister, wie sie vor  dem  Auftreten
des FLD1-IRQs vorhanden waren, zurückge-
holt werden und der IRQ beendet wird:   
Cycle dec $d019  ;VIC-ICR löschen       
      lda #$18   ;25-Zeilen-Darst. ein- 
      sta $d011  ; schlaten (Bordereff.)
      lda #$f8   ;Rasterz. $F8 ist näch-
      sta $d012  ; ster IRQ-Auslöser    
  ldx #<Bord ;IRQ-Vektor                
      ldy #>Bord ; auf                  
      stx $fffe  ; Border-Routine       
      sty $ffff  ; verbiegen            
      nop        ;Verzögern             
      lda $02    ;FLD-Zähler laden      
      beq FDEnd  ; Wenn 0, kein FLD!    
      ldx #$00   ;Zählreg.f.Farben init.
      clc                               
FDLop lda $d012  ;FLD-Sequenz (Zeilen-  
      adc #$02   ; anfang vor Raster-   
      and #$07   ; strahl herschieben   
      ora #$18                          
      sta $d011                         
      lda $1200,x;Farbe links holen     
      sta $d020  ; und setzen        sta
$d021                                   
      nop        ;Verzögern bis Mitte   
      nop                               
      nop                               
      nop                               
      nop                               
      nop                               
      nop                               
      lda $1300,x;Farbe rechts holen    
      sta $d020  ; und setzen           
      sta $d021                         
      bit $ea    ;Verzögern             
      inx        ;Farb-Zähler+1         
      cpx $02    ;Mit FLD-Zähler vgl.   
      bcc FDLop  ;ungl., also weiter    
FDEnd nop        ;Verz. bis Zeilenende  
      nop                               
      nop                               
      nop                               
      nop                               
      lda #$0e   ;Vorder-/Hintergrund-  
      sta $d020  ; farben auf           
      lda #$06   ; hellblau u. dunkel-  
      sta $d021  ; blau setzen          
      pla        ;Akku, X- und Y-Reg.   
      tay        ; zurückholen          
      pla                               
      tax                               
      pla                               
      rti        ;IRQ beenden           
Das war es dann wieder für diesen Monat.
Im  nächsten  Kursteil  werden  wir eine
weitere Anwendung besprechen,  die  eine
IRQ-Glättung  benötigt:  die Sideborder-
routinen zum Abschalten des  linken  und
rechten Bildschirmrands, nämlich.       
                                    (ub)
             Interrupt-Kurs             
     "Die Hardware ausgetrickst..."     
                (Teil 7)                
----------------------------------------
Nachdem wir uns im letzten Kursteil aus-
giebig  um das IRQ-Timing gekümmert hat-
ten, wollen wir in  dieser  Ausgabe  nun
eine  Anwendung  besprechen, bei der vor
allem das Glätten  von  Interrupts  eine
große Bedeutung einnimmt: es geht um die
Sideborderroutinen.                     
1) DAS PRINZIP                          
Im  dritten Kursteil hatten wir ja schon
gelernt, wie man den oberen und  unteren
Bildschirmrand  abschaltet.  Wir  hatten
dem VIC hierzu zunächst mitgeteilt,  daß
er einen 25-Zeilen hohen Bildschirm dar-
stellen soll. Zwei Rasterzeilen bevor er
jedoch  den  Rand  desselben   erreichte
schalteten   wir   ihn   auf  24-Zeilen-
Darstellung  um,  weswegen  er  glaubte,
schon  zwei  Rasterzeilen vorher mit dem
Zeichnen des Randes begonnen  zu  haben.
Da dies aber nicht der Fall war, und der
VIC   seine  Arbeit  normal  fortführte,
"vergaß" er sozusagen, den  unteren  und
den  oberen  Bildschirmrand zu zeichnen,
was uns ermöglichte, in diesen Bereichen
Sprites  darzustellen.  Derselbe   Trick
funktioniert nun auch mit dem linken und
rechten  Bildschirmrand  - zumindest vom
Prinzip her. Bit 3 in VIC-Register $D016
steuert die Breite des sichtbaren  Bild-
schirms.  Ist  dieses  Bit  gesetzt,  so
zeichnet der VIC 320 sichtbare Pixel  in
der  Vertikalen,  was  einer Darstellung
von 40  Zeichen  pro  Zeile  entspricht.
Löschen wir dieses Bit, so stellt er nur
302  Pixel,  bzw.  38  Zeichen pro Zeile
dar. Dieses Bit wird vor allem zum  ver-
tikalen  Scrollen  verwendet, da man bei
einem 38-Spalten-Bildschirm neu  herein-
laufende  Bildschirmzeichen setzen kann,
ohne daß sie vom Betrachter gesehen wer-
den.                                    
Durch das rechtzeitige  Setzen  und  Lö-
schen  dieses Bits kann man nun auch den
linken und  rechten  Bildschirmrand  ab-
schalten.  Hierbei  liegt  die  Betonung
besonders auf dem Wort "rechtzeitig". Da
der Rasterstrahl in der vertikalen  näm-
lich derart schnell ist, daß der Prozes-
sor kaum nachkommt, müssen wir den Zeit-
punkt  der Umschaltung sehr genau abpas-
sen, damit  unser  Effekt  funktioniert.
Bei  der  38-Zeichen-Darstellung beginnt
der linke  Bildschirmrand  nur  8  Pixel
später  und endet nur 8 Pixel früher als
sonst. Da aber genau dann, wenn  er  En-
det,  umgeschaltet  werden  muß, und der
Rasterstrahl zum Zeichnen von  8  Pixeln
gerade  mal 1.2 Taktzyklen benötigt, ha-
ben wir eben nur genau  diese  Zeit  zur
Verfügung, um Bit 3 in Register $D016 zu
löschen.  Da  ein  "STA $D016" aber drei
Taktzyklen verbraucht, muß der Prozessor
diesen Befehl also schon zwei Zyklen vor
dem Bildschirmrand erreichen und  begin-
nen    abzuarbeiten.   Der   eigentliche
Schreibzugriff findet  dann  im  dritten
Taktzyklus,  genau  zwischen 38- und 40-
Zeichen-Rand statt.  Zur  Verdeutlichung
sollten Sie sich einmal das Programmbei-
spiel "SIDEBORDER.0" anschauen. Wie alle
unsere Beispiele ist  es  absolut  (also
mit  ",8,1") zu laden und mit SYS4096 zu
starten. Sie verlassen die  Routine  mit
einem  Druck  auf  den  Feuerknopf eines
Joysticks in Port2. Mit Joystickbewegun-
gen  nach  oben und unten können Sie die
Anzahl der zu öffnenden  Zeilen  variie-
ren.                                    
Wir  möchten  Ihnen  nun die Kernroutine
des Beispiels zeigen. Die Erklärung  der
Interruptinitialisierung  werden wir uns
an dieser Stelle sparen, da wir  sie  ja
schon  oft  genug  besprochen  haben. Im
Beispiel selbst haben  wir  alle  Inter-
ruptquellen,  außer  den  Interrupts vom
Rasterstrahl gesperrt. Desweiteren wurde
das  Betriebssystems-ROM   abgeschaltet,
und   die  Adresse  auf  unsere  Routine
gleich in  den  Vektor  bei  $FFFF/$FFFE
geschrieben,  so  daß der Prozessor ohne
Zeitverzögerung   zu   unserer   Routine
springt. Selbige "glättet" den Interrupt
zunächst  nach  der  im letzten Kursteil
beschriebenen Art und Weise. Direkt nach
der Glättung folgt nun diese Routine:   
     ...                                
     LDX #00     ;Zeilenzähler löschen  
     CLC                                
LOOP NOP         ;7 NOPs Verzögerung    
     NOP         ;  bis Zeilenende      
     NOP                                
     NOP                                
     NOP                                
     NOP                                
     NOP                                
     LDA #00     ;EINEN Taktzykl. vor   
     STA $D016   ;Beg. d.rechten Randes 
     LDA #08     ;Bit 3 löschen und     
     STA $D016   ;gleich wieder setzen  
     NOP         ;13 weitere NOPs zum   
     NOP         ; Verzögern von 26     
     NOP         ; Taktzyklen           
     NOP                                
     NOP                                
     NOP                                
     NOP                                
     NOP                                
     NOP                                
     NOP                                
     NOP                                
     NOP                                
     NOP                                
     BIT $EA     ;3 Zyklen verzögern    
     INX         ;Linienzähler+1        
     CPX $02     ;Mit Anz.zu öffnender  
     BCC LOOP    ;Zeilen vgl. u. weiter 
     ...                                
Diese  Abfolge  von  Befehlen beinhaltet
nun haargenau das benötigte Timing,  da-
mit  der Sideborder-Effekt auch sichtbar
wird. Sie sehen hier  zunächst  7  NOPs,
mit  denen  wir vom Ende der Rasterglät-
tung bis zum Beginn des sichbaren  rech-
ten Bildschirmrandes verzögern. Hierauf-
hin  wird  Bit  3  von  Register   $D016
gelöscht,  um  auf 38-Zeilen-Darstellung
umzuschalten, und gleich  darauf  wieder
gesetzt,  damit  das  Umschalten  in der
nächsten Rasterzeile noch genauso effek-
tiv  ist.  Anschließend  wird mit den 13
NOPs und dem BIT-Befehl um insgesamt  29
Taktzyklen  verzögert,  und  geprüft, ob
das X-Register, das als Zähler der schon
geöffneten  Rasterzeilen  fungiert,  den
Wert  der  zu  öffnenden Zeilen enthält,
der in Speicherzelle $02  abgelegt  ist.
Wenn  nicht,  so  wird wieder zum Anfang
der Schleife verzweigt  und  somit  eine
weitere Rasterzeile geöffnet.           
2) EINE VERFEINERUNG                    
Wenn  Sie  das Beispiel einmal gestartet
und angesehen haben, so werden  Sie  be-
merken,  daß  wir  lediglich  im oberen,
sonst mit dem Rahmen verdeckten  Bereich
des   Bildschirms  den  seitlichen  Rand
geöffnet haben. Das funktioniert  natür-
lich nur, wenn wir den VIC zuvor mit dem
normalen  Bordereffekt überlistet haben,
so daß er den oberen und  unteren  Bild-
schirmrand  ebenfalls  wegfallen  lässt.
Desweiteren kann der Rand dort  nur  bis
zur  letzten  Zeile,  des (unsichtbaren)
oberen Bildschirmrandes geöffnet werden,
was einen besonderen Grund hat: Wie  wir
mittlerweile  ja  wissen,  liest der VIC
zum Beginn  einer  jeden  Charakterzeile
(also jede achte Rasterzeile), die 40 in
dieser   Zeile  darzustellenden  Zeichen
ein, und blockiert während  dieser  Zeit
den Prozessor für eine Zeitspanne von 42
Taktzyklen.  Dadurch gerät natürlich un-
ser ganzes Timing durcheinander, weshalb
der Effekt an diesen Stellen nicht  mehr
zu sehen ist (obwohl immer noch dieselbe
Schleife  läuft!).  Um nun innerhalb des
normalen  Bildschirms,  in  dem  Zeichen
dargestellt  werden  können, den Rand zu
öffnen, verkompliziert sich  das  Timing
natürlich  erheblich,  da wir jede achte
Rasterzeile 42  Taktzyklen  weniger  zur
Verfügung haben. Die einfachste Methode,
dieses  Problem zu umgehen, ist die Kom-
bination der Sideborderroutine mit einer
FLD-Routine. Dadurch schieben wir ja den
Beginn der nächsten  Charakterzeile  vor
dem VIC her, so daß er keine Daten liest
und  uns  somit  nicht  bei  der  Arbeit
stört. Diesen  Weg  geht  unser  zweites
Programmbeispiel mit dem Namen "SIDEBOR-
DER.1". Hier das Listing  der  entschei-
denden  Schleife.  Auch  hier  wurde der
Interrupt natürlich zuvor geglättet:    
     ...                                
LOOP LDA $D012  ;FLD-Routine            
     ADC #$02                           
     AND #$07                           
     ORA #$18                           
     STA $D011                          
     LDA #$00   ;wieder 1 Zykl. vor Beg.
     STA $D016  ;d. rechten Randes Bit 3
     LDA #$08   ;von $D016 löschen und  
     STA $D016  ;gleich wieder setzen   
     NOP        ;13 weitere NOPs zum    
     NOP        ; Verzögern von 26      
     NOP        ; Taktzyklen            
     NOP                                
     NOP                                
     NOP                                
     NOP                                
     NOP                                
     NOP                                
     NOP                                
     NOP                                
     NOP                                
     NOP                                
     BIT $EA    ;3 Zyklen verzögern     
     INX        ;Linienzähler+1         
     CPX $02    ;Mit Anz. zu öffnender  
     BCC LOOP   ;Zeilen vgl. u. weiter  
     ...                                
Im Prinzip  hat  sich  hier  nicht  viel
geändert.  Nur  daß die 7 NOPs am Anfang
der Routine den fünf Befehlen  des  FLD-
Effektes  gewichen  sind. Diese brauchen
ebenso 14 Taktzyklen, so daß unsere Rou-
tine  vom  Timing her immer noch genauso
funktioniert, wobei wir den  Bildschirm-
rand jedoch innerhalb des normalen Bild-
bereichs abgeschaltet haben!            
3) SPRITES IM SIDEBORDER                
Wie  bei  den Bildschirmrändern oben und
unten kann man  natürlich  auch  in  den
seitlichen, abgeschalteten Rändern Spri-
tes darstellen. Dies gestaltet sich  je-
doch  ebenfalls  schwieriger  als sonst.
Nämlich so wie der VIC den Prozessor  in
jeder   Charakterzeile  anhält,  um  un-
gestört die Charakterdaten zu lesen,  so
hält er ihn auch an, wenn er Spritedaten
zu  lesen  hat.  Hierbei  prüft  der VIC
zunächst einmal, ob ein  Sprite  an  der
aktuellen    Rasterposition    überhaupt
sichtbar ist. Wenn ja, so liest  er  die
drei Datenbytes des Sprites, die in die-
ser Rasterzeile anzuzeigen sind.  Hierzu
benötigt er ungefähr 2.4 Taktzyklen, die
uns von der Prozessorzeit natürlich wie-
der abgehen! Somit muß  die  Verzögerung
innerhalb unserer Schleife verkürzt wer-
den. Da wir aber  keine  Bruchteile  von
Taktzyklen  verzögern  können,  wird die
ganze Sache umso haariger!!! In der  Re-
gel  hilft hier nur die alte "Trial-and-
Error"-Methode, um das  perfekte  Timing
genau  auszuloten.  Zum  Einschalten von
sieben Sprites müssen wir nach  dem  Lö-
schen  und  Setzen  des  Anzeigebits von
Register $D016 nur noch  12  Takte,  an-
stelle  von ursprünglich 29, warten, bis
der Rasterstrahl die nächste  Zeile  er-
reicht. Es gehen uns also 17 Zyklen ver-
loren!  Programmbeispiel  "SIDEBORDER.2"
benutzt  anstelle  der  13  NOPs und dem
BIT-Befehl nur noch 6  NOPs.  Sehen  Sie
sich  dieses  Beispiel  ruhig einmal mit
einem Disassembler oder  Speichermonitor
an.  Wenn  Sie durch Herunterdrücken des
Joysticks den geöffneten Bereich vergrö-
ßern,  so werden Sie merken, daß der Si-
deborder-Effekt nur in den  Rasterzeilen
funktioniert,   in   denen  die  Sprites
sichtbar sind. Das gilt auch dann,  wenn
Sie  die Sprites in Y-Richtung expandie-
ren (vor dem Aufruf  des  Beispiels  PO-
KE53271,255 eingeben). Hier funktioniert
der  Effekt  dann doppelt soviele Zeilen
lang, da der VIC bei Expansion jede Zei-
le  eines  Sprites einfach doppelt liest
(auf zwei Rasterzeilen verteilt).  Möch-
ten  Sie den Rand weiter nach unten öff-
nen, so ist nur noch ein  komisches  Li-
nien-Wirr-Warr  zu  sehen,  da in diesen
Zeilen keine Sprites  angezeigt  werden,
und somit wieder eine Verzögerung von 29
Taktzyklen  von  Nöten wäre! Wohlgemerkt
funktioniert  dieser  Effekt  nur,  wenn
auch  wirklich in jeder Rasterzeile sie-
ben Sprites darzustellen sind.  Schalten
Sie  weniger  oder  mehr Sprites ein, so
ist der  Sidebordereffekt  zunichte  ge-
macht.  Das  gleiche  passiert, wenn Sie
nicht alle sieben Sprites Y-expandieren.
Hier Endet der Effekt  nach  21  Zeilen,
nämlich dann wenn ein nicht-expandiertes
Sprite  zu  Ende gezeichnet ist, und Sie
sehen im Border nur die Hälfte  der  ex-
pandierten Sprites!                     
4) HINWEISE ZUM OPTIMALEN TIMING        
Möchten Sie nun selbst  die  Verzögerung
ausloten,  die zur Darstellung einer be-
stimmten Anzahl Sprites  benötigt  wird,
so beachten Sie folgende Regeln:        
* Geben Sie allen Sprites, die in Berei-
  chen  angezeigt  werden,  in denen der
  Sideborder abgeschaltet ist,  ein  und
  dieselbe  Y-Koordinate, so daß sie ho-
  rizontal in einer  Linie  liegen.  Da-
  durch  werden Ungleichheiten innerhalb
  der  Rasterzeilen  vermieden,  so  daß
  immer gleich viele Spritedaten gelesen
  werden.   Gleiches  gilt  für  die  Y-
  Expandierung. Es sollten entweder alle
  oder keines der, in einem solchen  Be-
  reich  sichtbaren,  Sprites expandiert
  sein (es sei denn Sie möchten nur  die
  Hälfte  eines expandierten Sprites se-
  hen).                                 
* Achten  Sie darauf, daß andere Sprites
  nicht in den Bereich  ohne  Sideborder
  hineinragen,  da  auch  sie das Timing
  durcheinanderbringen.                 
* Beim  Ausloten  der  Verzögerung  sind
  NOP- und  BIT-Befehle  am  Besten  zum
  schrittweisen   Timing-Test  geeignet.
  Möchten Sie eine ungerade  Anzahl  Zy-
  klen  verzögern, so benutzen Sie einen
  Zeropage-BIT-Befehl (so wie  der  oben
  benutzte "BIT $EA"), da er drei Zyklen
  verbraucht.  Bei  einer geraden Anzahl
  Zyklen reicht eine  entsprechende  An-
  zahl  NOPs,  die  jeweils nur 2 Zyklen
  benötigen. So brauchen Sie zur  Verzö-
  gerung  von  z.B.  7 Zyklen 2 NOPs und
  einen BIT-Befehl. Bei 8 Zyklen sind  4
  NOPs ohne BIT-Befehl ausreichend, usw.
* Schätzen  Sie  in  etwa  ab,  wieviele
  Taktzyklen Sie  bei  einer  bestimmten
  Anzahl  Sprites  etwa benötigen, um zu
  verzögern (Formel:  29-AnzSprites*2.4)
  und testen Sie ob dieser Wert funktio-
  niert. Wenn nicht  ändern  Sie  in  1-
  Zyklus-Schritten nach oben oder unten.
* Natürlich wird der Prozessor immer nur
  eine gerade Anzahl Zyklen  angehalten.
  Der Richtwert von 2.4 Zyklen pro Spri-
  te ist lediglich zur Orientierung  ge-
  dacht.  Da  der Prozessor immer nur zu
  einem Taktsignal eine Operation begin-
  nen  kann, sollte die Verzögerung also
  immer zu finden sein.                 
* Zum Testen, wann Register $D016  geän-
  dert  wird,  sollten  Sie die Zugriffe
  auf dieses Register zu Testzwecken mit
  Zugriffen auf Register $D021 ersetzen.
  Sie erzielen dadurch eine Farbänderung
  des Hintergrunds, und  zwar  genau  zu
  dem  Zeitpunkt,  zu  dem normalerweise
  auch Register  $D016  verändert  wird.
  Sehen  Sie  einen  schwarzen Strich am
  Bildschirmrand, der länger als 8 Pixel
  ist, so ist der Zugriff zu früh. Sehen
  Sie gar keinen Strich, so  war  er  zu
  spät.  Wenn  Sie die richtige Einstel-
  lung gefunden haben können  Sie  $D021
  wieder  mit  $D016  ersetzen,  und der
  Sideborder-Effekt sollte funktioieren.
5) NOCH TRICKREICHERE PROGRAMMIERUNG    
Zum Abschluß möchte ich Ihnen  noch  ein
viertes Programmbeispiel erläutern, näm-
lich eine  Sideborderroutine,  die  acht
Sprites im Border darstellt. Das verwun-
derliche an ihr ist die  Funktionsweise.
Denn  obwohl  die Sideborderschleife ge-
nauso viele  Taktzyklen  verbraucht  wie
die  Version  für  sieben  Sprites, kann
dennoch ein Sprite mehr dargestellt wer-
den.  Ermöglicht  wird  dies  durch eine
trickreichere Variante des  Löschen  und
Setzen   des  Bildschirmbreite-Bits  aus
Register $D016. Wir initialisieren  die-
ses  Register vor der eigentlichen Side-
borderroutine nämlich mit dem Wert  $C8,
was  dem  Standardwert  dieses Registers
entspricht. In der eigentlichen  Routine
wird  dann Bit 3 gelöscht, indem wir das
ganze Register  mittels  "DEC $D016"  um
eins  herunterzählen  (auf  $C7  -  bin.
%11000111 - Bit 3 gelöscht). Ein  direkt
folgendes "INC $D016" setzt das Bit dann
wieder. Hier das Listing der Kernroutine
des Beispiels "SIDEBORDER.3":           
     ...                                
LOOP LDA $D012  ;FLD-Routine            
     ADC #$02                           
     AND #$07                           
     ORA #$18                           
     STA $D011                          
     DEC $D016  ;Bit 3 löschen und      
     INC $D016  ;gleich wieder setzen   
     NOP        ;Wie bei "SIDEBORDER.2" 
     NOP        ; stehen hier trotzdem  
     NOP        ; nur 6 NOPs!!!         
     NOP                                
     NOP                                
     NOP                                
     INX        ;Linienzähler+1         
     CPX $02    ;Mit Anz. zu öffnender  
     BCC LOOP   ;Zeilen vgl. u. weiter  
     ...                                
Der  Grund,  warum  die  Routine dennoch
funktioniert ist, daß der Prozessor  zur
Abarbeitung    des   DEC-Befehls   sechs
Taktzyklen   verbraucht,    wobei    der
Schreibzugriff  mit  dem dekrementierten
Wert durch den internen Aufbau  des  Be-
fehls  schon  früher  eintritt. Alles in
Allem ist das Funktionieren  dieser  Me-
thode  umso  wunderlicher, da die beiden
Befehle DEC und INC, ebenso wie die bei-
den  LDAs  und STAs vorher, 12 Taktyklen
benötigen. Wir tauchen hier  also  schon
in  die  tiefere Bereiche der Assembler-
programmierung ein, da es an  der  Funk-
tionsweise  des DEC-Befehls liegt, warum
das  Bit  rechtzeitig   gelöscht   wird.
Beachten  Sie daher bitte auch diese Va-
riation der Sideborder-Routine für eige-
ne  Zwecke  und  benutzen  Sie sie, wenn
normales Ausloten nicht ausreicht.      
6) EIN LETZTES BEISPIEL                 
Als zusätzlichen Leckerbissen haben wir 
Ihnen noch ein weiteres Beispielprogramm
auf  dieser  MD untergebracht: "SIDEBOR-
DER.4" basiert auf  dem  selben  Prinzip
wie  "SIDEBORDER.3", nur daß die Sprites
zusätzlich X-expandiert wurden und somit
eine lückenlose  Laufschrift  durch  den
Bildschirmrand  dargestellt werden kann.
Die Zeichendaten werden dabei direkt aus
dem Zeichensatz-ROM geholt, und mit Hil-
fe  des  ROL-Befehls  bitweise durch die
Sprites rotiert. Wie so  etwas  funktio-
niert  wissen  Sie bestimmt, weshalb ich
es hier nicht noch eimal extra  erläute-
re, zumal es ja eigentich nicht zu unse-
rem Thema gehört.                       
Öbrigens:  vielleicht  ist  Ihnen  schon
aufgefallen, daß beim Üffnen  des  Side-
borders  der rechte Bildschirmrand immer
eine Rasterzeile höher geöffnet ist, als
der Linke. Und das dieser umgekehrt eine
Rasterzeile länger offen  ist,  als  der
Rechte.  Das  liegt  daran,  daß das Ab-
schalten des Randes zwar am rechten Ende
einer Rasterzeile geschieht, sich jedoch
auf das linke Ende der folgenden Raster-
zeile auswirkt!                         
In der nächsten Folge dieses Kurses wer-
den wir Ihnen zeigen, wie man den Border
OHNE  wegdrücken  des  Bildschirms durch
FLD abschaltet  (besonders  haarige  Ti-
mingprobleme).  Desweiteren  bleiben wir
dann beim Thema  "Sprites",  und  wollen
uns  anschauen,  wie  man den VIC derart
austrickst, daß er mehr als acht  dieser
kleinen  Grafikstückchen  auf  den Bild-
schirm zaubert. Bis  dahin  sollten  Sie
sich  die  in der heutigen Folge bespro-
chenen Beispiele nocheinmal genauer  an-
schauen,  und ein wenig damit herumexpe-
rimentieren. Sie ahnen nicht  wie  viel-
seitig man mit dem abgeschalteten Border
arbeiten kann...                        
                                 (ub/ih)
                IRQ-KURS                
     "Die Hardware ausgetrickst..."     
                (Teil 8)                
----------------------------------------
Herzlich  willkommen zum achten Teil un-
seres  Raster-IRQ-Kurses.  Heute  wollen
wir  uns mit einem besonders interessan-
ten Rastertrick beschäftigen:  der  FLI-
Routine, die es uns ermöglicht, Grafiken
mit  mehr  als  2 Farben im Hires-Modus,
bzw. mehr als 4  Farben  im  Multicolor-
Modus, jeweils pro 8x8-Pixel-Block, dar-
zustellen. Durch die  dabei  entstehende
Farbvielfalt  können sehr eindrucksvolle
Bilder angezeigt  werden,  die  bei  ge-
schickten  Grafikern  schon  an die Qua-
lität von Amiga-Bildern reichen können! 
1) GRUNDLAGEN                           
Wollen   wir  uns  zunächst  einmal  an-
schauen, wie der VIC  vorgeht,  um  eine
Bitmap-Grafik auf den Bildschirm zu zau-
bern:                                   
Zunächst einmal muß der Grafikmodus ein-
geschaltet werden, wobei  die  Lage  der
Bitmap  im  Speicher  mitangegeben wird.
Nun zeigt der VIC also die Bits des  an-
gegegebenen Speicherbereichs als einzel-
ne Pixel auf dem Bildschirm an. Um  die-
sen  Pixeln  nun auch noch Farbe zu ver-
leihen, muß in zwei Fälle  unterschieden
werden:                                 
a) DER HIRES-MODUS                      
Hier bestimmt ein Farbcode im Video-RAM,
das im  Textmodus  zur  Darstellung  der
Zeichen  benutzt wird und sich normaler-
weise bei $0400  (dez.  1024)  befindet,
die  Farbe  der  Pixel  innerhalb  eines
8x8-Pixel-Blocks. So legt das erste Byte
des Video-RAMs (als Zeichen  ganz  links
oben), die Farbe für die 8x8, im Grafik-
modus quasi "über" ihm liegenden,  Pixel
fest.  Das zweite Byte ist für den näch-
sten 8x8-Pixel-Block zuständig,  und  so
weiter. Da der VIC insgesamt nur 16 Far-
ben kennt, sind dabei  jeweils  nur  die
unteren  vier Bits eines Video-RAM-Bytes
von Bedeutung. Die oberen vier sind  un-
benutzt und werden ignoriert.           
b) DER MULTICOLOR-MODUS                 
In diesem Modus werden zwei  nebeneinan-
der  liegende  Pixel  jeweils  zu  einem
Farbcode zusammengefasst. Sind beide  0,
so erscheint an ihrer Stelle die Hinter-
grundfarbe, sind beide auf  1,  so  wird
für  beide die Farbe aus einem äquvalen-
ten  Byte  des  Color-RAMs  geholt  (bei
Adresse  $D800 - dez. 55296), das norma-
lerweise zur  Farbgebung  der  einzelnen
Zeichen im Textmodus verwendet wird. Bei
der  Bitkombination "10" wird wieder das
Lownibble (die unteren  vier  Bits)  des
Video-RAMs  zur  Farbgebung ausgewertet.
Bei der Bitkombination "10" sind die, im
Hires-Modus  unbenutzten,  oberen   vier
Bits  des  Video-RAMs für die Pixelfarbe
zuständig. Sie sehen also, daß  das  Vi-
deo-RAM  im  Multicolormodus gleich zwei
Farbwerte für  Pixelkombinationen  fest-
legt.                                   
2) DAS FLI-PRINZIP                      
Soviel  zur  Darstellung  einer  Grafik.
Sicherlich  ist  Ihnen  bei  den  obigen
Ausführungen  das  häufige Auftreten des
Begriffs "Video-RAM",  bzw.  "Video-Map"
aufgefallen.  Und  genau  dieser Begriff
ist  der  Schlüssel  zu   unserer   FLI-
Routine. Wie Sie vielleicht wissen, kann
man  die  Lage  der Video-Map, innerhalb
des Adressierungsbereichs des  VICs  (im
Normalfall   von  $0000-$3FFF)  in  1KB-
Schritten verschieben. Hierfür ist Regi-
ster  24  des VICs (Adresse $D018 - dez.
53272) zuständig. Seine  oberen  4  Bits
geben  die  Lage  der Video-Map an, also
des RAM-Bereichs, der für  die  Darstel-
lung  der Textzeichen, bzw. im Grafikmo-
dus der Farbzeichen, zuständig ist.  Die
unteren  Bits von 0-3 bestimmen die Lage
des Zeichengenerators,  also  des  Spei-
cherbereichs,  in  dem die Daten für den
Zeichensatz ausgelesen werden. Dies soll
uns hier jedoch nicht interessieren  und
sei nur nebenbei angemerkt.             
Konzentrieren  wir uns auf die Bits 4-7:
Sie bestimmen die  Lage  des  Video-RAMs
innerhalb  des  16KB-Bereichs  des VICs.
Die im  Folgenden  angegebenen  Adressen
verstehen sich also als Offsets, die auf
die  Startadresse des 16KB-Bereichs auf-
addiert werden müssen:                  
Wert Bits  Bereich (Hex)  Bereich (Dez) 
--------------------------------------- 
 0:  0000    $0000-$03FF       0- 1023  
 1:  0001    $0400-$07FF    1024- 2047  
 2:  0010    $0800-$0BFF    2048- 3071  
 3:  0011    $0CFF-$0FFF    3072- 4095  
 4:  0100    $1000-$13FF    4096- 5119  
 5:  0101    $1400-$17FF    5120- 6143  
 6:  0110    $1800-$1BFF    6144- 7167  
 7:  0111    $1CFF-$1FFF    7168- 8191  
 8:  1000    $2000-$23FF    8192- 9215  
 9:  1001    $2400-$27FF    9216-10239  
10:  1010    $2800-$2BFF   10240-11263  
11:  1011    $2CFF-$2FFF   11264-12287  
12:  1100    $3000-$33FF   12288-13311  
13:  1101    $3400-$37FF   13312-14335  
14:  1110    $3800-$3BFF   14336-15359  
15:  1111    $3CFF-$3FFF   15360-16383  
Obwohl die Video-Map nur 1000 Bytes lang
ist, habe ich  hier  dennoch  1024-Byte-
Bereiche angegeben. Das hat nämlich auch
eine  Bedeutung  für  den nächsten Kurs-
teil.                                   
Desweiteren wollen wir noch schnell  kl-
ären,  wie man den 16KB-Bereich des VICs
verschiebt, da die FLI-Routine eine Men-
ge  Video-Speicher benötigt (nämlich die
vollen 16KB), sollten  wir  den  VIC  in
einem  Bereich  arbeiten  lassen, in dem
wir  nicht  auf  Zeropage-Adressen   und
Sprungvektoren   (die   im  ersten  VIC-
Bereich - von $0000-$3FFF -  eine  Menge
Platz  wegnehmen)  Rücksicht  nehmen  zu
müssen.                                 
Der  Adressbereich  des VIC wird nun mit
den  untersten  zwei  Bits  der  Adresse
$DD00  (dez.  56576) angegeben (richtig:
dies ist ein CIA-B Register, das dem VIC
seinen Adressbereich vorschreibt).  Hier
eine  Auflistung der möglichen Bitkombi-
nationen und der Adressbereiche die  sie
aktivieren:                             
 3:   11   $0000-$3FFF        0-16383   
 2:   10   $4000-$7FFF    16384-32767   
 1:   01   $8000-$BFFF    32768-49151   
 0:   00   $C000-$FFFF    49152-65535   
In unserem Programmbeispielen werden wir
den  VIC-Bereich nach $4000 verschieben,
womit der Wert 2 in  die  Adresse  $DD00
geschrieben werden muß. Die tatsächliche
Adresse  der  Video-Map  ist  dann immer
$4000 plus der oben angegebenen Offseta-
dresse.                                 
Nachdem  wir  nun  also  die  Grundlagen
gelkärt hätten, ist  die  Erklärung  des
Prinzips  der FLI-Routine höchst simpel:
Zum Einen wissen wir, daß die  Video-Map
zur   Farbgebung  der  Grafik  verwendet
wird. Zum Anderen haben wir gesehen, wie
die Startadresse, aus der der  VIC  sich
die   Video-Map-Daten  holt,  verschoben
werden kann. Was liegt  nun  also  näher
als  zwei und zwei zusammenzuzählen, und
eine  Raster-IRQ-Routine  zu  schreiben,
die  in  JEDER  Rasterzeile  eine andere
Video-Map aktiviert?  Die  Folge  dieser
Operation wäre dann nämlich die Möglich-
keit, einem 8x8-Pixel Block  der  Grafik
in  jeder Zeile eine neue Farbe zuzutei-
len (im Multicolormodus sogar 2), so daß
wir die 2-, bzw. 4-Farbeinschränkung auf
einen Bereich von 1x8 Pixeln reduzieren!
3) DIE UMSETZUNG                        
Vom  Prinzip  her  klingt  das natürlich
sehr einfach,  jedoch  stellt  sich  uns
noch ein kleines Problem in den Weg:    
Wie wir bei der  Beschreibung  des  FLD-
Effektes schon gelernt hatten, liest der
VIC ja nur in jeder Charakterzeile (also
jede  achte  Rasterzeile), die 40 Bytes,
die er in den folgenden acht  Rasterzei-
len  darzustellen  hat.  Somit würde ein
einfaches Umschalten auf eine  neue  Vi-
deo-Map  nichts  bewirken, da der VIC ja
immer noch mit den 40 Zeichen, die er zu
Beginn der Charakterzeile  gelesen  hat,
die  folgenden Zeilen darstellen würde -
und das selbst bei umgeschalteter Video-
Map. Also müssen wir ihn auch  hier  mit
einem  kleinen Raster-Trick "veräppeln".
Man kann den VIC nämlich auch dazu zwin-
gen,  eine  Charakterzeile  NOCHMALS  zu
lesen. Der anzuwendende  Trick  ist  uns
dabei  gar  nicht mal so unbekannt, denn
er funktioniert  ähnlich  wie  der  FLD-
Effekt.  Bei  diesem schoben wir den An-
fang der nächsten  Charakterzeile  durch
Hochsetzen der vertikalen Bildschirmver-
schiebung vor dem Rasterstrahl  her,  so
daß  die Charakterzeile für den VIC erst
begann, als wir mit unserer Manipulation
aufhörten. Nun arbeiten wir  im  Prinzip
genauso,  nur  daß wir die Chrakterzeile
nicht VOR dem Rasterstrahl  wegschieben,
sondern  MIT  ihm.  Verschieben  wir den
Vertikal-Offset nämlich so, daß er immer
mit dem Anfang einer Charakterzeile  zu-
sammenfällt,  so meint der VIC, er müsse
jetzt die  neuen  Charakterdaten  lesen,
selbst  wenn  er  das in der Rasterzeile
zuvor auch schon getan hat. Schalten wir
nun gleichzeitig auch noch auf eine  an-
dere  Video-Map um, so liest der VIC, so
als wäre alles in Ordnung, die  40  Cha-
rakter der neuen Map!                   
Bevor ich mich nun aber in theoretischen
Erklärungen  verliere,  möchte Ich Ihnen
ein Programmbeispiel zeigen,  das  alles
einfacher zu erklären vermag. Sie finden
es  auf  dieser  MD unter dem Namen "GO-
FLI-CODE", und müssen es mit der  Endung
",8,1"  laden.  Gleichzeitig  (und eben-
falls mit ",8,1") sollte auch  noch  die
lange Grafik "GO-FLIPIC" geladen werden,
damit Sie beim Start der Routine mittels
"SYS4096", auch etwas auf dem Bildschirm
sehen. Es handelt sich dabei um das Logo
unseres Schwestermagazins "Game On", und
zwar  in schillernd bunten Farbabstufun-
gen!                                    
Doch kommen wir nun zum Sourcecode  die-
ser  FLI-Routine.  Zunächst einmal werde
ich Ihnen die Initialisierung hier  auf-
listen,  die  größenteils  identisch mit
den zuvorigen Init-Routinen ist,  jedoch
auch  einige FLI-spezifische Einstellun-
gen vornimmt:                           
Init:sei         ;IRQs sperren          
     lda $7F     ;CIA-IRQs und NMIs     
     sta $dc0d   ; sperren              
     sta $dd0d                          
     bit $dc0d   ;ggf. vorhandene IRQ-  
     bit $dd0d   ;oder NMI-Anfr. löschen
     lda #$0b    ;Bildschirm            
     sta $d011   ; abschalten           
     ldy #$00    ;Daten für Color-Map   
     lda $3c00,y ; von $3C00            
     sta $d800,y ; nach $D800           
     lda $3d00,y ; kopieren             
     sta $d900,y                        
     lda $3e00,y                        
     sta $da00,y                        
     lda $3f00,y                        
     sta $db00,y                        
     iny                                
     bne color                          
     lda #$00    ;Hardvektor füt IRQ bei
     sta $fffe   ; $FFFE/$FFFF auf      
     lda #$11    ; eigene Routine bei   
     sta $ffff   ; $1100 setzen         
     ldx #$38    ;Basisw. Vert.Versch.  
     stx $02     ; in Zeropage ablegen  
     inx         ;Pro Zeile +1 und      
     stx $03     ; ebenfalls in ZP abl. 
     inx         ;usw. bis X=$3F        
     stx $04                            
     inx                                
     stx $05                            
     inx                                
     stx $06                            
     inx                                
     stx $07                            
     inx                                
     stx $08                            
     inx                                
     stx $09                            
     lda #$5C    ;Rasterz. $d012 ist    
     sta $d012   ; IRQ-Auslöser         
     lda #$81    ;Raster als IRQ-Quelle 
     sta $d01a   ; einschalten          
     dec $d019   ;ggf. VIC-IRQ freigeben
     lda #$35    ;Basic- u. Betr.Sys.-  
     sta $01     : ROM abschalten       
     cli         ;IRQs freigeben        
     lda #$7f    ;Tastaturport init.    
     sta $dc00                          
spc: lda $dc01   ;Auf 'SPACE'-Taste     
     cmp #$ef    ; warten               
     bne spc                            
     lda #$37    ;ROMs wieder           
     sta $01     ; einschalten          
     jmp $fce2   ;und RESET auslösen    
Hier schalten wir zunächst alle CIA-IRQs
ab, tragen die Startadresse unserer FLI-
Routine bei $1100 in den Hard-IRQ-Vektor
$FFFE/$FFFF  ein,   schalten   das   Be-
triebssystem  ab,  damit der Vektor auch
angesprungen wird, und erlauben dem  VIC
in Rasterzeile $5C einen Raster-IRQ aus-
zulösen. Zudem wird eine Tabelle in  den
Zeropageadressen von $02 bis $09 initia-
lisiert, die in Folge die Werte von  $38
bis  $3F  enthält. Diese müssen wir spä-
ter, je nach  Rasterzeile,  in  Register
$D011  schreiben,  damit  der Anfang der
Charakterzeile immer  in  der  aktuellen
Rasterzeile ist. Durch den Basiswert $38
legen  wir  zunächst  nur  fest, daß der
Bildschirm eingeschaltet ist,  und  sich
der   VIC  im  Grafikmodus  und  in  25-
Charakterzeilen-Darstellung    befindet.
Durch  das  jeweilige  aufaddieren von 1
wird dann legiglich der  vertikale  Ver-
schiebeoffset  um 1 erhöht, was dann im-
mer dem jeweiligen Wert für  den  Beginn
einer  Charakterzeile entspricht (Start-
wert $38 -> Verschiebung=0 für  tatsäch-
liche  Charakterzeile; nächster Wert=$39
-> Verschiebung=1 Zeile, wehalb die  er-
ste Rasterzeile hinter der normalen Cha-
rakterzeile vom VIC wieder  als  Charak-
terzeile  aufgefasst  wird;  usw.).  Die
Werte werden übrigens  deshalb  in  eine
Tabelle innerhalb der Zeropage eingetra-
gen,  damit  das  Timing  später  besser
klappt.                                 
Desweiteren  wird  hier  der  Inhalt der
Color-Map initialisiert.  Hierzu  sollte
ich  vielleicht  noch erläutern, wie das
68 Blocks  lange  FLIPIC-File  aufgebaut
ist:  zunächst einmal wird es an Adresse
$3C00 geladen, wo sich  auch  die  $0400
Bytes  für  die  Color-Map  befinden. Ab
Adresse $4000 beginnen nun die acht  Vi-
deo-Maps, die später von der FLI-Routine
durchgeschaltet  werden.  Auch  sie sind
jeweils $0400 Bytes lang (was einer  Ge-
samtlänge    von   $0400*8=$2000   Bytes
gleichkommt). Hiernach, im  Bereich  von
$6000-$8000 befindet sich nun die darzu-
stellende Grafik-Bitmap. Was  die  Init-
Routine  nun  macht,  ist  lediglich die
Color-Map-Daten zwischen $3C00 und $4000
nach  $D800  (Basisadresse  des   Color-
RAMs),  zu kopieren. Die Video-RAM-Daten
liegen in den Speicherbereichen, die mit
den Werten von 0-7 für die Highbits  von
Register  $D018, angewählt werden können
(was gleichzeitig auch der Anordnung  in
Rasterzeilen entspricht - Video-RAM Nr.0
($4000) für die 0. Rasterzeile innerhalb
Charakterzeile;  Video-RAM  Nr.1 ($4400)
für die  1.  Rasterzeile  innerhalb  der
Charakterzeile; usw).                   
Kommen  wir  nun  zur   Interruptroutine
selbst. Sie beginnt wie all unsere zeit-
kritischen Raster-IRQs zunächst mit  der
Glättungsroutine,   wobei  ein  weiterer
Interrupt festgelegt und ausgelöst wird,
bevor die eigentliche FLI-Routine folgt.
Sie beginnt wieder ab dem Label "Onecyc-
le",  ab  dem  der  IRQ "geglättet" ist.
Hier nun der eigentliche Kern der Routi-
ne. Da der Interrupt bei Rasterzeile $5C
ausgelöst  wurde,  und die Glättung zwei
Rasterzeilen verbrauchte,  befinden  wir
uns  nun  also  in  Zeile $5E, womit wir
also zwei Rasterzeilen  vor  Beginn  der
achten Charakterzeile stehen:           
   dec $d019   ;VIC-ICR löschen         
   lda #$5C    ;Neue IRQ-Rasterzeile    
   sta $d012   ; festlegen ($5C)        
   lda #$00    ;IRQ-Vektor wieder auf   
   sta $fffe   ; die erste IRQ-Routine  
   lda #$11    ; verbiegen (für den     
   sta $ffff   ; nächsten Aufruf)       
   lda #$00    ;Bildschirmrahmen- und   
   sta $d020   ; Hintergrundfarbe auf   
   sta $d021   ; 'schwarz' setzen       
   NOP         ;Hier folgen nun 40 NOPs 
   ...         ; um bis zum richtigen   
   NOP         ; Zeitpunkt zu verzögern 
   ldy #$08    ;Rasterzeilenzähler init.
   jsr fliline ;8 FLI-Rasterzeilen      
   jsr fliline ; für 12 Charakterzeilen 
   jsr fliline ; durchführen (gesamte   
   jsr fliline ; Größe des FLI-Bereichs:
   jsr fliline ; 8x12=96 Rasterzeilen)  
   jsr fliline                          
   jsr fliline                          
   jsr fliline                          
   jsr fliline                          
   jsr fliline                          
   jsr fliline                          
   jsr fliline                          
   lda #$02    ;Bildschirmrahmen- und   
   sta $d020   ; Hintergrundfarbe auf   
   sta $d021   ; 'rot' setzen           
   lda #$38    ;Vertikalverschiebung    
   sta $d011   ; zurücksetzen (=0)      
   lda #$88    ;VideoMap 8              
   sta $d018   ; einschalten            
   pla         ;Prozessorregister vom   
   tay         ; Stapel zurückholen     
   pla         ; und IRQ beenden.       
   tax                                  
   pla                                  
   rti                                  
Hier werden erst einmal die Vorbereitun-
gen für den nächsten Interrupt getroffen
(also für den nächsten Bildaufbau). Dies
ist  das Rücksetzen des IRQ-Vektors, auf
die Glättung-IRQ-Routine, das neue fest-
legen   der  Rasterzeile  $5C  als  IRQ-
Auslöser, sowie  das  Löschen  des  VIC-
ICR-Registers  durch Zugriff auf Adresse
$D019. Nun wird das Y-Register  mit  dem
Wert  8  initialisiert,  der  in der nun
folgenden  "FLILINE"-Routine   gebraucht
wird.  Selbige  führt  den  eigentlichen
FLI-Effekt aus,  wobei  Sie  immer  acht
Rasterzeilen  lang,  seit  Beginn  einer
Rasterzeile, den Effekt  erzeugt.  Durch
den  12-maligen  Aufruf  lassen  wir ihn
also ab Rasterzeile $5E für  96  Raster-
zeilen  aktiv sein. Hier nun der Source-
code zur FLILINE-Routine:               
   (Anm. d. Red.: Bitte wählen Sie nun  
       den zweiten Teil des Kurses      
            aus dem Textmenu)           
fliline sty $d018 ;Video-Map0 einschalt.
        ldx $02   ;Vert.Versch.=0 ($38) 
        stx $d011 ; schreiben           
        lda #$18  ;VideoMap1-Wert laden 
        ldx $03   ;Vert.Versch.=1 ($39) 
        nop       ;Verzögern...         
        nop                             
        nop                             
        nop                             
        sta $d018 ;Werte zeitgenau in   
        nop       ; $d018 und $d011     
        stx $d011 ; eintragen           
        lda #$28  ;VideoMap2-Wert laden 
        ldx $04   ;Ver.Versch.=1 ($3A)  
        nop       ;Verzögern...         
        nop                             
        nop                             
        nop                             
        sta $d018 ;Werte zeitgenau in   
        nop       ; $d018 und $d011     
        stx $d011 ; eintragen           
        lda #$38  ;Dito f. die folgenden
        ldx $05   ; 6 Rasterzeilen      
        nop                             
        nop                             
        nop                             
        nop                             
        sta $d018                       
        nop                             
        stx $d011                       
        lda #$48                        
        ldx $06                         
        nop                             
        nop                             
        nop                             
        nop                             
        sta $d018                       
        nop                             
        stx $d011                       
        lda #$58                        
        ldx $07                         
        nop                             
        nop                             
        nop                             
        nop                             
        sta $d018                       
        nop                             
        stx $d011                       
        lda #$68                        
        ldx $08                         
        nop                             
        nop                             
        nop                             
        nop                             
        sta $d018                       
        nop                             
        stx $d011                       
        lda #$78                        
        ldx $09                         
        nop                             
        nop                             
        nop                             
        nop                             
        sta $d018                       
        ldy #$08                        
        stx $d011                       
        rts                             
Dieser etwas merkwürdige Aufbau der Rou-
tine  ist mal wieder absolut unerläßlich
für die richtige Funktionsweise des FLI-
Effektes.  Wie  immer  muß  hier  extrem
zeitgenau gearbeitet werden,  damit  die
einzelnen  Befehle  zum  richtigen Zeit-
punkt ausgeführt werden. Durch die  NOPs
innerhalb  der  eigentlichen IRQ-Routine
wird die FLILINE-Routine zum ersten  Mal
genau  dann  angesprungen, wenn sich der
Rasterstrahl kurz vor dem  Beginn  einer
neuen Charakterzeile befindet. Durch den
"STY $D018"-Befehl am Anfang der Routine
wird nun gleich auf Video-Map 0 geschal-
tet, deren Ihnhalt  für  die  Farbgebung
der  Grafikpixel  in  dieser Rasterzeile
zuständig ist. Gleichzeitig  setzen  wir
den   vertikalen   Verschiebeoffset  auf
Null, wobei mit dem Wert  von  $38,  der
sich  in  der Zeropageadresse $02 befin-
det, gleichzeitig die  Grafikdarstellung
eingeschaltet  wird. Genau nachdem diese
beiden Werte geschrieben wurden  beginnt
nun  die Charakterzeile, in der der VIC,
wie wir ja wissen,  den  Prozessor,  und
damit  unser Programm, für 42 Taktzyklen
anhält.  Hiernach  haben  wir  noch   21
Taktzyklen Zeit, die Werte für die näch-
ste Rasterzeile einzustellen, wobei auch
dies exakt vor Beginn derselben  gesche-
hen  muß.  Hierzu wird zunächst der Akku
mit dem Wert $18 geladen (Wert für Regi-
ster  $D018), wobei die oberen vier Bits
die Lage der  Video-Map  bestimmen.  Sie
enthalten  den Wert 1 und bezeichnen da-
mit Video-Map1 bei  Adresse  $4400.  Die
unteren vier Bits bestimmen die Lage des
Zeichensatzes   und  könnten  eigentlich
jeden beliebigen Wert enthalten. Da  Sie
normalerweise  den Wert 8 enthalten, be-
nutzen wir ihn ebenfalls. Der LDA-Befehl
verbraucht nun 2 Taktzyklen.  Als  Näch-
stes  wird  das  X-Register mit dem Wert
für Register  $D011  inititalisiert.  Er
ist   diesmal  $39,  was  der  Vertikal-
Verschiebung  des  Bildschirms  um  eine
Rasterzeile entspricht. Hierbei wird der
Wert  aus Speicherzelle $03 der Zeropage
ausgelesen. Vielleicht wird Ihnen  jetzt
auch  klar,  warum  wir die Wertetabelle
überhaupt, und dann ausgerechnet in  der
Zeropage  angelegt  haben: Durch die ZP-
Adressierung verbraucht  der  LDX-Befehl
nämlich  3  Taktzyklen, anstelle von nur
zweien  (bei  direktem  Laden  mit  "LDX
#$39"),  was  für unser Timing besonders
wichtig  ist!  Es  folgen  nun  4   NOP-
Befehle,  die  einfach  nur 8 Taktzyklen
verbrauchen sollen, damit wir zum  rich-
tigen   Zeitpunkt  in  die  VIC-Register
schreiben.  Was  dann  auch  tatsächlich
geschieht,  wobei  wir  mit den Befehlen
"STA", "STX" und "NOP" nochmals 10 Takte
"verbraten" und uns wieder genau am  Be-
ginn  der nächsten Rasterzeile befinden.
Durch die im letzten Moment vorgegaukel-
te Vertikal-Verschiebung um eine Raster-
zeile, meint der VIC nun,  daß  er  sich
wieder  am  Anfang  einer Charakterzeile
befindet, weswegen er  sie  auch  prompt
einliest.  Sinnigerweise  jedoch aus der
neu eingeschalteten  Video-Map  Nr.1 !!!
Dies  setzt  sich  so fort, bis auch die
letzte der acht Rasterzeilen,  nach  Be-
ginn  der  eigentlichen  Charakterzeile,
abgearbeitet wurde. Nun wird zum  Haupt-
IRQ  zurückverzweigt,  wo  "FLILINE" so-
gleich zur Bearbeitung der nächsten Cha-
rakterzeile aufgerufen wird.            
Wenn Sie übrigens einmal die  Summe  der
verbrauchten  Taktzyklen pro Rasterzeile
berechnen, so wird Ihnen auffallen,  daß
wir  23  (=2+3+10+8),  anstelle  von  21
Taktzyklen verbraucht haben, so daß  un-
ser   Programm   also  2  Zyklen  länger
dauert, als es eigentlich  sollte.  Dies
liegt an einem kleinen "Nebeneffekt" von
FLI.  Dadurch, daß wir den VIC austrick-
sen,  scheint  er  etwas  "verwirrt"  zu
sein,  weswegen  er  nicht gleich die 40
Zeichen der  neuen  Video-Map  einliest,
sondern  24 Pixel lang erstmal gar nicht
weiß, was  er  machen  soll  (vermutlich
liest er sie zu spät, weswegen er nichts
darstellen  kann). Aus diesem Grund kön-
nen auch die ersten 24 Pixel einer  FLI-
Grafik  nicht  dargestellt  werden. Wenn
Sie sich das Beispielbild einmal genauer
ansehen, so werden Sie  merken,  daß  es
nur  296  Pixel  breit  ist (3 Charakter
fehlen auf der linken Seite).  Erst  da-
nach  kann  der VIC die Grafik mit akti-
viertem FLI-Effekt darstellen. In den  3
Charaktern  davor  ist  dann  wieder das
Bitmuster der letzten Speicherzelle sei-
nes   Adressierungsbereiches   zu  sehen
(normalerweise $3FFF - im Beispiel  aber
durch  die  Bereichsverschiebung $7FFF).
Gleichzeitig scheint der  Prozessor  da-
durch  aber  wieder 2 Zyklen mehr zu ha-
ben, was die einzige Erklärung  für  die
oben  aufgezeigte  Diskkrepanz sein kann
(Sie sehen: selbst wenn man  einen  sol-
chen Effekt programmieren kann - bleiben
manche   Verhaltensweisen  der  Hardware
selbst dem Programmierer ein Rätsel).   
4) WEITERE PROGRAMMBEISPIELE            
Das war es dann wieder einmal für diesen
Monat. Ich hoffe, daß Sie meinen, manch-
mal  etwas  komplizierten,  Ausführungen
folgen konnten. Mit  der  Erfahrung  aus
den letzten Kursteilen sollte das jedoch
kein  Problem für Sie gewesen sein. Wenn
Sie sich ein paar  FLI-Bilder  anschauen
möchten,  so  werfen Sie einen Blick auf
diese Ausgabe der  MD.  Außer  dem  oben
schon  angesprochenen "GO-FLIPIC", haben
wir Ihnen noch zwei  weitere  FLI-Bilder
mit  auf  die  Diskette  kopiert. "FULL-
FLIPIC",  ist  ein  Multicolor-FLI-Bild,
daß  über  den gesamten Bildschirm geht.
Viel mehr als es anzuschauen können  Sie
nicht  damit machen, da sich der Prozes-
sor während des annähernd gesamten  Bil-
daufbaus  im FLI-Interrupt befindet, und
dadurch wenig Rechenzeit für andere  Ef-
fekte  übrigbleibt.  Das dritte Beispiel
ist ein  FLI-Bild  in  HIRES-Darstellung
("HIRES-FLIPIC").  Hier  sehen  Sie  wie
Eindrucksvoll der FLI-Effekt sein  kann,
da durch die hohe Auflösung noch bessere
Farbeffekte erzielt werden. Zu jedem der
drei  Bilder müssen Sie das gleichnamige
Programm laden (erkennbar an der  Endung
"-CODE"),  das  immer  mit "SYS4096" ge-
startet   und   durch   einen   'SPACE'-
Tastendruck  beendet  wird.  Das Prinzip
der einzelnen Routinen  ist  immer  ähn-
lich,  nur  daß bei "FULL" noch mehr Ra-
sterzeilen in  FLI-Darstellung  erschei-
nen,  und bei "HIRES" zusätzlich der Hi-
res-Modus  zur   Darstellung   verwendet
wird. Am Besten Sie disassemblieren sich
die drei Programme mit Hilfe eines Spei-
chermonitors und  manipulieren  sie  ein
wenig, um die Vielfalt und Funktionswei-
se von FLI-Effekten besser zu erlernen. 
Übrigens:  Der  FLI-Effekt  funktioniert
auch im normalen Textmodus. Hierbei wer-
den dann immer nur die oberen acht Pixel
eines  Zeichens in allen acht Rasterzei-
len wiederholt. Man könnte  damit  einen
recht  eindrucksvollen Textein- und aus-
blend-Effekt programmieren, bei dem  die
einzelnen  Zeichen  quasi  auf den Bild-
schirm "fließen"  (oder  von  ihm  "weg-
schmelzen").  Dies  als Anregung für ein
eigenes FLI-Projekt...                  
                                 (ih/ub)
                IRQ-KURS                
     "Die Hardware ausgetrickst..."     
                (Teil 9)                
----------------------------------------
Herzlich  Willkommen  zum  neunten  Teil
unseres  Raster-IRQ-Kurses.  In   dieser
Ausgabe  soll es um die trickreiche Pro-
grammierung von Sprites gehen,  die  wir
bisher  ja  nur  am  Rande angeschnitten
hatten. Durch Raster-IRQs ist es nämlich
möglich, mehr als acht Sprites  auf  den
Bildschirm zu zaubern! Wir wollen hierzu
eine    sogenannte   Sprite-Multiplexer-
Routine kennenlernen, mit der wir bis zu
16 Sprites (fast) frei  über  den  Bild-
schirm bewegen können!                  
1) DAS FUNKTIONSPRINZIP                 
Zunächst wollen wir klären, wie  man  im
Allgemeinen   die   Anzahl  der  Sprites
erhöht. Das Prinzip ist  höchst  simpel:
dadurch nämlich, daß man einen Rasterin-
terrupt z.B. in der Mitte des sichtbaren
Bildschirms auslöst,  hat  man  die  Mö-
glichkeit  die  Sprite-Register  des VIC
neu zu beschreiben, um z.B. neue Sprite-
positionen  und Spritepointer zu setzen.
Bevor der Rasterstrahl  nun  wieder  die
obere  Bildschirmhälfte erreicht, können
dann wieder die Daten  der  ersten  acht
Sprites  in  den VIC geschrieben werden.
Auf diese Weise kann man in der  oberen,
sowie in der unteren Bildschirmhälfte je
acht  Sprites  darstellen.  Wie  einfach
dieses Prinzip funktioniert soll folgen-
des  Programmbeispiel verdeutlichen, das
Sie auf dieser MD auch unter  dem  Namen
"16SPRITES"  finden,  und  wie immer mit
",8,1" laden  und  durch  ein  "SYS4096"
starten.  Nach  dem  Start werden Sie 16
(!) Sprites auf  dem  Bildschirm  sehen,
acht in der oberen und acht in der unte-
ren Bildschirmhäfte,  jeweils  in  einer
Reihe.  Kommen  wir  zunächst  zur Init-
Routine unsres kleinen Beispiels,  deren
Funktionsprinzip  uns  mittlerweile  be-
kannt sein sollte:                      
Init:sei         ;IRQs sperren          
     lda #6      ;Farbe auf 'blau'      
     sta $d020                          
     sta $d021                          
     lda #$7f    ;Alle Interruptquellen 
     sta $dc0d   ; von IRQ- und NMI-CIA 
     sta $dd0d   ; sperren ggf. aufge-  
     bit $dc0d   ; tretene IRQs frei-   
     bit $dd0d   ; geben                
     lda #$94    ;Rasterzeile $94 als   
     sta $d012   ; IRQ-Auslöser         
     lda $d011   ; festlegen            
     and #$7f                           
     sta $d011                          
     lda #$01    ;Rasterstrahl ist      
     sta $d01a   ; Interruptquelle      
     lda #<irq1  ;Vektor auf erste IRQ- 
     sta $fffe   ; Routine verbiegen    
     lda #>irq1                         
     sta $ffff                          
     ldy #19     ;Pseudo-Sprite-Register
lo1: lda vic1,y  ; in Zeropage von      
     sta $80,y   ; von $80 bis $C0      
     lda vic2,y  ; kopieren             
     sta $a0,y                          
     dey                                
     bpl lo1                            
     ldy #62     ;Spriteblock Nr. 13    
     lda #$ff    ; mit $FF auffüllen    
lo2: sta 832,y                          
     dey                                
     bpl lo2                            
     ldy #7      ;Spritepointer aller   
lo3: lda #13     ; Sprites auf Block 13,
     sta 2040,y  ; sowie Farbe aller    
     lda #1      ; Sprites auf 'weiß'   
     sta $d027,y ; setzen               
     dey                                
     bpl lo3                            
     lda #$35    ;ROM abschalten        
     sta $01                            
     cli         ;IRQs freigeben        
lo4: jsr $1200   ;Bewegunsroutine aufr. 
     lda $dc01   ;SPACE-Taste abfragen  
     cmp #$ef                           
     bne lo4                            
     lda #$37    ;Wenn SPACE, dann ROM  
     sta $01     ; wieder einschalten   
     jmp $fce2   ; und RESET auslösen.  
Hier  schalten  wir nun also wie gewohnt
alle Interruptquellen der  CIA  ab,  und
aktivieren  den Raster-IRQ, wobei dieser
das erste Mal in Rasterzeile $94 auftre-
ten  soll,  was  in  etwa  die Mitte des
sichtbaren Bildbereichs ist.  Dort  soll
dann  die Routine "IRQ1" aufgerufen wer-
den,  deren  Adresse  in  den  Hard-IRQ-
Vektor bei $FFFE/$FFFF geschrieben wird.
Damit  der  Prozessor beim Auftreten des
IRQs  auch  tatsächlich  unsere  Routine
anspringt, wird zuvor noch das ROM abge-
schaltet,  und  anschließend   in   eine
Schleife  gesprungen, die auf die SPACE-
Taste wartet, und in dem Fall einen  Re-
set  auslöst.  Wichtig sind nun noch die
drei Kopierschleifen innerhalb der  Ini-
tialisierung.  Die  erste  davon ("LO1")
kopiert nun zunächst  eine  Tabelle  mit
Sprite-Register-Werten,  die am Ende des
Programms stehen,  in  die  Zeropage  ab
Adresse  $80. Was es damit auf sich hat,
sehen wir später. Die zweite und  dritte
Schleife  füllen  dann  noch den Sprite-
block 13 mit  gesetzten  Pixeln,  setzen
die Spritepointer aller Sprites auf die-
sen Block,  sowie  die  Farbe  Weiß  als
Spritefarbe.                            
Sehen wir nun, was die  Interruptroutine
"IRQ1" macht:                           
IRQ1:pha         ;Prozessorregister     
     txa         ;retten                
     pha                                
     tya                                
     pha                                
     lda #0      ;Farbe auf 'schwarz'   
     sta $d020                          
     sta $d021                          
     lda #$fc    ;Rasterzeile $FC soll  
     sta $d012   ; nächster IRQ-Auslöser
     lda #<irq2  ; sein, wobei Routine  
     sta $fffe   ; "IRQ2" angesprungen  
     lda #>irq2  ; werden soll          
     sta $ffff                          
     dec $d019   ;VIC-ICR freigeben     
     lda $a0     ;X-Pos. Sprite 0       
     sta $d000   ; setzen               
     lda $a1     ;Y-Pos. Sprite 0       
     sta $d001   ; setzen               
     lda $a2     ;Ebenfalls für Sprite 1
     sta $d002                          
     lda $a3                            
     sta $d003                          
     lda $a4      ;Sprite 2             
     sta $d004                          
     lda $a5                            
     sta $d005                          
     lda $a6      ;Sprite 3             
     sta $d006                          
     lda $a7                            
     sta $d007                          
     lda $a8      ;Sprite 4             
     sta $d008                          
     lda $a9                            
     sta $d009                          
     lda $aa      ;Sprite 5             
     sta $d00a                          
     lda $ab                            
     sta $d00b                          
     lda $ac      ;Sprite 6             
     sta $d00c                          
     lda $ad                            
     sta $d00d                          
     lda $ae      ;Sprite 7             
     sta $d00e                          
     lda $af                            
     sta $d00f                          
     lda $b0      ;Hi-Bits der X-Pos.   
     sta $d010    ; aller Sprites setzen
     lda $b1      ;Sprite-Enable setzen 
     sta $d015    ; (welche sind an/aus)
     lda $b2      ;X-Expansion setzen   
     sta $d017                          
     lda $b3      ;Y-Expansion setzen   
     sta $d01d                          
     lda #6       ;Farbe wieder 'blau'  
     sta $d020                          
     sta $d021                          
     pla          ;Prozessor-Regs.      
     tya          ; zurückholen         
     pla                                
     txa                                
     pla                                
     rti          ;Und IRQ beenden...   
Wie Sie sehen, tut die  Routine  eigent-
lich  nichts  anderes, als Werte aus den
Zeropageadressen von $A0 bis $B3 in ein-
zelne  VIC-Register  zu  kopieren. Damit
geben wir dem VIC wir ab der Rasterposi-
tion  $94 also einfach neue Spritewerte.
Gleiches  macht  nun  auch  die  Routine
"IRQ2", die an Rasterzeile $FC ausgelöst
wird,  nur daß sie die Werte aus den Ze-
ropageadressen von $80 bis  $93  in  den
VIC-Kopiert.  In  den  beiden  genannten
Zeropage-Bereichen haben wir also  quasi
eine   Kopie   der  wichtigsten  Sprite-
Register für jeweils zwei  Bildschirmbe-
reiche  untergebracht, deren Inhalte je-
weils an Rasterzeile $94 und $FC in  den
VIC  übertragen  werden.  Verändern  wir
diese Werte nun innerhalb der  Zeropage,
so  können  wir  jedes der 16 sichtbaren
Sprites einzeln bewegen, ein-  oder  au-
schalten,  sowie X-, bzw. Y-Expandieren.
Wir haben also quasi  zwei  "virtuelle",
oder  "softwaremäßige"  Sprite-VICs  er-
schaffen, deren  Register  wie  die  des
normalen VICs beschrieben werden können.
Dies  können Sie übrigens mit einer Rou-
tine ab Adresse $1200 machen.  Sie  wird
im  Hauprogramm (sh. INIT-Listing) stän-
dig aufgerufen, womit ich Ihnen die  Mö-
glichkeit der Spritebewegung offenhalten
wollte.  Im  Beispiel  steht  an  dieser
Adresse nur ein "RTS",  das  Sie  jedoch
mit  einer eigenen Routine ersetzen kön-
nen.                                    
Wir haben nun also 16  Sprites  auf  dem
Bildschirm,  jedoch  mit  der Einschrän-
kung, daß immer nur jeweils acht im obe-
ren  und  unteren  Bildschirmbereich er-
scheinen  dürfen.  Setzen  wir  die   Y-
Position  eines  Sprites aus dem unteren
Bereich auf eine Zahl kleiner  als  $94,
so  wird es nicht mehr sichtbar sein, da
diese Position ja erst dann in  den  VIC
gelangt,  wenn der Rasterstrahl schon an
ihr vorbei ist. Umgekehrt darf ein Spri-
te  im oberen Bildbereich nie eine Posi-
tion größer als $94 haben. Ausserdem ist
noch ein weiterer Nachteil  in  Kauf  zu
nehmen:  das Umkopieren der Register ist
zwar schnell,  da  wir  absichtlich  mit
Zeropageadressen  arbeiten,  auf die der
Zugriff schneller  ist,  als  auf  Low-/
High-Byte-Adressen  (2  Taktzyklen, ans-
telle von 3),  jedoch  dauert  es  immer
noch knappe 4 Rasterzeilen, in denen gar
keine Sprites dargestellt werden können,
da  es  dort  zu  Problemen kommen kann,
wenn der VIC teilweise schon  die  Werte
der   oberen  und  der  unteren  Sprites
enthält.                                
2) DIE OPTIMIEREUNG                     
Wie  Sie  in  obigem Beispiel sahen, ist
die Programmierung von mehr als 8  Spri-
tes  recht problemlos, solange alle wei-
teren Sprites in der  Horizontalen  von-
einander   getrennt  dargestellt  werden
können. Was nun aber, wenn Sie z.B.  ein
Action-Spiel  programmieren  möchten, in
dem mehr als acht Sprites auf dem  Bild-
schirm  darstellbar sein sollen, und zu-
dem auch noch möglichst kreuz  und  quer
beweglich  sein  müssen? Für diesen Fall
brauchen wir eine  etwas  intelligentere
Routine,  die  es uns ermöglicht, flexi-
bler  mit   den   horizontalen   Sprite-
Positionen umzugehen. Solch eine Routine
werden wir nun realisieren. Sie ist all-
gemeinhin  unter  dem   Namen   "Sprite-
Multiplexer"  bekannt.  Wer  sich nichts
darunter  Vorstellen  kann,  der  sollte
sich  auf  dieser  MD einmal das Double-
Density-Demo anschauen, in dem die  Hin-
tergrundsterne,  sowie  die Meteore, die
über den Bildschirm  huschen  auf  diese
Art und Weise dargestellt werden.       
Kommen wir zunächst zum Grundprinzip der
Multiplex-Routine. Mit ihr soll  es  uns
möglich  sein,  16 Sprites auf dem Bild-
schirm darzustellen, wobei wir  uns  mö-
glichst  wenig  Sorgen über die Darstel-
lung machen wollen.  Diese  Arbeit  soll
unsere  Routine  übernehmen, und automa-
tisch die richtige  Darstellung  wählen.
Damit  es  keine Bereiche gibt, in denen
gar  keine  Sprites  dargestellt  werden
können,  weil  gerade irgendwelche Pseu-
do-VIC-Daten kopiert werden, sollte  Sie
zusätzlich  auch  noch möglichst schnell
sein, bzw. den Zeitpunkt der anfallenden
Werteänderungen   im   VIC    sorgfältig
auswählen können.                       
Um nun all diesen Anforderungen zu genü-
gen,  legen  wir  uns wieder einen "vir-
tuellen" VIC an, den wir  so  behandeln,
als könne er tatsächlich 16 Sprites dar-
stellen. Seine Register sollen wieder in
der Zeropage zu finden sein,  damit  die
Zugriffe   darauf  schneller  ausgeführt
werden können. Hierzu  belegen  wir  die
obere  Hälfte der Zeropage mit den benö-
tigten Registerfunktionen. Beachten  Sie
bitte, daß in dem Fall keine Betriebssy-
stemroutinen mehr verwendet werden  kön-
nen,  da diese nämlich ihre Parameter in
der Zeropage zwischenspeichern und somit
unseren VIC verändern würden!  Hier  nun
zunächst  eine  Registerbelegung unseres
Pseudo-VICs:                            
$80-$8F                                 
16  Bytes, die bestimmen, welche Sprites
ein- oder ausgeschaltet sind. Eine 0  in
einem dieser Bytes schaltet das entspre-
chende Sprite aus. Der Wert  1  schaltet
es  ein  ($80  ist  für Sprite0, $8F für
Sprite15 zuständig).                    
$90-$9F                                 
Diese  16  Bytes halten das Low-Byte der
X-Koordinaten der 16 Sprites.           
$A0-$AF                                 
In  diesen  16 Bytes sind die High-Bytes
der X-Koordinaten der 16 Sprites  unter-
gebracht.                               
$B0-$BF                                 
Hier   sind   nacheinander    alle    Y-
Koordinaten der 16 Sprites zu finden.   
$C0-$CF                                 
Diese Register legen die Farben  der  16
Sprites fest.                           
$D0-$DF                                 
Hier werden die  Spritepointer  unterge-
bracht, die angeben, welcher Grafikblock
durch ein Sprite dargestellt wird.      
$E0-$EF                                 
Dies ist ein Puffer für die Y-Positionen
der 16 Sprites. Er wird für die Darstel-
lung später benötigt.                   
$F0-$FF                                 
Innerhalb dieses Bereichs werden  Zeiger
auf  die  Reihenfolge  der Sprites ange-
legt. Mehr dazu später.                 
Die hier angegebenen Register können nun
von uns genauso  verwendet  werden,  als
könne  der  VIC  tatsächlich  16 Sprites
darstellen. Möchten wir also z.B. Sprite
Nr.9 benutzen, so müssen wir zunächst X-
und  Y-Position  durch  Beschreiben  der
Register  $99,  $A9,  sowie  $B9 setzen,
Farbe und Spritepointer in $C9  und  $D9
unterbringen, und anschließend das Spri-
te durch Schreiben des Wertes 1 in Regi-
ster  $89  einschalten.  Analog wird mit
allen anderen Sprites  verfahren,  wobei
die Sprite-Nummer immer der zweiten Zif-
fer des passenden Registers entspricht. 
Wie  muß  unsere  Multiplex-Routine  nun
vorgehen,  um  dem  VIC  tatsächlich  16
unabhängige  Sprites  zu  entlocken? Man
greift hier, wie bei  allen  Raster-IRQ-
Programmen, zu einem Trick: Wie wir bis-
her  gesehen  hatten,  ist  die  einzige
hardwaremäßige   Beschränkung,  die  uns
daran hindert,  mehr  als  acht  Sprites
darzustellen,  die X-Koordinate. Es kön-
nen also immer maximal acht Sprites  mit
derselben   Y-Koordinate   nebeneinander
stehen. Daran können wir  auch  mit  den
besten  Rastertricks  nichts  ändern. In
der Horizontalen, können wir  aber  sehr
wohl  mehr  als acht Sprites darstellen,
und das  eigentlich  beliebig  oft  (das
erste  Beispielprogramm  hätte auch pro-
blemlos 24, oder gar 32 Sprites auf  den
Bildschirm bringen können). Rein theore-
tisch kann ja  ein  Sprite,  das  weiter
oben  am Bildschirm schon benutzt wurde,
weiter unten ein weiteres Mal  verwendet
werden.  Einzige  Bedingung  hierzu ist,
daß das zweite, virtuelle,  Sprite  min-
destens  22 Rasterzeilen (die Höhe eines
ganzen Sprites plus eine Zeile  "Sicher-
heitsabstand") unterhalb des ersten vir-
tuellen Sprites liegt. Damit  diese  Be-
dingung  so  oft wie nur möglich erfüllt
ist, verwendet man ein echtes VIC-Sprite
immer zur Darstellung des  n-ten,  sowie
des  <n+8>-ten  virtuellen  Sprites. Das
echte Sprite0 stellt also das  virtuelle
Sprite0,  sowie  das  virtuelle  Sprite8
dar. Da die Y-Koordinaten beider Sprites
jedoch beliebig sein können, müssen  wir
zuvor  eine  interne  Sortierung  der Y-
Koordinaten  vornehmen,   so   daß   der
größtmögliche  Abstand zwischen den bei-
den Sprites erreicht wird.  Dies  sollte
unsere  Routine  in  einem Bildschirmbe-
reich tun, in dem  keine  Sprites  ange-
zeigt werden können, also dann, wenn der
Rasterstrahl  gerade dabei ist, den obe-
ren und unteren Bildschirmrand zu zeich-
nen  (selbst  wenn  man  diesen auch ab-
schalten kann). Nach der Sortierung kön-
nen  nun  die Werte der ersten acht vir-
tuellen Sprites im VIC eingetragen  wer-
den,  wobei gleichzeitig ermittelt wird,
in welcher Rasterzeile Sprite0  zu  Ende
gezeichnet  ist.  Für  diese Rasterzeile
wird  ein  Raster-Interrupt  festgelegt,
der  die  Werte  für  Sprite8 in den VIC
eintragen soll, und zwar in die Register
des "echten" Sprite0.  Ebenso  wird  mit
den  Sprites  von 9-15 verfahren. Jedes-
mal, wenn das  korrespondierende  Sprite
(n-8)  zu Ende gezeichnet wurde, muß ein
Rasterinterrupt auftreten, der die Werte
des neuen Sprites schreibt.             
Kommen wir nun  jedoch  zu  der  Routine
selbst.  Sie finden Sie übrigens auch in
den  beiden  Programmbeispielen  "MULTI-
PLEX1"  und  "MULTIPLEX2" auf dieser MD.
Beide wierden wie üblich mit ",8,1"  ge-
laden  um  mittels  "SYS4096" gestartet.
Die Initialierung möchte ich Ihnen dies-
mal  ersparen, da Sie fast identisch mit
der obigen ist. Wichtig ist,  daß  durch
sie  zunächst ein Rasterinterrupt an der
Rasterposition $F8  ausgelöst  wird,  an
der  unsere Multiplex-Routine ihre Orga-
nisationsarbeit durchführen soll.  Hier-
bei  wird  in  die  folgende IRQ-Routine
verzweigt, die in den Programmbeispielen
an Adresse $1100 zu finden ist:         
BORD:pha         ;Prozessor-Regs.       
     txa         ; retten               
     pha                                
     tya                                
     pha                                
     lda #$e0    ;Neuen Raster-IRQ      
     sta $d012   ; für Zeile $E0        
     lda #$00    ; und Routine "BORD"   
     sta $fffe   ; festlegen            
     lda #$11                           
     sta $ffff                          
     dec $d019   ;VIC-IRQs freigeben    
     jsr PLEX    ;Multiplexen           
     jsr SETSPR  ;Sprites setzen        
     pla         ;Prozessorregs. wieder 
     tay         ; zurückholen und      
     pla         ; IRQ beenden          
     tax                                
     pla                                
     rti                                
  (Bitte wählen Sie nun den 2. Teil des 
      IRQ-Kurses aus dem Textmenu!)     
      Fortsetzung IRQ-Kurs (Teil9)      
----------------------------------------
Wie Sie sehen, wird hier  lediglich  der
Rasterinterrupt  auf  Zeile $E0 gesetzt,
sowie die Bord-Routine  als  IRQ-Routine
eingestellt. Die eigentliche Arbeit wird
von  den  Routinen  "PLEX"  und "SETSPR"
durchgeführt, wovon wir uns die  Erstere
nun genauer anschauen möchten. Sie steht
ab Adresse $1300:                       
PLEX:clc        ;Sprites zaehlen, indem 
     lda $80    ; die Inhalte der Ein-/ 
     adc $81    ; Ausschaltregister     
     adc $82    ; aller Sprites einfach 
     adc $83    ; im Akku aufaddiert    
     adc $84    ; werden.               
     adc $85                            
     adc $86                            
     adc $87                            
     adc $88                            
     adc $89                            
     adc $8a                            
     adc $8b                            
     adc $8c                            
     adc $8d                            
     adc $8e                            
     adc $8f                            
     sta $7e    ;Anzahl merken          
     tax        ;Aus Tabelle ONTAB      
     lda ONTAB,x; den VIC-Wert zum Ein- 
     sta $7f    ; schalten holen und in 
                ; $7F ablegen           
     cpx #$00   ;Keine Sprites an?      
     bne clry   ;Nein, also weiter      
     rts        ;Sonst Prg. beenden     
Diese Routine ermittelt zunächst einmal,
wieviele Sprites überhaupt eingeschaltet
sind. Dies tut sie, indem Sie die Inhal-
te  der  Einschalt-Register  des Pseudo-
VICs aufaddiert, wobei das Ergebnis  die
Anzahl  eingeschalteter  Sprites  ergibt
(wenn an, dann  Wert=1,  sonst  0).  An-
schließend  wird aus der Tabelle "ONTAB"
der Wert ausgelesen,  der  in  das  VIC-
Register  zum  Einschalten  der  Sprites
kommen  muß,  um  die  gefundene  Anzahl
Sprites zu aktivieren. Die Liste enthält
folgende Werte:                         
$00,$01,$03,$07,$0F,$1F,$3F,$7F         
$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF         
Sind nun weniger als acht Sprites einge-
schaltet, so wird auch nur diese  Anzahl
aktiviert  werden. Sind es mehr, so müs-
sen immer alle acht  eingeschaltet  wer-
den. Der so ermittelte Wert wird dann in
der  Speicherzelle  $7F  zwischengespei-
chert. Für den weiteren Verlauf der Rou-
tine ist auch die ermittelte Anzahl not-
wendig, die in der  Zeropageadresse  $7E
untergebracht wird. Zum Schluß prüft die
Routine  noch,  ob  überhaupt ein Sprite
eingeschaltet werden soll, und kehrt bei
einer Anzahl von 0 unverrichteter  Dinge
zur IRQ-Routine zurück.                 
Wenn mindestens ein Sprite eingeschaltet
ist, so wird die eigentliche  Multiplex-
Routine   aktiv.  Sie  muß  nun  die  Y-
Koordinaten der Sprites  sortieren,  und
die Verteilung der 16 virtuellen Sprites
auf  die  echten VIC-Sprites übernehmen.
Zur Sortierung benötigen wir  noch  zwei
weitere, jeweils 16 Byte große, Felder. 
Im ersten, von Zeropageadresse  $E0  bis
$EF,  wird  eine Kopie der Y-Koordinaten
angelegt, welche dann sortiert wird.  Da
die Sortierroutine die Werte der einzel-
nen Koordinaten  manipulieren  muß,  ist
diese  Maßnahme  notwendig.  Desweiteren
darf sie die Spritedaten  in  den  Regi-
stern  von  $80 bis $DF nicht verändern,
bzw. vertauschen, da ein  Programm,  das
die  viruellen  VIC-Register  mit  Daten
füttert, ja immer  davon  ausgehen  muß,
daß  es  bei  Sprite0  immer das ein und
selbe Sprite anspricht, und nicht eines,
das von  einer  Position  weiter  hinten
hierhin sortiert wurde. Deshalb sortiert
die  Multiplex-Routine nicht die eigent-
lichen Sprites in die benötigte  Reihen-
folge,  sondern  legt  eine  Tabelle mit
Zeigern auf die Reihenfolge der  Sprites
an. Selbige wird in den Zeropageadressen
von  $F0  bis  $FF abgelegt. Sie enthält
nach der Sortierung Werte zwischen 0 und
15, die als Index auf die dem virtuellen
Sprite entsprechenden Register dienen.  
Zur  eigentlichen  Sortierung  verwenden
wir nun einen ganz einfachen Bubblesort-
Algorithmus, der so oft über dem zu sor-
tierenden Feld angewandt wird, wie Spri-
tes  eingeschaltet  sind.  Er  ermittelt
durch eine Reihe von  Vergleichen  immer
den  kleinsten  Wert innerhalb der Kopie
der Y-Positionen, legt ihn in  der  Zei-
ger-Liste bei $F0 ab, und setzt die ent-
sprechende Koordinate auf $FF, damit sie
im   nächsten    Sortierdurchlauf    den
größtmöglichen  Wert  enthält  und somit
nicht noch einmal das Minimum sein kann.
Ebenso müssen wir mit den  Y-Koordinaten
von  abgeschalteten  Sprites  verfahren,
die vor dem eigentlichen Sortieren eben-
falls  auf  $FF  gesetzt werden. Dadurch
stehen sie immer am Ende der Liste. Nach
der Sortierung kann die Routine dann mit
Hilfe der Zeiger die Daten zu den  Spri-
tes  aus  den  entsprechenden  Registern
holen, und in den VIC schreiben.        
Bevor  wir nun zur Beschreibung der Rou-
tine kommen, noch einige technische Hin-
weise:   damit   die  Routine  möglichst
schnell arbeitet, wurde auf die  Verwen-
dung  von  Schleifen so weit wie möglich
verzichtet. Das heißt, daß z.B jeder der
Y-Werte über  einen  eigenen  CMP-Befehl
verfügt, der ihn mit dem aktuellen Mini-
mum vergleicht. Analog ist es bei  ande-
ren Vorgängen (so auch bei der Aufaddie-
rung der Sprite-Einschalt-Register oben,
wo eigentlich auch eine  Schleife  hätte
benutzt  werden können). Dadurch verlän-
gert sich  das  Programm  natürlich  ein
wenig,  jedoch  ist der damit verbundene
Speicheraufwand noch erträglich und  wir
erreichen zudem eine hohe Verarbeitungs-
geschwindigkeit. Da sich viele  Vorgänge
oft  wiederholen,  werde  ich  an diesen
Stellen mit "..." eine  Folge  andeuten,
die  analog auch für alle weiteren Spri-
tes ausgeführt wird.                    
Kommen  wir  nun  also  zur eigentlichen
Multiplex-Routine,  die  mit  dem  Label
"CLRY"  beginnt,  wohin  auch die letzte
Routine verzweigt, wenn  Sprites  einge-
schaltet sind. Hier wird nun geprüft, ob
ein   Sprite   eingeschaltet  ist,  oder
nicht, und  in  letzterem  Fall  die  Y-
Koordinate  mit dem Wert $FF überschrie-
ben, damit das abgeschaltete Sprite  bei
der Sortierung später nicht mehr berück-
sichtigt wird:                          
CLRY:ldy #$ff  ;Y-Reg mit $FF laden     
sx0: lda $80   ;Sprite0 an?             
     bne sx1   ;Ja, also weiter         
     sty $B0   ;Nein, also $FF in Y-Pos 
sx1: lda $81   ;Sprite 1 an?            
     bne sx2   ;Ja, also weozer         
     sty $B1   ;Nein, also $FF in Y-Pos 
sx2: ...Analog für Sprites 3-14         
sx15: lda $8F   ;Sprite 15 an?          
      bne YCOPY ;Ja, also zu YCOPY spr. 
      sty $BF   ;Nein, also $FF in Y-Pos
Damit  hätten  wir nun also alle Y-Posi-
tionen abgeschalteter Sprites  gelöscht.
Da die Routine die Y-Positionen der ein-
geschalteten  Sprites  nicht   verändern
darf,  wird  nun eine Kopie dieser Werte
in $E0 bis $EF angelegt:                
YCOPY:lda $B0  ;Y-Wert Sprite 0         
      sta $E0  ; kopieren               
      ...Analog für Sprites 1-14        
      lda $BF  ;Y-Wert Sprite 15        
      sta $EF  ; kopieren               
Nachdem nun auch das Sorierfeld angelegt
wurde, können wir endlich mit  der  Sor-
tierung  beginnen.  Hierbei benutzen wir
das Y-Register um den momentan kleinsten
Y-Wert zu speichern. Der Akku wird  beim
Auffinden  eines  minimalen  Wertes dann
immer mit einem Zeiger auf das  entspre-
chende  Sprite  geladen, der der Sprite-
nummer   entspricht.   Das    X-Register
enthält den aktuellen Index auf die Zei-
gertabelle und  wird  pro  Durchlauf  um
eins erhöht. Die Sortierung ist beendet,
wenn  die Vergleichsroutine insgesamt so
oft durchlaufen wurde, wie Sprites  ein-
geschaltet sind:                        
SORT: ldx #$00  ;Index init.            
loop: ldy #$ff  ;YMin. init.            
      cpy $E0   ;YSpr0 < YMin?          
      bcc s1    ;Nein, also weiter      
      ldy $E0   ;Ja, also YMin=YSpr0    
      lda #$00  ;Zeiger Spr0=0 laden    
s1:   cpy $E1   ;YSpr1 < YMin?          
      bcc s2    ;Nein, also weiter      
      ldy $E1   ;Ja, also YMin=YSpr1    
      lda #$01  ;Zeiger Spr1=1 laden    
s2:   ...Analog für Sprites 3-14        
s15:  cpy $EF   ;YSpr15 < YMin?         
      bcc s16   ;Nein, also weiter      
      ldy $EF   ;Ja, also YMin=YSpr15   
      lda #$0F  ;Zeiger Spr15=15        
s16:  sta $F0,x ;Zeiger ablegen         
      tay       ;Zgr. als Index in Y-Reg
      lda #$ff  ;YSpr mit YMin auf $FF  
      sta $E0,y ; setzen.               
      inx       ;Zeiger-Index+1         
      cpx $7E   ;mit Anzahl Sprites vgl.
      beq end   ;Gleich, also Ende      
      jmp loop  ;Sonst nochmal sortieren
end:  rts                               
Wie Sie sehen, wird nach jedem ermittel-
ten Minimum der entsprechende Y-Wert auf
$FF  gesetzt,  damit er im nächsten Ver-
gleich nicht mehr herangezogen wird. Auf
diese Weise wird nun nach und nach immer
wieder der kleinste Wert ermittelt,  so-
lange, bis der Puffer nur noch $FF-Werte
enthält,   und   die  Schleife  "Anzahl-
Sprites"-Mal durchlaufen wurde. Die Sor-
tierung  ist damit beendet, und die Mul-
tiplex-Routine  kehrt  wieder  zur  IRQ-
Routine zurück.                         
Hier nun wird als Nächstes die "SETSPR"-
Routine aufgerufen, die anhand  der  er-
mittelten  Werte  zunächst die Daten der
ersten acht virtuellen  Sprites  in  den
VIC  überträgt.  Gleichzeitig  berechnet
sie  mit  Hilfe  der  Y-Position  dieser
Sprites  die Rasterzeile, an der ein IRQ
ausgelöst werden  muß,  um  das  jeweils
achte Sprite nach dem aktuellen anzuzei-
gen, und setzt den nächsten IRQ-Auslöser
entsprechend. Zunächst einmal wollen wir
uns den ersten Teil dieser  Routine  an-
schauen. Er beginnt ab Adresse $1500:   
SETSPR:                                 
      lda $7F    ;VIC-Wert für eingesch.
      sta $d015  ; Sprites setzen       
      lda #$00   ;High-Bits der X-Pos   
      sta $d010  ; löschen              
      lda $7E    ;Anzahl Sprites holen  
      cmp #$01   ;Wenn mind. 1 Spr. ein-
      bcs spr00  ;gesch., dann weiter   
      rts        ;Sonst Ende            
spr00:clc        ;C-Bit für Add. löschen
      ldx $E0    ;Zgr. aus Tabelle holen
      lda $90,x  ;X-Pos. holen und für  
      sta $d000  ; VIC-Spr.0 setzen     
      lda $B0,x  ;Y-Pos. holen und für  
      sta $d001  ; VIC-Spr.0 setzen     
      adc #22    ;Raster für Spr.Ende=  
      sta ras8+1 ; YPos+22 setzen       
      lda $D0,x  ;Spr.Zeiger holen und  
      sta $07f8  ; für VIC-Spr.0 setzen 
      lda $C0,x  ;Spr.Farbe holen und   
      sta $d027  ; für VIC-Spr.0 setzen 
      ldy $a0,x  ;X-Pos-High holen,     
      lda $d010  ;X-High-Bit-Reg. holen 
      ora high0,y;Wert f. VIC-Spr.0 ein-
      sta $d010  ; odern und zurückschr.
      lda $7E    ;Anzahl holen          
      cmp #$02   ; Mehr als 1 Spr. an?  
      bcs spr01  ; Ja, also weiter.     
      rts        ;Sonst Ende            
spr01:Analog für Sprite 1-7             
      ...                               
spr07:...        ;Werte f. Spr.7 setzen 
      lda $7E    ;Falls mehr als acht   
      cmp #$09   ; Sprites, dann        
      bcs acht   ; neuen IRQ setzen     
      rts        ; Sonst Ende           
acht: lda #<spr08;Adr. Routine "Spr8"   
      sta $fffe  ; in IRQ-Vektor bei    
      lda #>spr08; $FFFE/$FFF ein-      
      sta $ffff  ; tragen               
ras8: lda #$00   ;Rasterz. Spr0+22 als  
      sta $d012  ; IRQ-Quelle setzen    
      dec $d019  ;VIC-IRQs freigeben    
      rts        ;Ende                  
Wie Sie sehen, holt  die  SETSPR-Routine
nun  nacheinander  alle  Zeiger  aus der
sortierten Tabelle bei $F0 und überträgt
die Werte der ersten acht Sprites in den
VIC. Auf die  Register  des  Pseudo-VICs
wird  dabei  über  X-Register-indizierte
Adressierung  zugegriffen.  Zwei   Dinge
sollten  nun  noch erläutert werden: Zum
Einen wird nach  Setzen  der  Y-Position
eines Sprites die Rasterzeile berechnet,
an der es zu Ende gezeichnet ist. Der so
ermittelte  Wert  wird nun  an dem Label
"RAS8" plus 1 eingetragen, womit wir den
Operanden des LDA-Befehls  am  Ende  der
Routine  modifizieren.  Er  lädt nun den
Akku mit der besagten Rasterzeilennummer
und  schreibt  ihn  in  das  Raster-IRQ-
Register, womit der VIC nachdem er Spri-
te0 auf dem Bildschirm dargestellt  hat,
einen  Raster-IRQ  erzeugt. Hierbei wird
dann zur Routine "SPR8"  verzweigt,  die
ich Ihnen gleich erläutern werde. Analog
wird  mit den Sprites von 1-7 verfahren,
wobei nach jedem Sprite geprüft wird, ob
noch ein weiteres  Sprite  eingeschaltet
ist,  und  demnach  initialisiert werden
muß. Ist das nicht  der  Fall,  so  wird
direkt  zum  IRQ  zurückgekehrt. Dadurch
wird ebenfalls nur dann ein IRQ auf  die
Routine    "SPR8"    eingestellt,   wenn
tatsächlich  mehr  als  acht   virtuelle
Sprites  eingeschaltet  sind. Im anderen
Fall brauchen wir ja keine  Manipulation
vorzunehmen,  weswegen  der  nächste Ra-
ster-IRQ, wieder auf "BORD" springt,  wo
wir  die  Multiplex-Routine ein weiteres
Mal durchlaufen. Sollen  nun  aber  mehr
als  acht Sprites dargestellt werden, so
wird ein IRQ erzeugt, der auf die Routi-
ne  "SPR08"  springt, die wir uns gleich
näher anschauen werden.                 
Die zweite, etwas undurchsichtige Stelle
ist  das  Setzen  der  High-Bits für die
X-Position eines Sprites. Hier gehen wir
wiefolgt vor: Zunächst  wird  der  High-
Wert  der  X-Position  geholt, der nur 0
oder 1 sein,  je  nach  dem  ob  die  X-
Position  kleiner oder größer/gleich 256
ist. Dieser Wert wird nun als Zeiger auf
eine Tabelle mit  X-High-Bit-Werten  für
das  jeweilige Sprite benutzt. Sie steht
ganz am Ende  des  Programms  und  sieht
folgendermaßen aus:                     
high0:    $00,$01                       
high1:    $00,$02                       
high2:    $00,$04                       
high3:    $00,$08                       
high4:    $00,$10                       
high5:    $00,$20                       
high6:    $00,$40                       
high7:    $00,$80                       
Wie  Sie  sehen,  enthält  sie für jedes
Sprite einmal ein  Nullbyte,  das  durch
die  SETSPR-Routine  geladen  wird, wenn
der X-High-Wert Null  ist,  sowie  einen
Wert, in dem das Bit gesetzt ist, das im
X-High-Bit-Register für das entsprechen-
de Sprite zuständig ist. Durch das  Ein-
odern  des  ermittelten Wertes in dieses
Register setzen wir nun letztendlich das
High-Bit der X-Position  eines  Sprites.
Hierbei  wird  bei  den  Sprites von 1-7
jeweils  auf  ein  eigenes  "High"-Label
zugegriffen,   bei  Sprite  1  also  auf
"High1", bei Sprite 2 auf "High2" und so
weiter.                                 
Kommen   wir  nun  jedoch  zur  "SPR08"-
Routine, die als IRQ-Routine  aufgerufen
wird,  und  zwar nachdem Sprite0 auf dem
Bildschirm dargestellt wurde:           
SPR08:pha        ;Prozessor-Regs. retten
      txa                               
      pha                               
      tya                               
      pha                               
      ldx $F8    ;Zgr. aus Sort-Liste   
      lda $90,x  ;X-Pos. in VIC-Spr0    
      sta $d000  ; eintragen            
      lda $B0,x  ;Y-Pos. in VIC-Spr0    
      sta $d001  ; eintragen            
      lda $D0,x  ;Spr.Zgr. in VIC-Spr0  
      sta $07f8  ; eintragen            
      lda $C0,x  ;Spr.Farbe in VIC-Spr0 
      sta $d027  ; eintragen            
      ldy $90,x  ;X-High-Wert holen     
      lda $d010  ;VIC-High-Reg. lesen   
      and #$FE   ;Bit f. Spr0 ausmask.  
      ora high0,y;Mit Wert f. X-High    
      sta $d010  ; odern u. zurückschr. 
      lda #<spr09;Adresse f. Raster-IRQ 
      sta $fffe  ; des nächsten Sprite  
      lda #>spr09; in IRQ-Vektoren      
      sta $ffff  ; schreiben            
      dec $d019  ;VIC-IRQs freigeben    
      lda $7E    ;Anzahl holen,         
      cmp #$0a   ;Spr9 benutzt?         
      bcs ras9   ;Ja, also weiter       
      jmp bordirq;Sonst IRQ rücksetzen  
ras9: lda #$00   ;Rasterz. f. Spr9 laden
      cmp $d012  ;m. akt. Strahlpos vgl.
      bmi direct9;Wenn größer o. gleich 
      beq direct9; Spr.9 sofort zeichnen
      sta $d012  ;Sonst n. IRQ festlegen
      pla        ;Prozessor-Regs zurück-
      tay        ; holen und IRQ        
      pla        ; beenden              
      tax                               
      pla                               
      rti                               
Wie  Sie  sehen,  werden zunächst wieder
wie  schon  bei  den   anderen   Sprite-
Routinen, die vituellen VIC-Werte in den
echten  VIC  übertragen.  Hiernach  wird
verglichen, ob  mehr  als  neun  Sprites
dargestellt  werden  sollen, und wenn ja
zur Routine "RAS9"  verzweigt,  die  den
IRQ  für  Sprite9  vorbeitet.  An diesem
Label  befindet  sich  wieder  der  LDA-
Befehl,  der  von der Darstellungroutine
für Sprite1 so abgeändert wurde, daß  er
die Rasterzeile des Endes dieses Sprites
in  den  Akku lädt. Bevor nun der Inter-
rupt gesetzt wird,  prüft  das  Programm
durch  einen  Vergleich  des Akkuinhalts
mit der aktuellen  Rasterstrahlposition,
ob  der  Rasterstrahl  an besagter Zeile
nicht schon vorüber  ist.  In  dem  Fall
wird  kein  IRQ vorbereitet, sondern di-
rekt auf die Routine zur Darstellung des
nächsten  Sprites  verzweigt  (sh.  BEQ-
bzw.  BMI-Befehle).  Diese  beginnt dann
folgendermaßen:                         
  (Bitte wählen Sie nun den 3.Teil des  
      IRQ-Kurses aus dem Textmenu!)     
       Forsetzung IRQ-Kurs (Teil9)      
----------------------------------------
SPR09:  pha       ;Prozessor-Regs.      
        txa       ; retten (dieser Teil 
        pha       ; wird bei einem IRQ  
        tya       ; abgearbeitet!)      
        pha                             
direct9:ldx $F9   ;Setzen der Werte für 
        lda $99,x ;Spr9 in VIC-Spr1     
        ...                             
Wie Sie sehen, wird durch den Sprung auf
"direct9"  lediglich  der  IRQ-Teil  der
Routine übersprungen. Ansonsten ist  die
Routine  identisch  mit  "SPR8".  Ebenso
existieren jeweils eigene  Routinen  für
die  Sprites von 9 bis 15. Sollten weni-
ger als 15 Sprites  dargestellt  werden,
so  wird,  wie  bei  SPR8 auch schon er-
sichtlich,  auf  die  Routine  "BORDIRQ"
gesprungen.  Sie  steht ganz am Ende des
Programms und setzt  "BORD"  wieder  als
nächste  IRQ-Routine und beendet den ak-
tuellen IRQ. Sie wird ebenfalls  abgear-
beitet,  wenn  das fünfzehnte, virtuelle
Sprite initialisiert wurde, so  daß  der
gesamte Multiplex-Vorgang nocheinmal von
vorne beginnen kann.                    
Dies wäre es dann wieder einmal für die-
sen Monat. Ich möchte Sie noch dazu ani-
mieren,  ein  wenig  mit  den Multiplex-
Routinen herumzuexperimentieren.  Versu-
chen  Sie doch einmal 24 Sprites auf den
Bildschirm zu bringen! Im  übrigen  sind
nicht  immer  alle  16  Sprites  auf dem
Bildschirm darstellbar. Ist der  Abstand
zwischen  zwei  virtuellen  Sprites, die
durch das selbe  "echte"  Sprite  darge-
stellt werden, kleiner als 22 Rasterzei-
len,  so  erscheint  das  zweite  Sprite
nicht  auf  dem  Bildschirm. Desweiteren
ist aus dem Multiplex-Demo2 ersichtlich,
daß es auch  Probleme  mit  der  Sprite-
Priorität gibt. Dadurch, daß die Sprites
umsortiert  werden  müssen, kann es pas-
sieren, daß manche zunächst Sprites vor,
und beim nächsten Bildaufbau hinter  an-
deren Sprites liegen. Da man die Sprite-
Priorität  nicht  beeinflussen kann, und
Sprites mit kleinen Spritenummern  Prio-
riät  vor  Sprites mit großen Spritenum-
mern haben, kommt  es  hier  zu  kleinen
Darstellungsfehlern,  die  leider  nicht
behoben,  sondern  lediglich  unsichtbar
gemacht  werden  können, indem man allen
Sprites dieselbe Farbe  gibt.  In  einem
Spiel,  in  dem  es wichtig ist, daß die
Spielfigur immer vor allen  anderen  Ob-
jekten  zu sehen ist, kann man aber auch
nur die Sprites von 1-7 multiplexen, und
Sprite 0 unberührt  von  den  restlichen
lassen.  Die  Routine kann dann zwar nur
15 Sprites darstellen, korrigiert jedoch
den obigen Fehler.  Wie  Sie  sehen  ist
auch  dieser Raster-Effekt in vielfachen
anwendeungsspezifischen  Kombinationsmö-
glichkeiten umsetzbar.                  
                                    (ub)
                IRQ-KURS                
     "Die Hardware ausgetrickst..."     
                (Teil 10)               
----------------------------------------
Herzlich  Willkommen  zum  zehnten  Teil
unseres  Raster-IRQ-Kurses.  In   dieser
Ausgabe wollen wir uns weiterhin mit der
trickreichen Spriteprogrammierung befas-
sen. Im letzten Kursteil hatten  Sie  ja
schon  gelernt,  wie man mit Hilfe eines
Sprite-Multiplexers mehr als  8  Sprites
auf  den  Bildschirm  zaubert. Mit einem
ähnlichen Trick werden wir  heute  einen
sogenannten "Movie-Scroller" programmie-
ren,  der  es  uns   ermöglicht,   einen
Scrolltext, so wie in Abspännen von Fil-
men, von unten nach oben über den gesam-
ten Bildschirm zu scrollen. Hierbei wer-
den  wir  wieder  durch  den  Multiplex-
Effekt insgesamt 104 (!) Sprites aus dem
VIC locken!                             
1) DAS PRINZIP DES MOVIESCROLLERS       
Das Funktionsprinzip des Movie-Scrollers
ist recht einfach und sollte nach Kennt-
nis der Multiplex-Routinen kein  Problem
für  Sie sein: Jede Zeile unseres Movie-
Scrollers soll aus 8  Sprites  aufgebaut
sein,  in  denen wir einen Text darstel-
len. Um nun mehrere Zeilen  zu  generie-
ren,  müssen wir in regelmäßigen Abstän-
den einen Raster-IRQ erzeugen,  der  je-
desmal  die  Y-Position, sowie die Spri-
te-Pointer der acht Sprites  neu  setzt,
um somit die nächste Scroller-Zeile dar-
zustellen. Unsere Routine tut dies  alle
24  Rasterzeilen,  womit  sich  zwischen
zwei Spritezeilen immer  3  Rasterzeilen
Freiraum  befinden. Um nun einen Scroll-
effekt nach oben zu  erzeugen,  benutzen
wir  zusätzlich  ein  Zählregister,  daß
einmal pro Rasterdurchlauf um  1  ernie-
drigt,  und so von $17 bis $00 herunter-
gezählt wird. Es dient als  zusätzlicher
Offset  auf  die  Rasterzeilen, in denen
ein IRQ ausgelöst werden muß. Ist dieses
Zählregister auf 0  heruntergezählt,  so
wird es wieder auf $17 zurückgesetzt und
die   Spritepointer  für  jede  einzelne
Spritezeile werden alle um je eine Zeile
höher kopiert. In die, am Ende der Poin-
terliste,  freigewordenen Sprites werden
dann die Buchstaben der neuen Zeile ein-
kopiert,  die sich dann von unten wieder
in den Scroller einreihen können.       
2) DER PROGRAMMABLAUF                   
Um  den  Movie-Scroll-Effekt  besser  zu
erläutern  haben  wir  natürlich  wieder
einige Beispielprogramme für Sie  parat.
Sie  heißen  "MOVIE.1" und "MOVIE.2" und
befinden sich ebenfalls auf  dieser  MD.
Wie  immer  müssen  beide  Beispiele mit
",8,1" geladen und durch  ein  "SYS4096"
gestartet  werden. "MOVIE.2" unterschei-
det sich von "MOVIE.1"  nur  darin,  daß
zusätzlich  zum  Scroller auch der obere
und untere Bildschirand geöffnet wurden.
Dies ist nur durch einen ganz besonderen
Trick möglich, den wir später noch  erl-
äutern  werden.  Ich  möchte  Ihnen  nun
zunächst  eine  Speicheraufteilung   der
beiden  Routinen  geben,  damit Sie sich
die  einzelnen  Unterroutinen,  die  ich
nicht alle in diesem Kurs erläutern wer-
de, mit Hilfe eines Disassemblers einmal
selbst anschauen können:                
Adr.   Funktion                         
----------------------------------------
$0800  Zeichensatz                      
$1000  IRQ-Init,  incl.  aller   Sprite-
       Initialiserungen                 
$1100  Movie-IRQ-Routine. Dies  ist  die
       eigentliche IRQ-Routine, die alle
       24  Rasterzeilen  die Sprites neu
       setzt.                           
$1200  BORDERNMI (nur in "MOVIE.2"), zum
       Üffnen  des  oberen  und  unteren
       Bildschirmrandes.                
$1300  MOVESPR-Routine.  Sie  bewegt die
       13 Spritezeilen pro Rasterdurlauf
       um eine Rasterzeile nach oben.   
$1400  MAKETEXT-Routine.  Diese  Routine
       schreibt  den  Text  in  die acht
       Sprites, die sich gerade am unte-
       ren Bildschirmrand befinden.     
$1500  Spritepointer-Liste, die auf  die
       Sprites  im  Bereich  von  $2600-
       $3FFF zeigt.                     
$1600  Der Scroll-Text im ASCII-Format. 
Die Initialisierungsroutine unseres  Mo-
vie-IRQs  schaltet  wie  üblich  das Be-
triebssystem-ROM ab, und setzt im  Hard-
ware-IRQ-Zeiger   bei   $FFFE/$FFFF  die
Startadresse    der     MOVIEIRQ-Routine
($1200)  ein.  Zudem  werden  alle  CIA-
Interrupts gesperrt und die  Spriteregi-
ster  des  VICs  initialisiert.  Hierbei
tragen wir lediglich  alle  X-Positionen
der  Sprites  ein, die bei $58 beginnen,
und  in  24-Pixel-Schritten  pro  Sprite
erhöht  werden.  Natürlich  muß auch das
X-Position-High-Bit des achten  Sprites,
daß  sich ganz rechts auf dem Bildschirm
befindet, auf 1 gesetzt werden.  Deswei-
teren  wird  die Farbe aller Sprites auf
"weiß" geschaltet.  Zusätzlich  wird  in
einer eigenen Unterroutine der Speicher-
bereich von $2600-$3FFF, in dem die  104
Sprites  unterbringen, gelöscht. Zuletzt
legt die  Init-Routine  Rasterzeile  $17
als ersten IRQ-Auslöser fest und erlaubt
dem VIC IRQs zu erzeugen.               
Die MOVIEIRQ-Routine stellt nun den Kern
unseres Movie-Scrollers dar. Es  handelt
sich  hierbei  um  die  IRQ-Routine, die
alle 24  Rasterzeilen  die  Y-Positionen
der  Sprites ändert. Sie wird zum ersten
Mal an Rasterzeile $17 angesprungen  und
setzt    dann    die    folgenden   IRQ-
Rasterzeilen von selbst.  Hier  nun  der
kommentierte Sourcecode:                
MOVIEIRQ:                               
   pha         ;Prozessorregs. retten   
   txa                                  
   pha                                  
   tya                                  
   pha                                  
   lda #$ff    ;Alle Sprites            
   sta $d015   ; einschalten            
   inc $d020   ;Rahmenfarbe erhöhen     
Nach Einsprung in die  IRQ-Routine  wer-
den,  nach  dem  obligatorischen  Retten
aller Prozessorregister,  zunächst  alle
Sprites    eingeschaltet.   Anschließend
erhöhen wir die Rahmenfarbe um 1,  damit
Sie  sehen  können, wie lange es dauert,
bis alle Sprites neu gesetzt wurden. Nun
beginnt der  eigentliche  Hauptteil  der
Routine:                                
   clc         ;C-Bit für Add. löschen  
   ldy counter ;Rasterzähler als Y-Index
   lda softroll;Akt. Scrolloffs. holen..
   adc ypos,y  ; ..Y-Wert addieren..    
   sta $d001   ; ..und selbigen in alle 
   sta $d003   ;   acht Y-Positionen    
   sta $d005   ;   der Sprites eintragen
   sta $d007                            
   sta $d009                            
   sta $d00b                            
   sta $d00d                            
   sta $d00f                            
Wir  setzen  hier  die  Y-Positionen der
Sprites. Hierbei hilft uns eine  Tabelle
namens  "YPOS",  in  der  alle  Basis-Y-
Positionen der insgesamt 13 Spritezeilen
untergebracht sind. Die Y-Positionen der
Sprites in der ersten Zeile  sind  dabei
auf  den Wert 26 festgelegt. Alle weite-
ren Positionen resultieren dann aus  dem
jeweils  letzten  Positionswert plus dem
Offset 24.                              
Desweiteren  erscheinen  in  diesem Pro-
grammteil noch zwei Labels, mit den  Na-
men "SOFTROLL" und "COUNTER". Sie stehen
für die Zeropageadressen $F6 und $F7, in
denen  wir  Zwischenwerte  unterbringen.
"SOFTROLL"  ($F6)  ist  der  oben  schon
erwähnte Rasterzeilenzähler, der von $17
auf 0 heruntergezählt wird. In "COUNTER"
ist  vermerkt,  wie  oft die IRQ-Routine
während des  aktuellen  Rasterdurchlaufs
schon   aufgerufen   wurde.  Dies  dient
gleichzeitig als  Zähler  dafür,  welche
Spritezeile  wir  momentan zu bearbeiten
haben. Beim ersten Aufruf enthält "COUN-
TER"  den Wert 0. Auf diese Weise können
wir ihn als Index auf  die  YPOS-Tabelle
verwenden. Nachdem der Akku nun also mit
den Scrolloffset "SOFTROLL" geladen wur-
de, kann so der  Basis-Y-Wert  der  ent-
sprechenden   Spritezeile   (im   ersten
Durchlauf Zeile 0,  Y-Pos  26)  auf  den
Akkuinhalt aufaddiert werden. Der resul-
tierende  Wert  entspricht  nun  der  Y-
Position aller Sprites dieser Zeile, die
wir sogleich in die VIC-Register eintra-
gen.                                    
Nun müssen noch  die  Spritepointer  der
neuen Spritezeile neu gesetzt werden, da
diese  ja einen anderen Text enthält als
die vorherige:                          
   ldy isline   ;Zgr.-Index in Y holen  
   ldx pointer,y;Zgr.-Basiswert aus Tab.
   stx $07f8    ;für Sprite0 setzen..   
   inx          ; ..um 1 erhöhen und    
   stx $07f9    ;   für Sprite1 setzen  
   inx          ;Ebenso für Sprites2-7  
   stx $07fa                            
   inx                                  
   stx $07fb                            
   inx                                  
   stx $07fc                            
   inx                                  
   stx $07fd                            
   inx                                  
   stx $07fe                            
   inx                                  
   stx $07ff                            
Auch hier verwenden wir ein Label um auf
eine Zeropageadresse zuzugreifen. "ISLI-
NE" steht für Adresse $F9, die  uns  als
Zwischenspeicher für einen Index auf die
Spritezeigerliste  dient.  In  letzterer
sind nun alle Spritepointerwerte für das
jeweils 0. Sprite einer jeden Zeile  un-
tergebracht.  Die Zeiger für die Sprites
von 1 bis 7 resultieren aus  dem  aufad-
dieren  von  1  auf  den jeweils letzten
Wert, was in unserer Routine  durch  die
INX-Befehle  durchgeführt wird. Die Zei-
gertabelle enthählt nun  die  Werte  von
$98 bis $F8, als Zeiger auf die Sprites,
die  im  Speicherbereich von $2600-$3FFF
liegen, jeweils in Achterschritten. Hier
eine Auflistung der kompletten Tabelle: 
pointer  .byte $98,$a0,$a8,$b0          
         .byte $b8,$c0,$c8,$d0          
         .byte $d8,$e0,$e8,$f0,$f8      
         .byte $98,$a0,$a8,$b0          
         .byte $b8,$c0,$c8,$d0          
         .byte $d8,$e0,$e8,$f0,$f8      
Wie Sie sehen, liegen hier die angespro-
chenen Pointer-Werte  zweimal  vor.  Das
ist  notwendig,  um  das  Umschalten der
Spritezeilen, wenn diese aus dem  oberen
Bildschirmrand  herausgescrollt  werden,
zu  vereinfachen.  Verschwindet  nämlich
die erste Spritezeile, deren Spritepoin-
ter von $98-$9F gehen, womit ihre  Spri-
tes  von  im Bereich von $2600 bis $2800
untergebracht sind, aus dem oberen Bild-
schirmrand, so muß die zweite Spritezei-
le (Pointer von $A0-$A7) von nun an  die
erste,  auf dem Bildschirm darzustellen-
de, Zeile sein,  wobei  wir  die  gerade
herausgescrollte Zeile als unterste Zei-
le verwenden müssen. Nachdem ihr Textin-
halt  in  die Sprites im Speicherbereich
von $2600 bis $2800  eingetragen  wurde,
müssen  nur  noch  die  Spritezeiger zum
richtigen Zeitpunkt auf diese Zeile  um-
geschaltet  werden.  Wir  haben nun noch
einen Weiteren Index, namens "SHOWLINE",
der in Zeropageadresse $F8 untergebracht
ist, und uns angibt, welche der  Sprite-
zeilen  als  Erstes  auf  dem Bildschirm
dargestellt werden muß. Zu Beginn  eines
neuen Rasterdurchlaufs wird "ISLINE" mit
diesem  Index initialisert. Dadurch, daß
unsere Tabelle nun nach dem Wert $F8 ein
zweites Mal von vorne beginnt, kann "IS-
LINE"  während  des Programmablaufs pro-
blemlos inkementiert werden, ohne  dabei
einen Zeilenüberlauf beachten zu müssen!
Kommen wir jedoch wieder zurück zu unse-
rer   IRQ-Routine.   Nachdem   die    Y-
Positionen,  sowie  die Spritezeiger ge-
setzt wurden müssen noch einige  verwal-
tungstechnische   Aufgaben  durchgeführt
werden:                                 
   clc          ;C-Bit für Add. löschen 
   ldy counter  ;Spr-Zeilen-Index holen 
   lda ad012+1,y;LO-Byte f.nächst.IRQ   
   adc softroll ;Scroll-Offs. add.      
   sta $d012    ;u.als nächst.IRQ setzen
   ror          ;C-Bit in Akku rotieren 
   and #$80     ;und isolieren          
   ora ad011+1,y;HI-Bit f.nächst.IRQ    
   ora $d011    ; sowie $D011 einodern  
   sta $d011    ; und setzen            
   dec $d019    ;VIC-IRQs freigeben     
   dec $d020    ;Rahmenfarbe zurücksetz.
In  diesem Teil der Routine wird nun der
folgende Raster-IRQ vorbereitet.  Hierzu
muß  zunächst  die Rasterzeile ermittelt
werden, in der  dieser  auftreten  soll.
Dafür  existieren zwei weitere Tabellen,
die die Basis-Rasterstrahlpositionen für
die  13  Interrupts  enthalten.  Hierbei
wird  es  wieder  etwas  kompliziert, da
nämlich auch  IRQs  an  Strahlpositionen
größer  als $FF ausgelöst werden müssen,
und wir deshalb das High-Bit der  auslö-
senden  Rasterstrahlposition, das in Bit
7 von $D011 eingetragen werden muß, mit-
berücksichtigen   müssen.  Auch  hierfür
müssen  wir  sehr  trickreich  vorgehen:
Zunächst  einmal  ermitteln wir das Low-
Byte der nächsten Rasterposition,  indem
wir  es,  mit  "COUNTER" als Index im Y-
Register, aus der Tabelle "AD012" ausle-
sen.  Auf  diesen  Wert muß nun noch der
momentane  Scrolloffset  aus  "SOFTROLL"
addiert  werden, um die tatsächliche Ra-
sterzeile zu erhalten. Der daraus resul-
tierende  Wert  kann  zunächst einmal in
$D012 eingetragen werden. Sollte bei der
Addition ein Öberlauf stattgefunden  ha-
ben,  also ein Wert größer $FF herausge-
kommen sein, so wurde dies im Carry-Flag
vermerkt. Selbiges rotieren wir mit  dem
ROR-Befehl  in den Akku hinein, und zwar
an Bitposition 7, wo auch  das  High-Bit
der  IRQ-Rasterstrahls  in $D011 untege-
bracht wird. Nachdem nun dieses High-Bit
durch  ein  "AND  $80"  isoliert  wurde,
odern  wir  aus  der Tabelle "AD011" das
High-Bit   der   Standard-Rasterposition
(ohne  Offset)  in  den Akku ein. Danach
müssen natürlich auch noch alle weiteren
Bits von $D011 in den  Akku  eingeknüpft
werden,   damit   wir   beim   folgenden
Schreibzugriff keine  Einstellungen  än-
dern.                                   
Nun   muß  nur  noch  das  ICR  des  VIC
gelöscht werden, damit der  nächste  IRQ
auch  auftreten  kann. Gleichzeitig wird
die Rahmenfarbe  wieder  heruntergezählt
(dadurch  entstehen  die  grauen Rechen-
zeit-Anzeigen im Bildschirmrahmen).     
Nun  noch  der  letzte  Teil  der   IRQ-
Routine,  der prüft, ob schon alle Spri-
tezeilen aufgebaut wurden:              
   inc isline   ;Akt. Zgr.-Zähler. +1   
   inc counter  ;Y-Pos-Zähler +1        
   lda counter  ;Y-Pos-Zähler holen     
   cmp #$0c     ; und mit 14 vgl.       
   bne endirq   ;Wenn ungl., dann weiter
   jsr movespr                          
   lda #$00                             
   sta $d015                            
mt:jsr maketext                         
ENDIRQ:                                 
   pla                                  
   tay                                  
   pla                                  
   tax                                  
   pla                                  
   rti                                  
Hier werden jetzt die Zähler und Indizes
für  den  nächsten  IRQ  voreingestellt,
sowie geprüft, ob schon alle  Spritezei-
len  dargestellt  wurden. Ist dies nicht
der Fall, so wird der IRQ durch  Zurück-
holen der Prozessorregister, gefolgt von
einem  RTI,  beendet.  Befinden  wir uns
allerdings schon im letzen der 13  IRQs,
die  pro  Rasterdurchlauf auftreten sol-
len, so fällt der Vergleich  von  "COUN-
TER"  mit dem Wert 14 negativ aus, womit
die Routine "MOVESPR" angesprungen wird.
Sie sorgt für das korrekten  Herabzählen
des Scrolloffsets, und erkennt, wenn die
oberste Spritezeile gerade aus dem Bild-
schirm gescrollt wurde:                 
MOVESPR:                                
   lda #$00    ;IRQ-Zähler init.        
   sta counter                          
   sec         ;C-Bit für Subtr. setzen 
   lda softroll;Scroll-Offs. holen      
   sbc #$01    ;Und 1 subtrahieren      
   sta softroll;neuen Scroll-Offs. abl. 
   bpl rollon  ;Wenn >0, dann weiter    
Wie  Sie  sehen,  wird hier zunächst der
IRQ-Zähler für den nächsten Rasterdurch-
lauf  auf  0 zurückgesetzt. Anschließend
subtrahieren wir den Wert 1 vom  Scroll-
Offset,  wodurch die Sprites im nächsten
Durchlauf eine Y-Position  höher  darge-
stellt  werden.  Durch  Ändern  des hier
subtrahierten Wertes in 2 oder 3  können
Sie übrigens auch die Scrollgeschwindig-
keit erhöhen. Gab es bei der Subtraktion
keinen  Unterlauf,  so  wird  zum  Label
"ROLLON"  (s.u.)  verzweigt.  Im anderen
Fall wurde durch den Scroll soeben  eine
ganze  Spritezeile  aus  dem  Bildschirm
gescrollt, weswegen wir die Zeile unten,
mit  einem  neuen  Text  belegt,  wieder
einfügen  müssen.  Zusätzlich müssen die
Spritepointer  in  anderer   Reihenfolge
ausgelesen  werden,  was  wir  durch das
Hochzählen  von  "SHOWLINE"   bewirken. 
Diese  Aufgaben werden in folgendem Pro-
grammteil ausgeführt:                   
   inc showline ;1.Spr-Zeilen-Ind. erh. 
   sec          ;C-Bit f. Subtr. setzen 
   lda showline ;Showline holen         
   sbc #$0d     ; und 13 subtr.         
   bmi noloop   ;Bei Unterlauf weiter   
   sta showline ;Sonst Wert abl.        
Da  unsere  Pointertabelle zwar doppelt,
aber nicht ewig lang ist, muß sie natür-
lich alle 13 Spritezeilen wieder zurück-
gesetzt werden, was durch den SBC-Befehl
geschieht. Erzeugte die Subtraktion  ein
negatives  Ergebnis, so sind wir noch in
einer Zeile kleiner als 13, und der  er-
haltene  Wert  wird ignoriert. Im andern
Fall haben  wir  soeben  die  Mitte  der
Pointerliste  erreicht,  ab  der  ja die
selben Werte stehen wie am  Anfang,  und
wir  können  "SHOWLINE"  wieder  auf den
erhaltenen Wert (immer 0) zurücksetzen. 
Den nun folgenden Routinenteil,  der  ab
dem  Label  "NOLOOP" beginnt, möchte ich
Ihnen  nur  der  Vollständigkeit  halber
hier  auflisten.  Er prüft, ob die Lauf-
schrift, die an dem Label "ESTEXT" abge-
legt ist, schon zu Ende gescrollt wurde.
Wenn ja, so wird in diesem Fall der Zei-
ger "TPOINT", der auf das erste  Zeichen
der als nächstes darzustellenden Sprite-
zeile zeigt, wieder mit  der  Startadra-
dresse  des  Textes ("ESTEXT") initiali-
siert:                                  
NOLOOP:                                 
   lda tpoint                           
   cmp #<estext+($34*$18)               
   bne continue                         
   lda tpoint+1                         
   cmp #<estext+($34*$18)               
   bne continue                         
   lda #$00                             
   sta showline                         
   lda #<estext                         
   sta tpoint+0                         
   lda #>estext                         
   sta tpoint+1                         
    (Anm.d.Red.: Bitte wählen Sie nun   
     den zweiten Teil des IRQ-Kurses    
         aus dem Textmenu aus.)         
      Fortsetzung IRQ-Kurs, Teil 10     
----------------------------------------
Es  folgt  nun  der Programmteil mit dem
Label "CONTINUE",  der  von  der  obigen
Routine  angesprungen wird. Hier kümmern
wir uns wieder um den Scrolloffset, des-
sen  Wert ja noch negativ ist, da wir im
ersten Teil  der  MOVESPR-Routine  durch
die Subtraktion einen Unterlauf des Zäh-
lers erzeugt hatten. Damit Sie hier  nun
auch  Werte  größer  1 einsetzen können,
womit der Scroll  schneller  durchläuft,
wird  "SOFTROLL"  nicht  wieder  mit $17
vorinitialisiert, sondern  wir  addieren
auf  das  Ergebnis  der  Subtraktion den
Offset, der zwischen  zwei  Rasterinter-
rupts  liegt,  $18  (=dez. 24), auf. Der
resultierende Wert wird dann  wieder  in
Softroll  abgelegt. Auf diese Weise wer-
den  also  auch  Scrollwerte  größer   1
berücksichtigt.  War  das  Ergebnis  der
Subtraktion z.B. -2, so wird  "SOFTROLL"
auf 22 zurückgesetzt, womit der Öberlauf
abgefangen  wird,  und  der Scrolleffekt
flüssig weiterläuft:                    
CONTINUE:                               
   clc         ;C-Bit f.Add. löschen    
   lda softroll;SOFTROLL laden          
   adc #$18    ;24 addieren             
   sta softroll;und wieder ablegen      
   lda #$20    ;Op-Code für "JSR"       
   sta mt      ;in MT eintragen         
Besonders  trickreich  ist  die LDA-STA-
Folge am Ende dieses Programmteils.  Wir
tragen hier den Wert $20, der dem Assem-
bler-Opcode   des   "JSR"-Befehls   ent-
spricht,  in das Label "MT" ein. Letzte-
res   befindet   sich    innerhalb   der
MOVIEIRQ-Routine,  und  zwar vor dem Be-
fehl  "JSR  MAKETEXT".   Die   MAKETEXT-
Routine baut eine Spritezeile auf, indem
Sie  die  Zeichendaten  dieser Zeile von
dem Zeichensatz bei $0800 in die zu  be-
nutzenden  Sprites  einkopiert.  Da dies
nicht  direkt   zu   unserem   Kursthema
gehört, möchte ich auch nicht weiter auf
diese  Routine eingehen. Wichtig zu wis-
sen ist nur,  daß  die  MOVESPR-Routine,
nachdem  sie  erkannt hat, daß eine neue
Spritezeile aufgebaut  werden  muß,  die
MOVIEIRQ-Routine derart modifiziert, daß
im  nächsten  IRQ  die  MAKETEXT-Routine
angesprungen  wird.  Innerhalb  selbiger
existiert  dann eine weitere Befehlsfol-
ge, die den Wert $2C in das  Label  "MT"
schreibt.  Selbiger  Wert ist der Opcode
für den Assemlerbefehl "BIT".  In  allen
folgenden IRQs arbeitet der Prozessor an
diesem  Label also immer den Befehl "BIT
MAKETEXT" ab, der eigentlich keine Funk-
tion  beinhaltet, sondern lediglich ver-
hindern soll, daß  die  Maketext-Routine
angesprungen  wird.  Erst,  wenn MOVESPR
erkannt hat, daß eine  neue  Spritezeile
aufgebaut  werden  muß,  ändert  sie den
Befehl wieder in "JSR MAKETEXT"  um,  so
daß  die  Zeile  im  nächsten IRQ wieder
automatisch neu berechnet wird.  Dieser,
zugegebenermaßen etwas umständliche, Weg
des  Routinenaufrufs  wurde  gewählt, da
MAKETEXT  recht  lange  (insgesamt  etwa
einen  Rasterdurchlauf)  braucht, um die
Spritezeile  aufzubauen.  Damit  Sie  in
dieser  Zeit die Raster-IRQs nicht blok-
kiert, muß sie auf  diesem  Weg  benutzt
werden.  Während  ihres  Ablaufs erlaubt
sie auch weitere IRQs, so daß  Sie  wäh-
rend  der  Darstellung  des nächsten Ra-
sterdurchlaufs, immer zwischen den Spri-
tepositionierungen durch den IRQ, ablau-
fen kann.                               
Kommen  wir nun zum letzten Teil der MO-
VESPR-Routine, dem Label "ROLLON".  Sel-
biges wird ja ganz am Anfang der Routine
angesprungen,  wenn  kein  Unterlauf von
"SOFTROLL"  stattfand,  und  somit  ohne
jegliche  Änderung weitergescrollt wird.
Sie setzt den Spritepointerindex  "ISLI-
NE"  zurück  auf  den Wert in "SHOWLINE"
und bereitet den ersten Raster-IRQ  vor,
der  ja  immer in der Rasterzeile "SOFT-
ROLL" aufzutreten hat.                  
ROLLON:                                 
   lda showline;ISLINE mit Inhalt von   
   sta isline  ; SHOWLINE init.         
   lda softroll;Scroll-Offset holen     
   sta $d012   ;und als nächsten IRQ-   
   lda $d011   ;Auslöser setzen, dabei  
   and #$7f    ;Hi-Bit löschen u.       
   ora #$08    ;gleichz. 24-Zeilen-     
   sta $d011   ;Darst. einschalten      
   rts                                  
Beim Festlegen des Wertes "SOFTROLL" als
nächste IRQ-Rasterzeile, muß die Routine
auch das  High-Bit,  dieser  Rasterposi-
tion,  in  $D011  löschen.  Gleichzeitig
schaltet sie  die  25-Zeilen-Darstellung
durch  Setzen  des  3. Bits dieses Regi-
sters wieder ein.  Dies  ist  eigentlich
eine  Aufgabe,  die für das Beispielpro-
gramm  "MOVIE.1"  irrelevant  ist.  Wohl
aber  für  "MOVIE.2", in dem wir den Mo-
vie-Scroller über einen  Bildschirm  mit
abgeschaltetem  oberen  und unteren Rand
laufen lassen. Wie Sie wissen, muß  dazu
in  Rasterzeile  $FA die Darstellung von
25  auf  24  Zeichen-Zeilen  herunterge-
schaltet werden, und danach, vor nochma-
ligem Erreichen von $FA, wieder  auf  25
Zeilen  zurück, was hiermit durchgeführt
wird. Da MOVESPR immer im 13.  Interrupt
aufgerufen  wird,  und  dieser immer nur
innerhalb  der  Rasterzeilen   $10C-$126
auftreten   kann,   befinden   wir   uns
tatsächlich schon unterhalb der  Raster-
zeile  $FA,  womit die Änderung zum kor-
rekten Zeitpunkt durchgeführt wird.     
3) GLEICHZEITIGES ÜFFNEN DES BILDSCHIRMS
Wie schon angesprochen, öffnet das  Pro-
grammbeispiel  "MOVIE.2" zusätzlich noch
den oberen und  unteren  Bildschirmrand,
damit  die  Sprites  in  voller Bildhöhe
über den Bildschirm laufen. Vom  Prinzip
her  ist dies ein recht einfaches Unter-
fangen, das wir auch schon ausgiebig  in
diesem  Kurs  besprochen  und  angewandt
haben. Durch unsere Scrollroutine stellt
sich uns jedoch ein kleines  Problem  in
den Weg: da unsere Raster-IRQs durch den
Scrolleffekt  immer in verschiedenen Ra-
sterzeilen aufzutreten  haben,  und  wir
nicht  immer  genau sagen können, ob nun
der 11. oder 12. Rasterinterrupt  gerade
ausgelöst  wurde, bevor der Rasterstrahl
die Position $FA erreicht hat,  wird  es
schwierig  die Scroll-Routine so abzuti-
men, daß sie genau  an  dieser  Position
die  Bildschirmdarstellung ändert. Würde
man versuchen durch  Verzögerungsschlei-
fen  die richtige Position abzutimen, so
wäre das mit einem erheblichen  Program-
mieraufwand  verbunden.  Deshalb greifen
wir zu einem kleinen  Trick:  Die  INIT-
Routine  von  "MOVIE.2"  wurde  um  eine
kleine Änderung erweitert. Zunächst las-
sen  wir hier den Prozessor, durch stän-
diges Auslesen und Vergleichen  der  ak-
tuellen  Rasterposition, auf Rasterzeile
$FA warten. Ist diese erreicht, so  ini-
tialiseren wir Timer A von CIA-2 mit dem
Wert  $4CC7  und starten ihn. Da der Ra-
sterstrahl immer exakt so viele  Taktzy-
klen  braucht, um einmal über den ganzen
Bildschirm zu laufen, erzeugt diese  CIA
immer  genau  in Rasterzeile $FA, in der
sie gestartet  wurde,  einen  NMI.  Weil
dieser  Vorrang vor dem IRQ hat, wird er
selbst dann ausgelöst, wenn  gerade  ein
Raster-IRQ  auftritt oder in Bearbeitung
ist. Die NMI-Routine nimmt nun  die  er-
forderliche  Änderung  von $D011 vor, um
den Rand abzuschalten  (Bit  3  löschen)
und  kehrt  anschließend  sofort  wieder
zurück, ggf. sogar in einen vom NMI  un-
terbrochenen  Raster-IRQ,  der dann ganz
normal  zu  Ende  bearbeitet  wird.  Das
Zurücksetzen  von $D011 auf die alte 25-
Zeilen-Darstellung, wird dann wieder von
der  MOVESPR-Routine  durchgeführt,  wie
wir oben ja schon gesehen hatten.       
Um  die  NMIs  zu  erzeugen  springt die
INIT-Routine von "MOVIE.2" auf eine  Un-
terrountine  zum  Initialiseren  des Ti-
mer-NMIs. Wie  das  funktioniert  hatten
wir  schon ganz zu Anfang des Interrupt-
Kurses bespochen. Hier die  Routine,  um
Ihnen  den Vorgang wieder ins Gedächtnis
zu rufen. Wie auch  schon  für  den  IRQ
springen  wir  diesmal  nicht  über  den
Soft-NMI-Vektor bei $0318/$0319, sondern
über den Hardvektor bei  $FFFA/$FFFB  in
den NMI ein:                            
NMIINIT:                                
   lda #<nmi ;Startadresse NMI-Routine  
   sta $fffa ; in die Hardvektoren bei  
   lda #>nmi ;$FFFA/$FFFB eintragen     
   sta $fffb                            
   lda #$c7  ;Timer A mit dem Zählwert  
   sta $dd04 ; $4CC7 initialiseren      
   lda #$4c                             
   sta $dd05                            
   lda #$fa  ;Rasterz. $FA in Akku      
WAIT:                                   
   cmp $d012 ;m. akt. Raster vgl.       
   bne wait  ;ungl. also weiter         
   lda #$11  ;Gleich, also Timer A      
   sta $dd0e ; starten                  
   lda #$81  ;Timer-A-NMIs in ICR       
   sta $dd0d ; erlauben                 
   rts                                  
Das war eigentlich schon alles. Von  nun
an  löst  Timer  A  von CIA-B alle $4CC7
Taktzyklen einen NMI aus. Da der Raster-
strahl  immer  genau diese Anzahl Zyklen
benötigt, um ein  ganzes  Mal  über  den
Bildschirm zu laufen, tritt der NMI also
immer  in  Rasterzeile  $FA ein, auf die
wir vor Starten des Timers gewartet  ha-
ben.  Die  NMI-Routine  selbst ist recht
kurz und sieht folgendermaßen aus:      
NMI:  pha       ;Akku retten            
      lda $d011 ;$D011 holen            
HILO: ora #$00  ;7.Bit Rasterpos. setzen
      and #$F7  ;3.Bit löschen          
      sta $d011 ;$D011 zurückschreiben  
      bit $dd0d ;NMIs wieder erlauben   
      pla       ;Akku zurückholen       
      rti       ; und Ende              
Da die NMI-Routine  lediglich  den  Akku
benutzt,  brauchen  wir auch ausschliße-
lich nur diesen auf dem Stapel  zu  ret-
ten.  Danach  wird  der Inhalt von $D011
gelesen. Der nun folgende ORA-Befehl hat
die Aufgabe eine ggf. gesetztes High-Bit
der  Rasterstrahlposition  des  nächsten
Raster-IRQs  zu  setzen, damit wir durch
unsere Manipulation von $D011 nicht ver-
sehentlich  die, schon gesetzte, nächste
Raster-IRQ-Position  verändern.   Hierzu
wurde die MOVIEIRQ-Routine von "MOVIE.2"
derart  abgeändert,  daß  Sie  die High-
Position  für  den  nächsten  Raster-IRQ
nicht  nur  in  $D011  schreibt, sondern
auch im Label "HILO+1"  ablegt,  so  daß
das  Argument  des  ORA-Befehls zwischen
$00 und $80 variiert und immer den rich-
tigen  ODER-Wert  enthält.  Anschließend
folgt nun ein AND-Befehl zum Löschen des
3. Bits, womit wir mit dem  Ablegen  des
Wertes  die  24-Zeilen-Darstellung  ein-
schalten. Durch  den  BIT-Befehl  führen
wir   einen  Lesezugriff  auf  das  ICR-
Register von CIA-2 aus, womit wir selbi-
ges  Löschen,  und dadurch das Auftreten
und Melden des  nächsten  NMIs  ermögli-
chen.  Hiernach  wird  dann nur noch der
gerettete Akkuinhalt zurückgeholt, bevor
wir den NMI mittels  "RTI"  beenden.  Da
die  MOVESPR-Routine  nun automatisch zu
einer späteren Position Bit 3  in  $D011
wieder   setzt,  funktioniert  der  NMI-
Border-Trick also auch wieder im folgen-
den  Rasterdurchlauf! Mit Hilfe des hier
benutzen  NMI-Tricks  können  Sie  quasi
zwei   Raster-Intterrupts   gleichzeitig
ablaufen lassen,  was  auch  für  andere
Rasterroutinen sehr hilfreich sein kann.
                                 (ih/ub)
                IRQ-KURS                
     "Die Hardware ausgetrickst..."     
                (Teil 11)               
----------------------------------------
Herzlich  Willkommen zum elften Teil un-
seres IRQ-Kurses. Wie schon in den letz-
ten Kursteilen, werden wir uns auch die-
sen Monat mit  der  Spriteprogrammierung
der besonderen Art beschäftigen. Es soll
um  einige  Tricks  gehen, mit denen man
den Bildschirm  auch  über  alle  Ränder
hinaus  mit Gafikdaten füllen kann. Die-
sen Effekt nennt man "ESCOS", der  Dreh-
und  Angelpunkt  für die Rastertricks in
diesem Kursteil sein wird.              
1) UNSER ZIEL                           
In  früheren  Kursteilen  hatten  wir ja
schon einmal besprochen, auf welche  Art
und Weise oberer und unterer, sowie lin-
ker  und  rechter  Bildschirmrand  abge-
schaltet  werden.  Wir  hatten weiterhin
gelernt, daß in  diesen  Bereichen  aus-
schließlich  Sprites  auftauchen können,
die  durch  die  abgeschalteten   Ränder
sichtbar  sind, wo sie sonst von letzte-
ren überdeckt werden. Wir hatten  ebenso
eine  Möglichkeit  kennengelernt,  beide
Ränder, die horizontalen und vertikalen,
gleichzeitig abzuschalten, wobei wir auf
sehr exaktes Timing achten  mussten,  da
das  Abschalten  der  linken und rechten
Bildschirmbegrenzung eine hohe  Genauig-
keit  erforderte. Desweiteren wird Ihnen
aus dem letzten Kursteilen bestimmt noch
die Sprite-Multiplexer-Routine in Kombi-
nation  mit  einem Moviescroller im Kopf
sein, mit der wir 104 Sprites gleichzei-
tig auf den Bildschirm brachten. Wie Sie
sich  vielleicht erinnern, waren wir da-
bei an gewisse Grenzen gebunden. So  war
z.B.  zwischen  zwei  Spritezeilen immer
ein Abstand von ca. 2  Rasterzeilen  er-
forderlich,  die  wir benötigten, um die
Spritepointer   sowie   die   neuen   Y-
Positionen  der  Sprites zu setzen. Des-
weiteren mussten  wir  ein  komplizierte
Timingroutine   benutzen,  die  zwischen
Charakterzeilen (jede achte Rasterzeile,
in der der VIC den  Prozessor  für  eine
Dauer von 42 Taktzyklen anhält) und nor-
malen Rasterzeilen zu unterscheiden hat-
te.  In dieser Folge unseres Kurses wol-
len wir nun all diese Komponenten mitei-
nander  verbinden  und  eine Möglichkeit
kennenlernen, Timingprobleme durch  Cha-
rakterzeilenberücksichtigung,  zu  umge-
hen. Das Endergebnis wird  ein  flächen-
deckend  (!)  mit Sprites belegter Bild-
schirm sein, wobei weder Leerräume  zwi-
schen  den  Sprites,  noch  im  gesamten
Bildschirmrahmen zu sehen  sein  werden!
Wir  werden also über eine Grafik verfü-
gen, die, ähnlich einem Fernsehbild, die
gesamte Bildröhrenfläche ausfüllt!      
2) ERSTES PROBLEM: DAS TIMING           
Kommen wir gleich zum Kern  dieses  Kur-
steils, dem Timing-Problem, das sich uns
entgegenstellt. Möchten wir nämlich alle
Bildschirmränder  abschalten und gleich-
zeitig auch noch die  Sprites  multiple-
xen,  so können wir programmtechnisch in
"Teufels Küche"  gelangen.  Wir  müssten
berücksichtigen, daß im sichtbaren Bild-
schirmfenster  alle  acht   Rasterzeilen
eine  Chakaterzeile auftritt, gleichzei-
tig müsste in jeder Rasterzeile der lin-
ke  und rechte Bildschirmrand abgeschal-
tet, sowie in jeder 21. Rasterzeile  die
Sprites  neu  positioniert werden. Dabei
stellen vor  allem  die  Charakterzeilen
ein  großes Problem dar. Wir müssten un-
terscheiden  zwischen  Rasterstrahlposi-
tionen im oberen und unteren Bildschirm-
rand und innerhalb des sichtbaren  Bild-
schirmfensters, und zudem noch für letz-
teren Fall  berücksichtigen,  wann  sich
der  VIC  gerade  in einer Chakaterzeile
befindet und wann in einer normalen  Ra-
sterzeile.  Dieses  Problem stellte sich
bei unseren  Raster-Effekten  nun  schon
häufiger  in den Weg, wobei wir es meist
durch einen einfachen Trick umgingen: in
der Regel benutzten wir in solchen  Fäl-
len eine FLD-Routine, die die Charakter-
zeile  im  fraglichen  Bildschirmbereich
einfach  nach unten "wegdrückte", so daß
wir in jeder Rasterzeile  63  Taktzyklen
zur  Verfügung hatten und somit ein ein-
heitliches Timing programmieren konnten.
Wir könnten diesen Effekt hier nun  auch
anwenden,  jedoch  gibt  es speziell für
diese  Anwendung  einen  weiteren,  viel
einfacherern  Trick, die Charakterzeilen
zu umgehen: da wir ja den gesamten Bild-
schirm  mit Sprites füllen möchten, kön-
nen wir davon ausgehen, daß wir  keiner-
lei  Hintergrundgrafik, bzw. Textzeichen
benötigen. Es gibt nun einen Trick,  den
wir  noch nicht kennengelernt haben, mit
dem wir  die  Charakterzeilen  ganz  ab-
schalten  können,  so  daß  nur noch die
Sprites  dargestellt  werden.  Wie  fast
immer  ist Register $D011, ein Dreh- und
Angelpunkt  der   VIC-Trickeffekt-Kiste,
für diesen Trick verantwortlich. Mit Bit
4  dieses  Registers  können wir nämlich
den gesamten Bildschirm abschalten,  was
einer  Deaktivierung  des  VICs  gleich-
kommt. Er wird durch das Löschen  dieses
Bits  veranlasst, auf dem gesamten Bild-
schirm nur noch die  Rahmenfarbe  darzu-
stellen,  und keine Charakterzeilen mehr
zu lesen.  Die  Sprites  bleiben  jedoch
weiterhin  aktiv,  obwohl  sie jetzt un-
sichbar sind, da sie jetzt auf  dem  ge-
samten  Bildschirm  mit dem Rahmen über-
deckt werden. Man könnte das Setzen  und
Löschen  des  4. Bits von Register $D011
quasi mit dem Üffnen und Schließen eines
Vorhangs vor einem Fenster  vergleichen:
Eine  Fliege  (oder  unser  Sprite), die
sich auf  der  Fensterscheibe  befindet,
ist   bei  geschlossenem  Vorhang  nicht
sichtbar. Ist letzterer jedoch geöffnet,
so sieht man  die  Fliege,  solange  sie
sich  im  sichtbaren Bereich des Fenster
bewegt, und  nicht  unter  den  seitlich
aufgerafften Vorhängen verschwindet.    
Nun,  selbst wenn die Sprites noch aktiv
sind, so nutzen Sie uns  herzlich  wenig
wenn  sie  unsichtbar sind. Deshalb gilt
es mal wieder, den VIC auszutricksen, um
das  gewünschte  Ergebnis  zu  erhalten.
Dies gestaltet sich in diesem Fall recht
einfach: Zunächst einmal schalten wir an
einer  Rasterposition,  an  der  der VIC
normalerweise den  oberen  oder  unteren
Bildschirmrand  zeichnet,  den  gesamten
Bildschirm durch Löschen von Bit  4  aus
Register $D011, ab. Erreicht der Raster-
strahl nun Rasterzeile $30, an  der  ei-
gentlich das sichtbare Bildschirmfenster
beginnt,  so prüft der VIC, ob der Bild-
schirm nun ein- oder ausgeschaltet  ist.
In  letzterem  Fall deaktiviert er seine
Zeichenaufbau-Schaltkreise bis zum näch-
sten  Erreichen  dieser  Rasterposition,
womit er  keine  einzige  Charakterzeile
mehr  liest. Anstelle dessen zeigt er in
den folgenden Rasterzeilen nur noch  die
Farbe des Bildschirmrahmens an. Wenn wir
diesen  jedoch  mit  Hilfe einer Border-
Routine bei $FA abschalten, so - oh Wun-
der  - zeigt uns der VIC den Bildschirm-
hintergrund, auf dem sich auch die Spri-
tes  herumtollen  dürfen!  Wie bei jedem
Effekt, bei dem der VIC etwas  tut,  was
er  sonst nicht tun kann, erscheint hier
dann  wieder  der  Inhalt  der   letzten
Adresse des VIC-Speichers (normalerweise
$3FFF)  in Schwarz auf Hintergrundfarbe.
Durch Schreiben des Wertes  0  in  diese
Speicherzelle  können  wir natürlich die
Schwarzen Streifen auch  abschalten  und
damit  nur die Hintergrundfarbe anzeigen
lassen.                                 
Um  diese  Vorgehensweise  nun besser zu
erläutern haben wir natürlich wieder ein
Programmbeispiel auf Lager, das ich  Ih-
nen  nun auflisten möchte. Sie finden es
auf dieser MD auch als fertig ausführba-
res  File  mit dem Namen "SPRITES-ONLY",
daß Sie wie immer mit ",8,1"  laden  und
durch ein "SYS4096" starten müssen.     
Kommen wir also zur  Initialiserungsrou-
tine  des  Beispiels,  die  bei  Adresse
$1000 beginnt:                          
INIT:                                   
    sei        ;IRQs sperren            
    lda #$7f   ;Alle CIA-IRQs abschalten
    sta $dc0d  ; (CIA1)                 
    sta $dd0d  ; (CIA2)                 
    bit $dc0d  ;CIA1-ICR löschen        
    bit $dd0d  ;CIA2-ICR löschen        
    lda #$01   ;VIC-Hintergrundstriche  
    sta $3fff  ; auf 1 setzen           
    jsr sprinit;Sprites initialiseren   
    jsr irqinit;IRQ initialiseren       
    lda #$35   ;ROMs abschalte          
    sta $01                             
    cli        ;IRQs erlauben           
spc:lda #$7f   ;Auf SPACE-Taste         
    sta $dc00  ; warten...              
    lda $dc01                           
    cmp #$ef                            
    bne spc                             
    sei        ;IRQs sperren            
    lda #$37   ;ROM wieder einschalten  
    sta $01                             
    jmp $fce2  ;und RESET auslösen      
Alles in allem für uns  keine  besondere
Initialisierung.  Wichtig  sind noch die
Routinen  "SPRINIT"  und  "IRQINIT".  In
ersterer  initialisieren  wir  lediglich
die Sprite-Positionen, sowie  die  Spri-
te-Pointer  und schalten alle acht Spri-
tes ein. Letzere  Routine  ist  für  das
Korrekte   initialisieren  unseres  IRQs
zurständig und sieht folgendermaßen aus:
IRQINIT:                                
  lda #<bordirq ;IRQ-Vektor bei         
  sta $fffe     ; $FFFE/$FFFF           
  lda #>bordirq ;auf "BORDIRQ" verbiegen
  sta $ffff                             
  lda #$1b      ;Wert für $D011 mit gel.
  sta $d011     ; High-Bit f. Rasterpos.
  lda #$fa      ;Ersten Raster-IRQ bei  
  sta $d012     ; Zeile $FA auslösen    
  lda #$81      ;VIC-Raster-IRQs        
  sta $d01a     ; erlauben              
  dec $d019     ;VIC-ICR ggf. löschen   
  rts                                   
Wie Sie sehen, aktivieren wir hier einen
Raster-IRQ für Rasterzeile $FA, der  bei
Auftreten   die  Routine  "BORDIRQ"  an-
springt, wo sich eine  ganz  gewöhnliche
Routine  zum  Abschalten  des oberen und
unteren Bildschirmrandes befindet.  Hier
das Listing dieser Routine:             
BORDIRQ:                                
  pha       ;Prozessorregs. retten      
  txa                                   
  pha                                   
  tya                                   
  pha                                   
  lda $d011 ;24-Zeilen-Darstellung      
  and #$77  ; einschalten               
  sta $d011                             
  lda #$28  ;nächster IRQ bei Zeile $28 
  sta $d012                             
  dec $d019 ;VIC-ICR löschen            
  lda #<soff;Routine für nächsten IRQ   
  sta $fffe ; "SOFF" in IRQ-Vektor      
  lda #>soff; eintragen                 
  sta $ffff                             
  pla       ;Prozessorregs. wieder vom  
  tay       ; Stapel holen und IRQ      
  pla       ; beenden                   
  tax                                   
  pla                                   
  rti                                   
Wir schalten hier also an  der  üblichen
Position  auf  24-Zeilen-Darstellung he-
runter, damit der VIC vergisst, den obe-
ren und unteren Bildschirmrand zu zeich-
nen. Gleichzeitig  wird  ein  neuer  IRQ
initialisiert,  der  bei  Erreichen  von
Rasterzeile $28 (acht  Rasterzeilen  vor
Beginn  des  sichtbaren  Bildschirmfens-
ters)  die  Routine  "SOFF"   anspringen
soll.  Diese  Routine  übernimmt nun die
Aufgabe, die Darstellung der  Charakter-
zeilen zu verhindern:                   
soff:                                   
  pha       ;Prozessorregs. retten      
  txa                                   
  pha                                   
  tya                                   
  pha                                   
  lda $d011 ;Bild ausschalten (durch    
  and #$6F  ; ausmaskieren von Bit4)    
  sta $d011                             
  lda #$32  ;nächster IRQ bei Raster-   
  sta $d012 ; zeile $32                 
  dec $d019 ;VIC-ICR löschen            
  lda #<son ;Nächster IRQ soll auf      
  sta $fffe ; Routine "SON" springen    
  lda #>son                             
  sta $ffff                             
  pla       ;Prozessorregs. wieder vom  
  tay       ; Stapel holen und IRQ      
  pla       ; beenden                   
  tax                                   
  pla                                   
  rti                                   
Wie Sie sehen eine recht einfache Aufga-
be:   durch   eine  AND-Verknüpfung  des
$D011-Inhalts mit dem Wert $6F wird ein-
fach    das   4.Bit   dieses   Registers
gelöscht. Gleichzeitig löschen wir dabei
Bit 7, das ja das High-Bit  der  Raster-
strahlposition  angibt,  womit  wir also
auch dieses Bit für  den  folgenden  Ra-
ster-IRQ  bei Zeile $32 vorbereitet hät-
tem. Die Routine, die hier  abgearbeitet
werden  soll,  heisst  "SON" und ist für
das  Wiedereinschalten  des  Bildschirms
verantwortlich.  Da sich Rasterzeile $32
im sichtbaren Fenster befindet wäre  so-
mit  der  VIC  überlistet und soweit ge-
bracht,  daß  er  keine  Charakterzeilen
mehr  liest,  geschweige denn darstellt.
Gleichzeitig schaltet diese Routine wie-
der auf die 25-Zeilen-Darstellung zurück
(Bit  3  von  $D011 muß gesetzt werden),
damit das Abschalten des Borders auch im
nächsten Rasterstrahldurchlauf  funktio-
niert:                                  
SON:                                    
  pha          ;Prozessorregs. retten   
  txa                                   
  pha                                   
  tya                                   
  pha                                   
  lda $d011    ;Bild und 25-Zeilen-     
  ora #$18     ; Darstellung einschalten
  sta $d011                             
  lda #$fa     ;nächster IRQ wieder bei 
  sta $d012    ; Zeile $FA              
  dec $d019    ;VIC-ICR löschen         
  lda #<bordirq;Wieder "BORDIRQ" in     
  sta $fffe    ; IRQ-Vektor eintragen   
  lda #>bordirq                         
  sta $ffff                             
  pla       ;Prozessorregs. wieder vom  
  tay       ; Stapel holen und IRQ      
  pla       ; beenden                   
  tax                                   
  pla                                   
  rti                                   
Nachdem Register $D011 auf den gewünsch-
ten  Wert  zurückgesetzt wurde, wird der
IRQ wieder für die Routine "BORDIRQ" bei
Rasterzeile $FA vorbereitet, womit  sich
der  Kreis  schließt und unser Programm-
beispiel komplett ist.                  
3) "ESCOS" - EINEN SCHRITT WEITER       
Nachdem  wir  nun  unser  Timing-Problem
gelöst,  und  die  störenden  Charakter-
Zeilen  aus dem Weg geräumt haben, möch-
ten wir wieder zu  unserer  eigentlichen
Aufgabenstellung zurückkehren: dem bild-
schirmfüllenden Darstellen von  Sprites.
Hierzu  müssen  wir,  nachdem oberer und
unterer Bildschirmrand, sowie  die  Cha-
rakterzeilen     abgeschaltet    wurden,
zusätzlich auch noch die  Bildschirmrän-
der  links  und rechts deaktivieren. Das
Funktionsprinzip einer solchen  Sidebor-
derroutine  sollte  Ihnen noch aus einem
der ersten Kursteile im Kopf sein: durch
rechtzeitiges Umstellen von 40- auf  38-
Spaltendarstellung  und  zurück tricksen
wir den VIC nach dem selben Prinzip aus,
wie wir es bei  den  horizontalen  Bild-
schirmrändern  tun.  Dadurch  aber,  daß
sich der Rasterstrahl  horizontal  recht
schnell  bewegt,  kommt es dabei auf ein
höchst exaktes Timing an. Einen  Taktzy-
klus  zu  früh oder zu spät funktioniert
die Routine schon nicht mehr.  Wenn  man
das  Ganze  nun  zusätzlich noch mit dem
Üffnen des oberen  und  unteren  Randes,
sowie   dem  Abschalten  der  Charakter-
Zeilen und einem Sprite-Multiplexer kom-
binieren  muß, so könnte man meinen, daß
dies  eine  recht  programmieraufwendige
Sache werden kann. Doch keine Panik, die
Umsetzung ist einfacher als Sie glauben.
Am Besten sehen Sie sich erst einmal das
Demoprogramm  "ESCOS1"  an.  Es wird wie
üblich geladen und mit  SYS4096  gestar-
tet,  und  zeigt dann einen vollends mit
Sprites gefüllten Bildschirm - und  zwar
über alle Ränder hinaus! Um so etwas nun
sebst   zu   programmieren,  werden  wir
zunächst auf die organisatirischen  Pro-
bleme und deren Lösung eingehen:        
Als  Erstes  müssen  Sie davon ausgehen,
daß wir keine IRQ-Routine  im  eigentli-
chen  Sinne  programmieren  werden. Auf-
grund des exakten Timings, das  zum  Ab-
schalten  des  linken und rechten Randes
notwendig ist, muß der Prozessor  nahezu
während  des  gesamten  Rasterdurchlaufs
damit  beschäftigt  sein,  die  richtige
Rasterposition  abzuwarten,  um zwischen
der 38- und  40-Zeilen-Darstellung  hin-
und  herzuschalten.  Dadurch  wird ledi-
glich ein einziger IRQ pro  Rasterdurch-
lauf  aufgerufen,  nämlich direkt zu An-
fang desselben, in Rasterzeile 0. Dieser
IRQ muß nun, auf die uns schon  bekannte
Art  und  Weise,  "geglättet" werden, so
daß kein einziger Taktzyklus Unterschied
zum vorherigen Rasterdurchlauf  besteht.
Ab  dann  (durch  das Glätten, das 2 Ra-
sterzeilen dauert also ab Zeile  2)  be-
ginnt  der Prozessor damit in jeder ein-
zelnen Rasterzeile den  Rand  abzuschal-
ten.  Dies  geschieht  dadurch,  daß wir
nach jedem  Umschalten  der  Spaltendar-
stellung  genau  am  Öbergangspunkt zwi-
schen sichtbarem  Bildschirmfenster  und
-rahmen,  exakt 63 Taktzyklen verzögern,
bevor dieser Vorgang wiederholt wird. So
zumindest sähe es aus,  wenn  wir  keine
Sprites darzustellen hätten. Da wir dies
jedoch tun, müssen wir weiterhin berück-
sichtigen, daß der  VIC  zum  Darstellen
der  Sprites den Prozessor ebenso anhal-
ten muß, wie beim Lesen  der  Charakter-
zeilen,  um sich die Spritedaten aus dem
Speicher zu holen. Hierbei braucht er  3
Taktzyklen  für  das  erste  Sprite, und
dann jeweils 2 Zyklen für alle weiteren,
eingeschalteten Sprites. Da wir  alle  8
Sprites  benutzen,  müssen wir also noch
den Betrag 3+7*2=17 von  den  63  Zyklen
abziehen  und  erhalten  somit  exakt 46
Taktzyklen, die der  Prozessor  pro  Ra-
sterzeile "verbrauchen" muß.            
  (Anm. d. Red.: Bitte wählen Sie jetzt 
 den zweiten Zeil des IRQ-Kurses aus dem
               Textmenu.)               
Gleichzeitig müssen wir berücksichtigen,
daß alle 21 Rasterzeilen die  Y-Position
der Sprites um diesen Betrag hochgezählt
werden muß, damit sie auch untereinander
auf  dem Bildschirm erscheinen. Dies ist
glücklicherweise eine nicht allzu schwe-
re Aufgabe, da der VIC uns diesbezüglich
ein wenig entgegenkommt. Ändern wir näm-
lich die X-Position eines Sprites inner-
halb einer Rasterzeile, so hat dies eine
sofortige eine Wirkung auf die Spritepo-
sition: das Sprite erscheint  ab  dieser
Rasterzeile an der neuen X-Position. Mit
der  Y-Position  verhält es sich anders:
wird sie noch  während  das  Sprite  ge-
zeichnet wird geändert, so hat das keine
direkte  Auswirkung  auf  die restlichen
Spritezeilen. Der VIC zeichnet das Spri-
te stur zu Ende, bevor er die Y-Position
nocheinmal  prüft.  Das gibt uns die Mö-
glichkeit, die Y-Position schon im  Vor-
raus  zu  ändern, nämlich irgendwann in-
nerhalb der  21  Rasterzeilen,  die  das
Sprite  hoch  ist.  Setzen  wir die neue
Y-Position nun also genau  auf  die  Ra-
sterzeile  nach  der letzen Spritezeile,
so kümmert sich der VIC darum erst  ein-
mal gar nicht. Er zeichnet zunächst sein
aktuelles Sprite zu Ende, und wirft dann
erst  einen  Blick  in  das Y-Positions-
Register des Sprites. Da  er  dort  dann
eine  Position  vorfindet,  die  er noch
nicht überlaufen  hat,  glaubt  er,  das
Sprite  wäre  noch nicht gezeichnet wor-
den, woraufhin er unverzüglich  mit  dem
Zeichnen  wiederanfängt - und schon wäre
dasselbe  Sprite  zweimal  untereinander
auf  dem  Bildschirm  zu  sehen! Dadurch
können wir uns in der Rasterroutine  mit
der  Neupositionierung  Zeit lassen, und
selbige über zwei Rasterzeilen  verteilt
durchführen  (innerhalb einer Zeile wäre
auch gar nicht genug Zeit dafür).       
Um die  Bildschirmränder  unsichtbar  zu
machen  muß  sich  ein Teil unserer IRQ-
Routine um das Abschalten des oberen und
unteren Bildschirmrandes, sowie der Cha-
rakterzeilen  kümmern.  Wie  Sie im Pro-
grammbeispiel zuvor gesehen  haben,  ist
das  eigentlich eine recht einfache Auf-
gabe. Da wir zum Abschalten  der  Ränder
links und rechts jedoch ein hypergenaues
Timing  benötigen, wäre es recht aufwen-
dig für die Rasterzeilen  $FA,  $28  und
$32  eigene  IRQ-Routinen  zu schreiben,
ohne dabei das Timing für  die  Sidebor-
derabschaltung  damit  durcheinander  zu
bringen. Aus diesem Grund haben wir  uns
einen Trick ausgedacht, mit dem wir bei-
de Aufgaben quasi  "in  einem  Aufwasch"
bewältigen:  Wie  Sie  zuvor  vielleicht
schon bemerkt haben,  hatten  alle  IRQs
unserer  Beispielroutine eins gemeinsam:
Sie  erzielten  den  gewünschten  Effekt
durch  irgendeine Manipulation des Regi-
sters  $D011.  Warum  sollten  wir  also
nicht aus zwei eins machen, und generell
in  jeder Rasterzeile einen Wert in die-
ses Register schreiben?  Das  hilft  uns
zum  Einen  das  Verzögern  des  Raster-
strahls bis zum  Ende  der  Rasterzeile,
und  hat  zudem den angenehmen "Nebenef-
fekt" die horizontalen Bildschirmränder,
sowie   die   Charakterzeilendarstellung
quasi "automatisch" abzuschalten.       
Nach all dieser trockenen Theorie möchte
ich  Ihnen  den  Source-Code  zu unserer
ESCOS-Routine nun nicht mehr länger  vo-
renthalten.  Die Init-Routine werden wir
uns diesmal sparen, da sie nahezu ident-
lisch  mit  der des letzten Programmbei-
spiels ist. Wichtig für uns ist, daß Sie
den VIC darauf vorbereitet, einen IRQ in
Rasterzeile  0  auszulösen,  bei  dessen
Auftreten  der  Prozessor in die Routine
"SIDEBORD" zu springen hat. Selbige Rou-
tine  befindet sich an Adresse $1200 und
sieht folgendermaßen aus:               
SIDEBORD:                               
   pha        ;Prozessorregs. retten    
   txa                                  
   pha                                  
   tya                                  
   pha                                  
   dec $d019  ;VIC-ICR löschen          
   inc $d012  ;nächste Rasterz. neuer   
   lda #<irq2 ; IRQ-Auslöser, mit Sprung
   sta $fffe  ; auf "IRQ2"              
   cli        ;IRQs erlauben            
ch:nop        ;13 NOPs während der der  
   ...        ;IRQ irgendwann auftritt  
   nop                                  
   jmp ch                               
IRQ2:                                   
   lda #$ff      ;Alle Sprites, sowie   
   sta $d015     ; X-Expansion          
   sta $d01d     ; einschalten          
   clc                                  
   lda $00FB      ;"SOFTROLL" lesen     
   adc #$02       ;Die Y-Position       
   sta $d001      ; der Sprites         
   sta $d003      ; befindet sich       
   sta $d005      ; 2 Rasterzeilen      
   sta $d007      ; nach RasterIRQ      
   sta $d009      ; und muß demnach     
   sta $d00b      ; gesetzt werden.     
   sta $d00d                            
   sta $d00f                            
   lda $d012     ;den letzen Zyklus     
   cmp $d012     ;korrigieren           
   bne onecycle                         
ONECYCLE:                               
   pla           ;Vom 2. IRQ erzeugte   
   pla           ;Rückspr.adr, u. CPU-  
   pla           ;Status v.Stapel entf. 
   lda #<sidebord;IRQ-Ptr. wieder auf   
   sta $fffe     ; "SIDEBORD" zurück    
   dec $d019     ;VIC-ICR löschen       
   lda $FB       ;Index für $D011-      
   lsr           ; Tabelle vorbereiten  
   tay                                  
Soweit also keine  uns  besonders  unbe-
kannte  Routine.  Der  hier  aufgezeigte
Auszug dient lediglich dem  Glätten  des
IRQs  und sollte Ihnen, wenn auch leicht
modifiziert, schon aus anderen  Beispie-
len  bekannt sein. Einzig zu erwähnender
Punkt wäre die Adresse  $FB.  In  dieser
Zeropageadresse  steht  die  Nummer  der
Rasterzeile, in der der  IRQ  aufgerufen
werden  soll.  Obwohl  das  bei uns zwar
immer der Wert 0 ist (womit also immer 0
in diesem Register steht), hat dies  ei-
nen entscheidenden Vorteil: durch einfa-
ches Hochzählen dieses Registers  um  1,
nach  jedem  Rasterdurchlauf,  wird  der
Raster-IRQ  jeweils  eine  Zeile  später
erst  auftreten,  womit  wir  einen ganz
simplen Scrolleffekt erzielen. Zu  sehen
ist  dies auch im Beispiel "ESCOS2", das
absolut identisch zu "ESCOS1"  ist,  je-
doch  mit  der  oben genannten Änderung.
Gleichzeitig hat die  Adresse  $FB  noch
eine zweite Aufgabe: sie wird zusätzlich
als  Index  auf eine Liste "mißbraucht",
in der die Werte stehen,  die  in  $D011
eingetragen werden müssen, um oberen und
unteren Bildschirmrand, sowie Charakter-
zeilen abzuschalten. Da im  Prinzip  nur
drei verschiedene Werte in dieser Tabel-
le stehen, und zudem die Änderungen  der
Werte  nicht  hundertprozentig  genau in
einer speziellen  Rasterzeile  auftreten
müssen,  sondern  ruhig auch einmal eine
Rasterzeile früher  oder  später,  haben
wir  die Tabelle nur halb solang gemacht
und tragen nur jede  zweite  Rasterzeile
einen  Wert  von  ihr in $D011 ein. Dies
hat den zusätzlichen  Vorteil,  daß  sie
nicht länger als 256 Byte wird und somit
keine  High-Byte-Adressen berücksichtigt
werden müssen.  Aus  diesem  Grund  wird
also  auch  der  Index-Wert,  der ins Y-
Register  geladen  wird,   durch   einen
"LSR"-Befehl  halbiert,  damit bei einer
Verschiebung automatsich die ersten  Ta-
bellenwerte  übersprungen  und somit auf
den "richtigen"  ersten  Tabelleneintrag
zugegriffen  wird. Wichtig ist nur noch,
daß die Tabelle unbedingt innerhalb  ei-
nes  256-Byte-Blocks  steht und an einer
Adresse  mit  Low-Byte-Wert  0  beginnt.
Durch die Y-indizierte Adressierung, mit
der  wir auf sie zugreifen, könnte es im
anderen Fall zu Timing-Problemen kommen.
Greifen wir nämlich mit dem Befehl  "LDA
$11FF,Y"  auf eine Speicherzelle zu, und
enthält in diesem  Fall  das  Y-Register
einen  Wert größer 1, so tritt ein Öber-
lauf   bei    der    Adressierung    auf
($11FF+1=$1200  -  High-Byte  muß  hoch-
gezählt  werden!).  Ein  solcher  Befehl
benötigt  dann  nicht mehr 4 Taktzyklen,
sondern einen Taktzyklus  mehr,  in  dem
das  High-Byte  korrigiert wird! Dadurch
würden wir also unser Timing durcheinan-
derbringen,   weswegen  die  Tabelle  ab
Adresse $1500  abgelegt  wurde  und  mit
Ihren 156 Einträgen keinen solchen Öber-
lauf erzeugt. Die Tabelle selbst enthält
nun, jeweils blockweise, die Werte  $10,
$00  und  $18,  die an den richtigen Ra-
sterpositionen untergebracht wurden,  so
daß  die Ränder und Charakterzeilen beim
Schreiben des entsprechenden Wertes  ab-
geschaltet  werden. Sie können ja einmal
mit Hilfe eines  Speichermonitors  einen
Blick hineinwerfen.                     
Wir werden uns nun um den  Rest  unserer
Routine  kümmern,  in der wir in 294 Ra-
sterzeilen  die  seitlichen  Ränder  ab-
schalten  und zudem jede 21. Rasterzeile
die Sprites neu  positionieren,  so  daß
insgesamt  14 Zeilen zu je 8 Sprites auf
dem Bildschirm erscheinen. Dies ist  die
Fortsetzung der SIDEBORD-IRQ-Routine:   
   nop       ;Diese Befehlsfolge wird   
   jsr open21; insgesamt 14x aufgerufen 
   ...       ; um je 21 Zeilen zu öffnen
   ...                                  
   nop       ;Sprite Line 14            
   jsr open21                           
   lda #$00  ;Sprites aus               
   sta $d015                            
   lda #$c8  ;40-Spalten-Modus zurück-  
   sta $d016 ; setzen                   
   lda $FB   ;Zeile aus $FB für nächsten
   sta $d012 ; Raster-IRQ setzen        
   pla       ;Prozessorregs. zurückholen
   tay       ; und IRQ beenden0         
   pla                                  
   tax                                  
   pla                                  
   rti                                  
Wie Sie sehen besteht der  Kern  unserer
Routine  aus den 14 Mal aufeinander fol-
genden Befehlen: "NOP" und "JSR OPEN21".
Der NOP-Befehl dient dabei lediglich dem
Verzögern. Die Unterroutine "OPEN21" ist
nun dafür zuständig, in den folgenden 21
Rasterzeilen (genau die Höhe eines Spri-
tes) den Rand zu öffnen, sowie die neuen
Y-Spritepositionen  zu setzen. Sie sieht
folgendermaßen aus:                     
;line 00                                
OPEN21:                                 
   nop         ;Verzögern bis rechter   
   nop         ; Rand erreicht          
   dec $d016   ;38 Spalten              
   inc $d016   ;40 Spalten              
   lda $1500,y ;$D011-Wert aus Tabelle  
   sta $d011   ; lesen und eintragen    
   iny         ;Tabellen-Index+1        
   jsr cycles+5;24 Zyklen verzögern     
;line 01                                
   dec $d016   ;38 Spalten              
   inc $d016   ;40 Spalten              
   jsr cycles  ;34 Zyklen verzögern     
   ...         ;dito für 2-15           
Wie  Sie  sehen  können,  verzögern  wir
zunächst  mit zwei NOPs bis zum Ende des
sichtbaren  Bildschirmfensters,  wo  wir
durch  Herunterzählen von Register $D016
die 38-Spalten-Darstellung  einschalten,
und  gleich darauf durch Hochzählen des-
selben Registers wieder  auf  40  Zeilen
zurückgehen.  Damit wäre der Rand geöff-
net worden. Als nächstes wird  der  Wert
für  $D011 aus der besagten Tabelle aus-
gelesen und in dieses Register eingetra-
gen,   sowie   der  Index-Zähler  im  Y-
Register für die Öbernächste Zeile um  1
erhöht. Danach wird die Routine "CYCLES"
aufgerufen,  jedoch  nicht  an ihrer ei-
gentlichen Adresse, sondern 5 Bytes wei-
ter. Diese Routine besteht aus insgesamt
11  NOPs,  die lediglich die Zeit verzö-
gern  sollen,  bis  die  nächste   Rand-
Abschaltung  fällig  wird.  Da ein NOP 2
Taktzyklen verbraucht, verzögert sie  22
Taktyklen.  Hier muß man zusätzlich noch
die Zeit hinzurechnen, die für den  JSR-
und  RTS-Befehl  draufgehen.  Beide ver-
brauchen  je  6  Taktzyklen,  womit  die
"CYCLES"-Routine  insgesamt 34 Zyklen in
Anspruch nimmt. Durch den Offset  von  5
Bytes,  den wir beim ersten Einsprung in
die Routine machen, "übergehen" wir ein-
fach  die  ersten  5  NOPs, womit nur 24
Zyklen verbraucht werden. Nach  Rückkehr
aus  dieser  Routine  befindet  sich der
Rasterstrahl nun genau an der  Position,
an der wir den Rand für die neue Raster-
zeile öffnen müssen, was  sogleich  auch
durch   die  INC-DEC-Befehlsfolge  getan
wird. Anschließend  wird  wieder  verzö-
gert,  wobei  wir  diesmal die "CYCLES"-
Routine komplett durchlaufen, da in die-
ser  Zeile kein neuer Wert in $D011 ein-
getragen wird, und wir somit  10  Zyklen
mehr zu verzögern haben!                
Dieser Programm-Auszug  wiederholt  sich
nun insgesamt noch 7 Mal, bis wir in die
16.  Spritezeile  gelangt sind. Hier nun
beginnen wir  mit  dem  Neupositionieren
der Sprites:                            
;line 16                                
   dec $d016   ;Altbekannte Methode     
   inc $d016   ; um Zeile 16 zu öffnen  
   lda $1500,y ; u. $D011 neu zu setzen 
   sta $d011                            
   iny                                  
   jsr cycles+5                         
;line 17                                
   dec $d016   ;38 Spalten              
   inc $d016   ;40 Spalten              
   clc         ;Neue Y-Pos. für nächste 
   lda $d001   ; Spr.-Reihe= Alte Y-Pos.
   adc #$15    ; plus 21                
   sta $d001   ;Und für Sprites 0-3     
   sta $d003   ; eintragen              
   sta $d005                            
   sta $d007                            
   nop         ;Nur 10 Zyklen verzögern 
   nop                                  
   nop                                  
   nop                                  
   nop                                  
;line 18                                
   dec $d016   ;Altbekannte Methode zum 
   inc $d016   ; öffnen der 17. Zeile...
   lda $1500,y                          
   sta $d011                            
   iny                                  
   jsr cycles+5                         
;line 19                                
   dec $d016  ;38 Spalten               
   inc $d016  ;40 Spalten               
   clc        ;Neue Y-Pos. für nächste  
   lda $d009  ; Sprite-Reihe berechnen  
   adc #$15                             
   sta $d009  ;und für Sprites 4-7      
   sta $d00b  ; setzen                  
   sta $d00d                            
   sta $d00f                            
   nop        ;Nur 10 Zyklen verzögern  
   nop                                  
   nop                                  
   nop                                  
   nop                                  
;line 20                                
   dec $d016  ;38 Spalten               
   inc $d016  ;40 Spalten               
   lda $1500,y;Neuen Wert für $D011 aus 
   sta $d011  ; Tab. holen u. eintragen 
   iny        ;Tab-Index+1              
   nop        ;6 Zyklen verzögern       
   nop                                  
   nop                                  
   rts        ;Und fertig!              
Wie  Sie sehen, benutzen wir für die ge-
raden Zeilennummern die alte Befehlsfol-
ge  zum Üffnen des Randes und Setzen des
neuen $D011-Wertes. In den  Spritezeilen
17  und  19  jedoch wird nach dem Üffnen
des Randes die Y-Position um  21  erhöht
und  in je vier Y-Sprite-Register einge-
tragen, womit also die Y-Positionen  der
nächsten  Spritereihe  festgelegt wären.
In Spritezeile 20 wird nun  ein  letztes
Mal  der $D011-Wert neu gesetzt und nach
einer  kurzen  Verzögerung  wieder   zum
Hauptprogramm  zurückgekehrt, von wo die
Routine für alle 14 Spritereihen nochmal
aufgerufen wird, und womit unser  ESCOS-
Effekt fertig programmiert ist!         
4) ABSCHLIESSENDE HINWEISE              
Wie schon erwähnt, ist der Prozessor zur
Anzeige dieser  insgesamt  114  Sprites,
sowie  dem Abschalten der Bildschirmrän-
der, fast den  gesamten  Rasterdurchlauf
lang  voll  beschäftigt. Viel Rechenzeit
bleibt uns nicht mehr übrig, um  weitere
Effekte  zu  programmieren. Dennoch sind
am Ende  des  Rasterdurchlaufs  noch  16
Rasterzeilen  zur  freien Verfügung, und
man muß noch die Zeit  hinzurechnen,  in
der  der  Rasterstrahl vom unteren Bild-
schirmrand wieder zum oberen Bildschirm-
rand  zu  wandern  hat, in der wir eben-
falls noch mit  dem  Prozessor  arbeiten
können.  Diese  Zeit  wurde  im Beispiel
"ESCOS2" benutzt,  um  die  Spritereihen
zusätzlich  noch zu scrollen. Jedoch ist
auch noch ein  Moviescroller  problemlos
machbar.  Dieses Thema werden wir jedoch
erst nächsten Monat ansprechen, und  uns
auch weiterhin mit den Sprites befassen.
Sie  werden  dabei eine Möglichkeit ken-
nenlernen, wie man diese kleinen Grafik-
winzlinge hardwaremässig dehnen und ver-
biegen kann. Wie  immer  gibt  es  dabei
einen  Trick,  den  VIC zu überreden das
Unmögliche möglich zu machen...         
                                 (ih/ub)
                IRQ-KURS                
     "Die Hardware ausgetrickst..."     
                (Teil 12)               
----------------------------------------
Herzlich Willkommen zu einer neuen Folge
unseres  IRQ-Kurses.  In  dieser Ausgabe
werden wir uns  weiter  mit  der  ESCOS-
Routine des letzten Kursteils beschäfti-
gen. Wir werden hierbei die Routine  mit
einem  Movie-Scroller  verbinden,  um so
einen   bildschirmübergreifenden    Text
scrollen  zu lassen. Dies gestaltet sich
ohne einen speziellen  Trick  gar  nicht
mal so einfach...                       
1) EINLEITUNG                           
Wie Sie sich vielleicht  noch  erinnern,
so  hatten  wir in der letzten Folge des
IRQ-Kurses zuletzt das  Beispielprogramm
"ESCOS2"  besprochen.  Diese Raster-IRQ-
Routine öffnete uns den linken und rech-
ten,  sowie den oberen und unteren Bild-
schirmrand und stellte dann alle 21  Ra-
sterzeilen  eine  neue Reihe mit je acht
Sprites auf dem Bildschirm dar.  Dadurch
hatten  wir insgesamt vierzehn Zeilen zu
je acht Sprites auf dem Bildschirm,  die
alle  nahtlos aneinandergereiht fast den
gesamten  Bildschirm  überdeckten.   Der
einzige  ungenutzte  Bereich war der von
Rasterzeile 294 bis 312,  der  zu  klein
war  um  eine  weitere Spritereihe darin
unterzubringen, aber sowieso  schon  un-
terhalb  der  für  normale Monitore dar-
stellbaren Grenze liegt.                
Der einzige Unterschied der Routine "ES-
COS2" zu "ESCOS1" bestand nun darin, daß
erstere Routine lediglich ein Zeilenoff-
set-Register  für den ersten auszulösen-
den  Raster-IRQ  herunterzählte,  um  so
einen  Scroll-Effekt der Spritereihen zu
erzielen. "ESCOS2" ist also  schon  eine
Art  "Moviescroller".  Der  Grund, warum
sie es nicht wirklich ist, liegt  darin,
daß in jeder Spritezeile dieselben Spri-
tes auf dem Bildschirm zu  sehen  waren.
Wie   wir   aber  von  den  Moviescroll-
Routinen wissen, müssen  wir  für  einen
Scrolltext das Aussehen einer Spritezei-
le ja individuell bestimmen können,  da-
mit  auch  wirklich  ein Text, und nicht
immer dieslbe Zeile, über den Bildschirm
läuft.                                  
2) DAS PROBLEM - DIE LÜSUNG             
"Kein Problem"  werden  Sie  nun  sagen,
"einfach  die Textausgaberoutine der Mo-
viescroller-Routinen vom  IRQ-Kurs  ein-
bauen, und schon haben wir einen Scroll-
text". Diese Feststellung stimmt  schon,
wie  sollte es auch anders gehen, jedoch
stellt sich uns dabei noch  ein  kleines
Problem in den Weg: dadurch nämlich, daß
wir  in  den  ESCOS-Routinen, keine Zwi-
schenräume mehr zwischen  den  einzelnen
Spritezeilen  haben,  gestaltet  es sich
als schwierig, die Sprite-Pointer  zeit-
genau  zu  setzen. Setzen wir diese näm-
lich auf die neu einzuscrollende Sprite-
zeile, noch bevor die letzte Spritezeile
abgearbeitet  wurde,  so  erscheinen die
alten  Sprites  schon  im  Aussehen  der
Neuen.  Umgekehrt können die Spritepoin-
ter auch nicht nach Abarbeiten der letz-
ten  Spritezeile  gesetzt werden da hier
ja schon die  neuen  Sprites  erscheinen
müssen.  Man  könnte  nun versuchen, ein
mega-genaues  Timing  zu  programmieren,
das  innerhalb  der  ersten  Rasterzeile
einer neuen Spritezeile exakt  vor  Dar-
stellen eines neuen Sprites dessen Poin-
ter neu setzt. Dies gestaltet  sich  je-
doch umso umständlicher, wenn wir beach-
ten müssen, daß gleichzeitig  auch  noch
der  linke und rechte Rand geöffnet wer-
den soll. Da dieser ebenfalls ein  exak-
tes  Timing verlangt, würden wir mit dem
Doppeltiming in des Teufels  Küche  kom-
men.  Aber keine Sorge: glücklicherweise
können wir als IRQ-Magier wieder in  die
Raster-Trickkiste  greifen,   und   eine
weitaus einfachere Lösung  des  Problems
anwenden.                               
Wie  Sie  z.B.  schon  von  unseren FLI-
Routinen wissen, hat man mit dem VIC die
Möglichkeit,  das  Video-RAM   innerhalb
seines  Adressbereiches von 16KB zu ver-
schieben. Der Speicherbereich,  den  der
Grafikchip dazu verwendet, um den Inhalt
des  Textbildschirms aufzubauen muß also
nicht zwingenderweise bei $0400  liegen,
wo  er  sonst  nach  dem Einschalten des
Rechners untergebracht  ist.  Durch  die
Bits 4-7 des Registers $D018 können ins-
gesamt 16 verschiedene  Speicherbereiche
gewählt  werden,  deren Basisadressen in
$0400-Schritten von  $0000-$3C00  gehen.
Nun  werden Sie fragen, was denn das Vi-
deo-RAM mit unserer ESCOS-Routine zu tun
hat, zumal wir die Bildschirmdarstellung
doch  sowieso  abgeschaltet  haben,  und
keinen  Text  auf  dem Bildschirm haben?
Nun, ganz einfach: wo befinden sich denn
die Register der Spritepointer normaler-
weise?  Natürlich  in  den  Adressen von
$07F8-$07FF.  Und  genau  diese  Adresse
liegen  am  Ende  des  Video-RAMs.  Ver-
schiebt man nun das  Video-RAM  an  eine
andere  Adresse, so verschiebt man auto-
matisch auch  die  Registeradressen  der
Spritepointer!  Wird  das Video-RAM also
beispielsweise um $0400 Bytes nach $0800
verschoben, so bewegen sich die  Sprite-
pointer-Register   ebenfalls  um  $0400-
Bytes nach vorne. Um nun also das Ausse-
hen der acht Sprites zu definieren, müs-
sen die Adressen $0BF8-$0BFF beschrieben
werden. Und genau  das  ist  die  Lösung
unseres  Problems.  Da  es  während  des
Spriteaufbaus zu lange dauert, alle acht
Spritepointer in den Akku zu  laden  und
in  die  Register zu schreiben, soll das
die Initialisierungsroutine des  Movies-
crollers  übernehmen.  Hierbei  schreibt
letztere die benötigten Werte  für  eine
Spritezeile in die Pointerregister eines
verschobenen   Video-RAMs.  Während  der
Darstellung brauchen wir  nun  nur  noch
durch  Schreiben  eines  einzigen Bytes,
nämlich des ensprechenden Wertes für ein
neues Video-RAM in $D018,  auf  selbiges
umzuschalten.  Dadurch,  daß der VIC für
die neue Spritezeile nun in  einem  ganz
anderen Video-RAM arbeitet, holt er sich
auch  die  Spritepointer  aus  den neuen
Adressen, womit wir  alle  acht  Sprite-
pointer  mit  nur  einem  Schreibzugriff
umgeschaltet hätten!                    
Die Umsetzung dieser Lösung in die  Pra-
xis gestaltet sich für uns sehr einfach.
Sinnigerweise  haben  wir nämlich in den
Routinen  "ESCOS1"  und  "ESCOS2"  schon
Platz  für  diese Änderung gelassen. Wie
Sie sich vielleicht erinnern, hatten wir
zur Darstellung  der  Sprites  innerhalb
der  IRQ-Routine dieser beiden Beispiel-
programme  die   Unterroutine   "OPEN21"
vierzehnmal  aufgerufen.  Sie wird immer
in der ersten  Rasterzeile  einer  neuen
Spritezeile  angesprungen, und übernimmt
das Neusetzen  der  Y-Koordinaten  aller
Sprites,  sowie  das Üffnen des Sidebor-
ders in den folgenden  21  Rasterzeilen.
So  sah  der  Aufruf  in den beiden Bei-
spielprogrammen aus:                    
   nop       ;Diese Befehlsfolge wird   
   jsr open21; insgesamt 14x aufgerufen 
   ...       ; um je 21 Zeilen zu öffnen
   nop       ;Sprite Line 14            
   jsr open21                           
Die "NOP"-Befehle dienten dabei nur  der
Verzögerung  um  zwei  Taktzyklen, damit
die  Änderung  zum  richtigen  Zeitpunkt
eintritt.  Wir  können diese Befehle mit
jedem anderen Befehl ersetzen, der nur 2
Takte in Anspruch nimmt. Diese  Tatsache
machen  wir  uns  für  den Moviescroller
zunutze. Wir ersetzen die NOPs mit  LDA-
Befehlen,  wobei  der  Wert,  der in den
Akku geladen wird, dem Wert  entspricht,
der in Register $D018 geschrieben werden
muß,  um  auf  das  neue Video-RAM umzu-
schalten. Er dient quasi  als  Parameter
für  die "OPEN21"-Routine. Demnach sieht
der Aufruf dieser Routine, aus  dem  Ra-
ster-IRQ heraus, nun folgendermaßen aus:
v00:lda #$00*$10 ;Code für Scr0 ($0000) 
    jsr open21   ;21 Rasterzeilen öffnen
v01:lda #$01*$10 ;Code für Scr0 ($0400) 
    jsr open21   ;21 Rasterzeilen öffnen
v02:lda #$02*$10 ;Code für Scr0 ($0800) 
    jsr open21   ;21 Rasterzeilen öffnen
    ...                                 
v0d:lda #$0d*$10 ;Code für Scr13 ($3400)
    jsr open21   ;21 Rasterzeilen öffnen
Wie  schon  in  den ESCOS-Beispielen, so
wiederholt sich auch hier  die  LDA-JSR-
Befehlsfolge  14  Mal, wobei jeweils der
nächste Video-RAM-Bereich als  Parameter
im Akku übergeben wird. Timingmässig hat
sich  nichts geändert, da der NOP-Befehl
genausolange braucht wie der LDA-Befehl.
Eine ähnliche Modifikation haben wir nun
für  das Schreiben dieses Akku-Wertes in
Register $D018 vorgenommen. Diese Aufga-
be  soll von der Routine "OPEN21" durch-
geführt werden. Hier ein Auszug der  er-
sten  Zeilen dieser Routine aus dem Bei-
spiel "ESCOS2":                         
open21:                                 
   nop         ;Verzögern bis rechter   
   nop         ; Rand erreicht          
   dec $d016   ;38 Spalten (Rand        
   inc $d016   ;40 Spalten   öffnen)    
   lda $1500,y ;$D011-Wert aus Tabelle  
   sta $d011   ; lesen und eintragen    
   iny         ;Tabellen-Index+1        
   jsr cycles+5;24 Zyklen verzögern     
   ...                                  
Wie Sie  sehen,  stehen  hier  ebenfalls
zwei  NOP-Befehle am Anfang der Routine.
Sie benötigen 4 Taktzyklen, was für  ei-
nen   "STA $XXXX"-Befehl  ebenfalls  zu-
trifft. Die zwei  NOPs  wurden  für  den
Moviescroller  nun mit einem "STA $D018"
ersetzt:                                
open21:                                 
   sta $d018   ;VRAM f.SprPtrs. versch. 
   dec $d016   ;38 Spalten (Rand        
   inc $d016   ;40 Spalten   öffnen)    
   lda $1500,y ;$D011-Wert aus Tabelle  
   sta $d011   ; lesen und eintragen    
   iny         ;Tabellen-Index+1        
   jsr cycles+5;24 Zyklen verzögern     
   ...                                  
Damit hätten wir also die  ESCOS-Routine
so  umprogrammiert, daß Sie uns in jeder
der 14 Spritezeilen  auch  neue  Sprite-
pointer  setzt.  Es müssen nun noch zwei
weitere Änderungen gemacht werden, damit
der  Movie-scroller  auch   voll   funk-
tionstüchtig ist.                       
Zunächst  einmal muß die Initialierungs-
routine erweitert werden. Sie  soll  uns
die  Spritepointer  der benutzten Video-
RAM-Adressen auf bestimmte Sprites  vor-
initialisieren,  so  daß später zur Dar-
stellung  einer  bestimmten  Spritereihe
nur  noch  die Nummer des zu benutzenden
Video-RAMs in den Labeln "V00" bis "V0D"
der Raster-IRQ-Routine (siehe oben) ein-
getragen  werden  muß,  und  die Routine
somit automatisch  den  richtigen  Bild-
schirm  zur  korrekten  Darstellung  der
entsprechenden    Spritezeile     wählt.
Zunächst  einmal wollen wir vereinbaren,
daß wir den 16K-Adressbereich  des  VICs
von $0000-$3FFF um eins nach oben in den
Bereich   von  $4000-$7FFF  verschieben.
Dadurch stört uns die Zeropage, die nor-
malerweise  ja  auch im Bereich des VICs
liegt, nicht mehr, und wir  haben  volle
16KB  zur  Speicherung  von  Sprites und
Spritepointer-Video-RAM  zur  Verfügung.
Die  Verschiebung  wird  durch Schreiben
des Wertes 2 in das Portregister  A  von
CIA-B  erreicht.  Innerhalb der Initial-
sierung wurde also  folgende  Befehlsse-
quenz hinzugefügt:                      
LDA #$02                                
STA $DD00                               
Damit befindet sich der Datenbereich für
den VIC nun im Bereich von  $4000-$7FFF.
Beachten  Sie  bitte  auch,  daß nun der
Bytewert, den der  VIC  in  "ausgetrick-
sten" Rasterzeilen darstellt, nicht mehr
in $3FFF, sondern in $7FFF abgelegt wer-
den muß (im Beispielprogramm enthält  er
den  Wert  $81,  womit vertikale Lininen
hinter dem Scrolltext  erscheinen).  Nun
folgt  der  Teil,  der die Spritepointer
setzt. Hier treffen wir die  Konvention,
daß  die  Sprites, die durch die Pointer
einer   Video-RAM-Bereichs   dargestellt
werden  auch innerhalb dieses Video-RAMs
unterbracht werden sollen. Liegt  dieses
also  Beispielsweise  bei $4400, so soll
Spritepointer 0 auf das Sprite bei $4400
(Blocknr. 16), Spritepointer 1  auf  das
Sprite  bei  $4440  (Blocknr. 17), usw.,
zeigen. Dies bewerkstelligt nun der fol-
gende Teil der Init-Routine:            
   lda #$f8       ;ZP-Zgr. $02/03 mit   
   sta $02        ; $43F8 init.         
   lda #$43                             
   sta $03                              
   lda #($0000/64);Ptr. f. Sprite-Line01
   jsr setpoint   ; setzen              
   lda #($0400/64);Ptr. f. Sprite-Line02
   jsr setpoint   ; setzen              
   lda #($0800/64);Ptr. f. Sprite-Line03
   jsr setpoint   ; setzen              
   ...                                  
   lda #($3000/64);Ptr. f. Sprite Line13
   jsr setpoint   ; setzen              
   lda #($3400/64);Ptr. f. Sprite Line14
   jsr setpoint   ; setzen              
Wie Sie sehen wird lediglich ein  Adres-
sierungszeiger in der Zeropage initiali-
siert, und dann 14 Mal die  Unterroutine
"SETPOINT" aufgerufen, wobei im Akku der
Inhalt  für  den  jeweils ersten Sprite-
pointer übergeben  wird.  Hier  nun  die
Routine "SETPOINT", die die eigentlichen
Werte in die Pointerregister schreibt:  
SETPOINT:                               
   ldy #$00   ;Index-Reg. init.         
   sta ($02),y;SprPtr0 ablegen          
   clc        ;Akku=Akku+1              
   adc #$01                             
   iny        ;Index=Index+1            
   sta ($02),y;SprPtr1 ablegen          
   clc        ;Akku=Akku+1              
   adc #$01                             
   iny        ;Index=Index+1            
   sta ($02),y;SprPtr2 ablegen          
   ...                                  
   clc        ;Akku=Akku+1              
   adc #$01                             
   iny        ;Index=Index+1            
   sta ($02),y;SprPtr7 ablegen          
   clc        ;Auf Hi-Byte des $02/$03  
   lda $03    ; Zeigers den Wert 4 add. 
   adc #$04   ; um auf nächstes VRAM zu 
   sta $03    ; positonieren            
   rts                                  
Wie Sie sehen, so wird vom Basiswert des
ersten Spritepointers an, acht  Mal  je-
weils um eins hochgezählt und das Ergeb-
nis über den Vektor bei $02/$03  in  die
entsprechende     Sprite-Pointer-Adresse
geschrieben. Beim ersten Durchlauf zeigt
dieser Vektor auf Adresse $43F8, wo sich
die Spritepointer des ersten  Video-RAM-
Bereichs  befinden. Am Ende der Pointer-
initialisierung wird  die  Vektoradresse
nun  um  $0400  erhöht (auf Hi-Byte wird
$04 addiert), damit beim nächsten Durch-
lauf  die Zeiger des nächsten Video-RAM-
Bereichs gesetzt werden.                
 (Anm.d.Red.: Bitte wählen Sie jetzt den
   zweiten Teil des IRQ-Kurses aus dem  
                Textmenu)               
    Fortsetzung IRQ-Kurs 12 - 2. Teil   
----------------------------------------
Nachdem nun die Spritepointer vorinitia-
lisiert  wurden,  muß   die   "MOVESPR"-
Routine, die Sie bestimmt noch von unse-
ren  alten  Moviescroller-Routinen   her
kennen,  so  modifiziert werden, daß sie
die IRQ-Routine derart  ändert,  daß  in
jeder  Spritezeile auch der richtige Vi-
deo-RAM-Bereich eingeschaltet  wird.  Da
die  "MOVESPR"-Routine schon im 10. Teil
des  IRQ-Kurses  ausfühlich  beschrieben
wurde  soll  hier  nur  das kommentierte
Listing die Funktionsweise  der  Routine
wieder ins Gedächtnis rufen. Der eigent-
liche, für uns wichtige Teil der Routine
befindet sich am Ende  und  heißt  "ROL-
LON".  Er  wurde der ESCOS-Routine ange-
passt und soll  anschließend  aufgeführt
werden.  Zunächst  jedoch  die  MOVESPR-
Routine:                                
movespr:                                
   lda softroll ;Scroll-Offs. holen     
   sbc #$01     ;Und 1 subtrahieren     
   sta softroll ;neuen Scroll-Offs. abl.
   bpl rollon   ;Wenn >0, dann weiter   
newline:                                
   inc showline ;1.Spr-Zeilen-Ind. erh. 
   sec          ;C-Bit f. Subtr. setzen 
   lda showline ;Showline holen         
   sbc #$0d     ; und 13 subtr.         
   bmi noloop   ;Bei Unterlauf weiter   
   sta showline ;Sonst Wert abl.        
noloop:                                 
   lda tpoint                           
   cmp #<estext+($34*$18)               
   bne continue                         
   lda tpoint+1                         
   cmp #<estext+($34*$18)               
   bne continue                         
   lda #$00                             
   sta showline                         
   lda #<estext                         
   sta tpoint+0                         
   lda #>estext                         
   sta tpoint+1                         
continue:                               
   clc         ;C-Bit f.Add. löschen    
   lda softroll;SOFTROLL laden          
   adc #$18    ;24 addieren             
   sta softroll;und wieder ablegen      
   lda #$20    ;Op-Code für "JSR"       
   sta mt      ;in MT eintragen         
Ab dem Label "NEWLINE" ist  die  Routine
damit  beschäftigt, die neue Spritezeile
aufzubauen, sowie die Zeiger  für  diese
Zeile neu zu setzen. Nach ihrer Abarbei-
tung gelangt sie  automatisch  zum  Teil
"ROLLON",  zu  dem  auch am Anfang verz-
weigt wird, wenn keine neue  Spritezeile
eingefügt werden muß. Hier geschehen nun
die  für uns wesentlichen Dinge, nämlich
das Voreinstellen der  richtigen  Video-
RAM-Werte   innerhalb  der  IRQ-Routine.
Zunächst das Listing:                   
rollon:                                 
   lda softroll     ;nächsten Raster-   
   sta $d012        ;IRQ vorbereiten    
   ldy showline     ;SprZeilen-Ind.holen
   lda pointer+$00,y;Wert f. 1. SprZeile
   sta v00,+1       ; in IRQ setzen     
   lda pointer+$01,y;Wert f. 2. SprZeile
   sta v01,+1       ; in IRQ setzen     
   ...                                  
   lda pointer+$0D,y;Wert f. 14.SprZeile
   sta v0D,+1       ; in IRQ setzen     
   rts                                  
Alles  in  Allem  also  keine schwierige
Aufgabe. Zur Erinnerung sollte ich  noch
erwähnen,  daß  die beiden Labels "SOFT-
ROLL" und "SHOWLINE" für  die  Zeropage-
adressen  $F8 und $F9 stehen. "SOFTROLL"
ist dabei ein Zähler für den  Scrolloff-
set,  der pro Bildschirmdurchlauf einmal
erniedrigt  wird.  Dieser  Zähler   gibt
gleichzeitig auch die Rasterzeile an, an
der  der  nächste  Raster-IRQ  auftreten
muß. Dadurch, daß dieser Wert am  Anfang
der "MOVESPR"-Routine um eins erniedrigt
wurde,  und er hier nun als Auslöser für
die  nächste   Rasterzeile   eingetragen
wird, erreichen wir den Scrolleffekt, da
die   Spritezeilen  pro  Rasterdurchlauf
immer 1 Rasterzeile  früher  dargestellt
werden,  solange bis "SOFTROLL" als Zäh-
ler einen Unterlauf meldet,  und  wieder
auf  21 zurückgesetzt wird. Gleichzeitig
muß dann die soeben weggescrollte  Spri-
tezeile  am  unteren Bildschirmrand, mit
neuem Text wiedereingefügt  werden,  was
vom  Abschnitt  "NEWLINE"  schon  durch-
geführt wurde. "SHOWLINE" ist ein Zeiger
auf die Tabelle "POINTER". Er  gibt  an,
welche  der  14 Spritezeilen momentan an
der obersten Position steht. Er wird bei
einem "SOFTROLL"-Unterlauf um eins hoch-
gezählt,  da  nun  ja die ehemals zweite
Spritezeile zur Ersten wird.  Damit  tut
unser  neuer  "ROLLON"-Teil nichts ande-
res, als für jede Spritezeile einen Wert
aus der Tabelle zu lesen und in eins der
Labels von "V00" bis "V0D"  einzutragen.
Wie  aus  dem  obigen  Auszug  der  IRQ-
Routine ersichtlich, so ist dies jeweils
der  LDA-Befehl,  der  vor  jedem   "JSR
OPEN21"  steht.  Da  wir  auf die Label-
adressen den Wert 1 addieren,  schreiben
wir  also  einfach einen neuen Operanden
für den LDA-Befehl in das  Programm,  so
daß   die  IRQ-Routine  automatisch  den
richtigen  Pointerwert   übergibt.   Die
Pointertabelle sieht nun wiefolgt aus:  
POINTER:                                
  .by $00,$10,$20,$30                   
  .by $40,$50,$60,$70                   
  .by $80,$90,$A0,$B0                   
  .by $C0,$D0                           
  .by $00,$10,$20,$30                   
  .by $40,$50,$60,$70                   
  .by $80,$90,$A0,$B0                   
  .by $C0,$D0                           
Wie  Sie  sehen, enthält sie einfach die
benötigten Werte  für  das  VIC-Register
$D018,  um der Reihe nach die Video-RAM-
Bereiche zwischen $4000 und $7400 einzu-
schalten.  Die Tabelle enthält, wie auch
schon  bei  den   alten   Moviescroller-
Routinen,   alle  Pointerdaten  doppelt,
damit "SHOWLINE" auch bis  zum  Wert  13
hochgezählt werden kann, und dennoch die
Werte korrekt gesetzt werden.           
Damit  wären  nun  alle relevanten Teile
der  Änderung  einer  ESCOS-Routine  zum
Moviescroller  erklärt  worden.  Wie Sie
sehen, haben wir auf recht  einfach  Art
und Weise zwei verschiedene Rastereffek-
te miteinander  kombiniert,  ohne  große
Änderungen  vornehmen zu müssen. Das Er-
gebnis ist quasi ein  Moviescroller  mit
ESCOS-IRQ-Routine.  Auf  diese Weise ist
es auch möglich ganz andere Effekte mit-
einander  zu kombinieren, was Ihre Phan-
tasie    bezüglich    der    Raster-IRQ-
Programmierung ein wenig beflügeln soll.
Das  hier  besprochene  Programmbeispiel
befindet sich wie immer auch auf  dieser
MD  und  kann von Ihnen begutachtet wer-
den. Es heißt "MOVIE V1.2" und  muß  mit
",8,1"  geladen  und durch ein "SYS4096"
gestartet werden. Im  nächsten  Kursteil
werden  wir  dann die, schon versproche-
nen, Routinen  zum  Dehnen  von  Sprites
besprechen.                             
                                 (ih/ub)
                IRQ-KURS                
     "Die Hardware ausgetrickst..."     
                (Teil 13)               
----------------------------------------
Herzlich Willkommen zum dreizehnten Teil
unseres  IRQ-Kurses.  Auch  diesen Monat
soll es um das trickreiche  manipulieren
von Sprites gehen. Wir werden uns einige
Routinen  zum  Dehnen  von  Sprites  an-
schauen, und dabei lernen, daß  es  auch
Sprites gibt, die mehr als 21 Rasterzei-
len hoch sind...                        
1) DAS PRINZIP                          
Wie immer kommen wir zu Beginn zum Funk-
tionsprinzip  des  Rastereffektes dieses
Kursteils: Wie Sie vielleicht wissen, so
ist die Größe eines Sprites  prinzipiell
auf  24x21  Pixel  begrenzt. Diese Größe
ist starr und eigentlich nicht veränder-
bar. Es existieren jedoch 3 Sonderfälle,
in  denen  das  Sprite  auch größer sein
kann, nämlich dann, wenn wir  mit  Hilfe
der  Register $D017 und $D01D die X- und
Y-Expansion eines  Sprites  einschalten.
In  diesem  Fall  kann sowohl die X- als
auch die Y-Ausdehnung verdoppelt werden,
so daß wir ein Sprite mit einer  maxima-
len  Größe von 48x42 Pixeln erhalten, in
dem die  normalen  24x21  Pixel  einfach
doppelt dargestellt werden. Wie Sie also
sehen  ist der VIC rein theoretisch doch
in der  Lage  größere  Sprites  auf  den
Bildschirm  zu zaubern. Jedoch auch dies
nur in höchst beschränkter Form, da  wir
nun  ebenfalls  an  eine  fixe Auflösung
gebunden  sind.  Wir  werden  allerdings
gleich  ein  Verfahren kennenlernen, mit
dem es uns möglich sein wird,  zumindest
die  Y-Auflösung  eines Sprites variabel
festzulegen.                            
Dreh-  und  Angelpunkt  dieses  Effektes
wird das eben schon angesprochene  Regi-
ster zur Y-Expansion der Sprites ($D017)
sein. Wollen wir zunächst einmal klären,
wie  der  VIC die Sprites überhaupt dar-
stellt: Nehmen wir also an, daß wir  ein
Sprite  auf  dem  Bildschirm  darstellen
möchten. Zunächst  einmal  nicht  expan-
diert.  Der  VIC vergleicht nun jede Ra-
sterzeilennummer mit der Y-Position  des
Sprites. Sind beide Werte gleich, so hat
er  die Rasterzeile erreicht, in der die
oberste Linie des Sprites zu sehen  sein
soll. Es wird nun eine interne Schaltlo-
gik aktiviert, die  dem  VIC  zu  Beginn
einer  jeden Rasterzeile die Adresse der
nächsten drei Datenbytes an den Datenbus
legt und ihn somit jedesmal mit  den  in
dieser  Zeile für dieses Sprite relevan-
ten  Daten  füttert.   Die   Schaltlogik
verfügt  nun  für  jedes Sprite über ein
1-Bit-Zähl-,  sowie   ein   1-Bit-Latch-
Register, die beide bei einem Schreibzu-
griff auf das Register  $D017,  mit  dem
Zustand  des Bits zum zugehörigen Sprite
neu  initialisiert  werden.  Das  Latch-
Register dient dabei als "Merkhilfe" für
den Zustand der Y-Expansion des Sprites.
Enthält  es  den  Bitwert 1, so soll das
Sprite in Y-Richtung verdoppelt  werden,
enthält es den Wert 0, so soll es normal
dargestellt  werden. Ab der Rasterzeile,
ab der das Sprite nun gezeichnet  werden
soll  legt  die Schaltlogik nun zunächst
die aktuelle Spritedatenadresse  an  den
Bus und füttert den VIC so mit den Spri-
tedaten  für  die   erste   Rasterzeile.
Gleichzeitig   wird  bei  Erreichen  der
nächsten Rasterzeile der 1-Bit-Zähler um
eins erniedrigt. Tritt dabei ein  Unter-
lauf auf, so reinitialisiert die Schalt-
logik den Zähler wieder mit  dem  Inhalt
des Latch-Registers und erhöht die Quel-
ladresse für die Speitedaten um 3 Bytes,
so daß Sie anschließend dem VIC die  Da-
ten   der  nächsten  Spritezeile  zukom-
menlässt. Tritt kein Unterlauf auf, weil
der Zähler auf 1 stand und  beim  Herun-
terzählen  auf  0  sprang, so bleibt die
Quelladresse  der  Spritedaten  unverän-
dert,  so daß der VIC auch in dieser Ra-
sterzeile dieselben  Spritedaten  liest,
die  er  auch  eine  Rasterzeile  vorher
schon darstellte. Ist  die  Spriteexpan-
sion  nun  abgeschaltet, so wurden Latch
und Zähler zu  Beginn  mit  dem  Wert  0
gefüttert.  In dem Fall läuft der Zähler
in jeder  Rasterzeile  unter  und  somit
bekommt  der  VIC  in  jeder Rasterzeile
neue  Spritedaten  zugewiesen.  Ist  die
Y-Expansion  eingeschaltet, so enthalten
Zähler und Latch den Wert 1 und es  wird
fortlaufend nur in jeder zweiten Raster-
zeile eine neue Adresse an den  Datenbus
angelegt,  womit  der  VIC das Sprite in
der  Vertikalen  doppelt  so  hoch  dar-
stellt.                                 
Wie nun eingangs schon erwähnt so werden
sowohl Latch als auch Zähler neu initia-
lisiert,  sobald  ein Schreibzugriff auf
Register $D017  erfolgt.  Dadurch  haben
wir  also  auch direkten Einfluß auf den
Y-Expansions-Zähler. Was sollte uns  nun
also  davon  abhalten,  diesen Zähler in
JEDER Rasterzeile durch  Setzen  des  Y-
Expansionsbits  des  gewünschten Sprites
wieder auf 1 zurückzusetzen, so daß  die
Schaltlogik nie einen Unterlauf erzeugen
kann,  und  somit immer wieder dieselben
Spritedaten angezeigt werden?  Und  ganz
genau  so  können  wir ein Sprite länger
strecken als es eigentlich ist und  z.B.
3-, 4-, oder 5-fache Expansion des Spri-
tes bewirken (indem jede Spritezeile 3-,
4- oder 5-mal hintereinander dargestellt
wird)! Wir haben sogar  die  Möglichkeit
jede Spritezeile beliebig, und voneinan-
der unabhängig oft, zu  wiederholen,  so
daß  man  z.B. auch eine Sinuswelle über
das Sprite laufen  lassen  kann!  Hierzu
muß  lediglich exakt zu Beginn einer Ra-
sterzeile  entweder  das   Expansionsbit
gesetzt  werden,  wenn  die  Rasterzeile
dieselben Spritedaten enthalten soll wie
die letzte Rasterzeile, oder wir Löschen
das Expansionsbit des gewünschten  Spri-
tes,  um  die Daten der nächsten Sprite-
zeile in den VIC zu holen!              
2) PROGRAMMBEISPIELE 1 UND 2            
Um einen Eindruck von den  Möglichkeiten
zu  bekommen, die uns dieser Effekt bie-
tet, sollten Sie sich  einmal  die  Bei-
spielprogramme "STRETCHER.1" bis "STRET-
CHER.4" auf  dieser  MD  anschauen.  Sie
werden alle wie immer mit LOAD"Name",8,1
geladen  und durch ein "SYS4096" gestar-
tet. Die ersten beiden Beispiele stellen
ein Sprite dar,  das  normalerweise  nur
eine  diagonale Line von der linken obe-
ren Ecke zur rechten  unteren  Ecke  des
Sprites  enthält. Im ersten Beispiel ha-
ben wir lediglich einige  dieser  Zeilen
mehrfach  dargestellt.  Das  zweite Bei-
spiel  enthält  eine  Streckungstabelle,
mit  der  wir  jede Rasterzeile in Folge
1-, 2-, 3-, 4-, 5-, und  6-Mal  darstel-
len, womit die Linie in etwa die Rundun-
gen einer Sinuskurve bekommt!           
Wollen  wir uns nun einmal den Programm-
code anschauen, den  wir  zur  Erzeugung
der Verzerrung in Beispiel "STRETCHER.1"
verwenden.  Die  Initialisierung und den
Beginn der IRQ-Routine  möchte  ich  wie
immer aussparen, da beides absolut iden-
tisch mit unseren anderen IRQ-Beispielen
ist. Wir legen hier den  Raster-IRQ  auf
Rasterzeile  $82  fest  und schalten Be-
triebssystem-ROM ab, um direkt über  den
IRQ-Vektor  bei $FFFE/$FFFF zu springen.
Die IRQ-Routine selbst  beginnt  nun  ab
Adresse  $1100, wo zunächst unser altbe-
kannter Trick zum Glätten des IRQs  auf-
geführt  ist.  Der  für  uns wesentliche
Teil beginnt wie  immer  ab   dem  Label
"ONECYCLE",  ab  den  der  IRQ geglättet
wurde, und die für uns relevanten Routi-
nenteile stehen. Zusätzlich sei erwähnt,
daß  wir gleichzeitig, um Timingprobleme
zu vermeiden, eine FLD-Routine  benutzen
um  die Charakterzeilen wegzudrücken und
gleichzeitig  den  linken  und   rechten
Bildschirmrand öffnen, damit wir in spä-
teren Beispielen auch Sprites in  diesen
Bereichen  darstellen  und sehen können.
Hier nun  jedoch  zunächst  der  Source-
Code:                                   
onecycle:                               
  lda #$18   ;1. Wert für FLD           
  sta $d011  ; in $D011 schreiben       
  lda #$f8   ;Nächsten IRQ bei Raster-  
  sta $d012  ; zeile $f8 auslösen       
  dec $d019  ;VIC-ICR löschen           
  lda #21*5  ;Zähler f. FLD init. (21*5=
  sta $02    ; 5-fache Spritehöhe)      
  nop        ;Verzögern..               
  ldx #$00   ;Tabellenindex init.       
fldloop:                                
  lda ad017,x;Wert aus Stretch-Tab lesen
  sta $d017  ; und in Y-Exp. eintragen  
  lda ad011,x;Wert aus FLD-Tab lesen    
  sta $d011  ; und in $D011 eintragen   
  dec $d016  ;38 Spalten   (Rand        
  inc $d016  ;40 Spalten    öffnen)     
  nop        ;Bis zum Anfang der        
  nop        ; nächsten Rasterzeile     
  nop        ; verzögern...             
  nop                                   
  nop                                   
  nop                                   
  nop                                   
  nop                                   
  nop                                   
  lda #$00   ;Y-Exp. auf 0              
  sta $d017  ; zurücksetzen             
  inx        ;Tab-Index+1               
  cpx $02    ;Mit FLD-Zähler vgl.       
  bcc fldloop;Kleiner, also weiter      
  lda #$82   ;Nächster IRQ bei Rasterz. 
  sta $d012  ; $82                      
  ldx #$00   ;IRQ-Vektoren              
  ldy #$11   ; auf eigene               
  stx $fffe  ; Routine                  
  sty $ffff  ; umstellen                
  lda #$0e   ;Farben auf hellblau/      
  sta $d020  ; schwarz setzen           
  lda #$00                              
  sta $d021                             
  pla        ;Prozessorregs. zurück-    
  tay        ; holen                    
  pla                                   
  tax                                   
  pla                                   
  rti        ;IRQ beenden               
Ab   dem   Label   ONECYCLE  setzen  wir
zunächst einige Basiswerte für die Spri-
te-Stretch, FLD- und Sideborderroutinen.
Hierzu  schreiben  wir  erst  einmal die
Anzahl der Rasterzeilen, in denen  diese
drei  Effekte aktiv sein sollen als Ver-
gleichszähler in Adresse $02 und löschen
das X-Register, das als  Index  auf  die
FLD-  und Sprite-Stretch-Tabellen dienen
soll (dazu später mehr). Das Setzen  des
nächsten  IRQ-Auslösers  auf Rasterzeile
$F8 ist lediglich ein Relikt aus älteren
IRQ-Routinen, in denen wir  den  unteren
und  oberen  Rand des Bildschirms öffne-
ten. Da wir später jedoch wieder Raster-
zeile  $82  als  IRQ-Auslöser festlegen,
ist diese Befehlsfolge eigentlich  unnö-
tig.  Dennoch haben wir sie beibehalten,
da das Entfernen der beiden Befehle  zum
Einen  das  Timing, das zum exakten syn-
chronisieren zwischen Programm  und  Ra-
sterstrahl  notwendig  ist,  durcheinan-
derbrächte und wir dadurch andere Befeh-
le  zum  Verzögern einfügen müssten, und
zum Anderen um die Flexibilität der Rou-
tine  dadurch  nicht einzuschränken. Auf
diese Weise wird es z.B.  für  Sie  sehr
einfach,  die Routine mit einer Top- und
Bottom-Border-Funktion   "nachzurüsten",
indem  Sie  lediglich  die  IRQ-Vektoren
weiter unten  auf  eine  solche  Routine
verbiegen und das Festlegen des nächsten
IRQs  bei Rasterzeile $82 auf diese Rou-
tine verlagern. Dies ist  übrigens  eine
saubere   Möglichkeit   timing-kritische
IRQ-Routinen  zu  schreiben,   und   sie
zusätzlich  zu  anderen  Raster-Effekten
erweiterbar zu halten.  Da  wir  sowieso
die meiste Zeit verzögern müssen tun uns
die  beiden  Befehle  auch  nicht weiter
weh.                                    
Es folgt nun der  eigentliche  Kern  der
IRQ-Routine;  eine Schleife namens "FLD-
LOOP". Hier lesen  wir  zunächst  einmal
einen  Wert  aus der Tabelle "AD017" aus
und  tragen  ihn  in  das  Y-Expansions-
Register ein. Die besagte Tabelle befin-
det sich im Code ab  Adresse  $1600  und
enthält  die  Werte,  die nötig sind, um
das Sprite wie gewünscht zu  dehnen.  Da
wir  uns  in  den Beispielen 1 und 2 nur
auf ein  Sprite  beschränken  (Sprite  0
nämlich),  enthält die Tabelle natürlich
nur $00- und  $01-Werte.  Bei  $00  wird
ganz  normal die nächste Spritezeile ge-
lesen und angezeigt, bei $01 wird  immer
wieder  die zuletzt dargestellte Sprite-
zeile auf den Bildschirm gebracht.  Fol-
gen  mehrere  $01-Werte  aufeinander, so
wird eine  einzige  Spritezeile  so  oft
wiederholt, wie $01-Werte in der Tabelle
stehen.  Um die nächste Spritezeile dar-
zustellen muß  jetzt  mindestens  einmal
ein  $00-Wert  folgen.  Diese Zeile kann
nun ebenfalls  beliebig  oft  wiederholt
werden, usw. Zur besseren Öbersicht hier
ein  Auszug  aus  der  Tabelle mit Ihren
Werten für das Beispiel "STRETCHER.1":  
ad017:                                  
  .byte $01,$01,$01,$00,$01,$00,$00,$00 
  .byte $00,$00,$00,$00,$00,$00,$00,$00 
  .byte $01,$01,$01,$00,$01,$00,$00,$00 
  .byte $00,$00,$00,$00,$00,$00,$00,$00 
  .byte $01,$01,$01,$01,$01,$01,$01,$01 
  .byte $01,$01,$01,$01,$01,$01,$01,$01 
  .byte $01,$01,$01,$01,$01,$01,$01,$01 
  .byte $01,$01,$01,$01,$01,$01,$01,$01 
  .byte $01,$01,$01,$01,$01,$01,$01,$01 
  .byte $01,$01,$01,$01,$01,$01,$01,$01 
  .byte $01,$01,$01,$01,$01,$01,$01,$01 
  .byte $01,$01,$01,$01,$01,$01,$01,$01 
  .byte $01,$01,$01,$01,$01,$01,$01,$01 
  .byte $01,$01,$01,$01,$00,$00,$00,$00 
Wie  Sie  sehen   verzerren   wir   hier
zunächst  die  ersten  Spritezeilen ver-
schieden oft. Hiernach wird  die  letzte
Spritezeile  so  oft wiederholt, bis das
Ende unseres, durch FLD- und  Sideborder
behandelten, Bildschirmbereichs erreicht
wurde.                                  
Kommen wir jedoch wieder zu unserer FLD-
LOOP  zurück.  Nach  dem  Setzen  des Y-
Expansions-Registers wird  abermals  ein
Tabellenwert  gelesen und diesmal in Re-
gister $D011 übertragen. Diese  Befehls-
folge  ist für den FLD-Effekt notwendig,
mit dem wir den Beginn der nächsten Cha-
rakterzeile  vor  dem  Rasterstrahl her-
schieben. Die Tabelle enthält immer wie-
der  die  Werte $19, $1A, $1B, $1C, $1D,
$1E, $1F, $18, usw., womit wir in  jeder
Rasterzeile  die  Horizontalverschiebung
um eine  Rasterzeile  versetzt  vor  dem
Rasterstrahl herdrücken.                
Als Nächstes folgt das Üffnen des linken
und rechten Bildschirmrandes  durch  die
altbekannte  Befehlsfolge  zum schnellen
Runter- und wieder Hochschalten zwischen
38- und 40-Spalten-Darstellung.         
Durch  die  nun  folgenden 9 NOP-Befehle
verzögern wir solange, bis  der  Raster-
strahl  eine  Position  erreicht hat, zu
der  die   VIC-Schaltlogik   schon   die
gewünschte  Spritezeilenadresse  an  den
Datenbus angelegt hat, und somit der VIC
mit den gewünschten Spritedaten für die-
se Zeile gefüttert wurde. Jetzt schalten
wir die Y-Expansion wiederum ganz ab, um
das  Register für den nächsten Wert vor-
zubereiten.  Gleichzeitig  stellen   wir
damit  sicher, daß der Rest des Sprites,
der ggf. über  unseren,  vom  Raster-IRQ
behandelten,  Bereich  hinausragt  (z.B.
wenn wir das Sprite zu lang gedehnt  ha-
ben),  in  normaler  Darstellung auf den
Bildschirm gelangt. Es wird nun nur noch
der Index-Zähler im X-Register  für  den
nächsten  Schleifendurchlauf um 1 erhöht
und mit der  Anzahl  der  Raster-Effekt-
Zeilen  in  Register $02 verglichen. Ist
er noch kleiner als dieser Wert, so kön-
nen wir die Schleife wiederholen, um die
nächste  Rasterzeile  zu  bearbeiten. Im
anderen Fall sind wir am Ende angelangt,
wo  der  nächste  Interrupt  vorbereitet
wird,  bevor wir die IRQ-Routine wie ge-
wohnt beenden.                          
Damit  hätten  wir  auch  schon den Kern
unserer Routine  besprochen.  Experimen-
tieren  Sie  doch ein wenig mit der Ver-
zerrung, indem Sie die  Tabelle  "AD017"
ab  Adresse  $1600 mit Hilfe eines Spei-
chermoditors abändern. Sie werden  sehen
welch  lustige Deformierungen dabei ent-
stehen können! Beachten Sie  dabei,  daß
Sie  die  Form  des  Sprites im Speicher
niemals ändern, sonder daß die  Änderung
hardwaremäßig eintritt!                 
(Anm.d.Red.: Bitte wählen Sie nun den 2.
   Teil des IRQ-Kurs aus dem Textmenu)  
     Fortsetzung IRQ-Kurs (Teil 13)     
----------------------------------------
Kommen  wir  nun  zum  Beispiel  "STRET-
CHER.2".  Rein  äußerlich  unterscheidet
sich diese  Routine  nicht  von  "STRET-
CHER.1".  Auch hier wird dasselbe Sprite
wieder verzerrt dargestellt,  wobei  die
Verzerrung jedoch etwas anders ausfällt.
Rein  theroretisch haben wir nur die Ta-
belle "AD017" verändert, so daß dasselbe
Sprite  ein  anderes  Aussehen  erlangt.
Technisch  gesehen wurde dieses Beispiel
jedoch um ein zusätzliches  Feature  er-
weitert:  Am  Ende  unserer IRQ-Routine,
genau bevor  wir  die  Prozessorregister
wieder  zurückholen  und den IRQ mittels
RTI verlassen, haben wir den Befehl "JSR
$1300" hinzugefügt.  An  dieser  Adresse
befindet  sich nun eine kleine Unterrou-
tine, die es uns ermöglicht,  flexiblere
Dehnungen  zu  programmieren,  ohne, daß
wir uns Gedanken  über  den  Aufbau  der
Tabelle   "AD017"   machen  müssen.  Sie
greift   auf   eine    Tabelle    namens
"LSTRETCH" zu, die 21 Bytes enthält, die
jeweils  die Anzahl der Rasterzeilen an-
geben, die eine jede der 21 Spritezeilen
wiederholt  werden  soll.  Die   Tabelle
liegt ab Adresse $1700 und sieht folgen-
dermaßen aus:                           
lstretch:                               
 .byte $01   ;Spriteline01 1x Wdh.      
 .byte $02   ;Spriteline02 2x Wdh.      
 .byte $03   ;Spriteline03 3x Wdh.      
 .byte $04   ;Spriteline04 4x Wdh.      
 .byte $05   ;Spriteline05 5x Wdh.      
 .byte $06   ;Spriteline06 6x Wdh.      
 .byte $06   ;Spriteline07 6x Wdh.      
 .byte $05   ;Spriteline08 5x Wdh.      
 .byte $04   ;Spriteline09 4x Wdh.      
 .byte $03   ;Spriteline10 3x Wdh.      
 .byte $02   ;Spriteline11 2x Wdh.      
 .byte $01   ;Spriteline12 1x Wdh.      
 .byte $01   ;spriteline13 1x Wdh.      
 .byte $01   ;Spriteline14 1x Wdh.      
 .byte $01   ;Spriteline15 1x Wdh.      
 .byte $01   ;Spriteline16 1x Wdh.      
 .byte $01   ;Spriteline17 1x Wdh.      
 .byte $01   ;Spriteline18 1x Wdh.      
 .byte $01   ;Spriteline19 1x Wdh.      
 .byte $01   ;Spriteline20 1x Wdh.      
 .byte $00   ;Spriteline21 0x Wdh.      
Die Routine bei $1300 ("STCHART" ist ihr
Name) soll nun diese Werte in Folgen von
$00/$01-Bytes umrechnen, und in der  Ta-
belle  "AD017" ablegen, so daß wir ledi-
glich angeben müssen, welche Spritezeile
wie oft wiederholt werden soll. Hier nun
der Sourcecode der STCHART-Routine:     
stchart                                 
  ldx #20     ;Zähler in X-Reg. init.   
  lda #$ff    ;Akku mit $FF init.       
fill:                                   
  sta ad017,x   ;AD017-Tab mit $FF      
  inx           ; auffüllen (=letzte    
  cpx #21*5     ; Spritezeile immer bis 
  bne fill      ; Ende wiederholen)     
  ldy #$00      ;Index f. LSTRETCH-Tab  
  ldx #$00      ;Index f. AD017-Tab     
nextline:                               
  lda lstretch,y;1. Wert lesen und in   
  sta $FF       ; $FF abl.              
double:                                 
  dec $FF       ;Wert-1                 
  beq normal    ;Wert=0 -> nächst.Zeile 
  lda #$01      ;Sonst 1x wiederholen in
  sta ad017,x   ; AD017-Tab eintr.      
  inx           ;AD017-Index+1          
  jmp double    ;Und nochmal durchlaufen
normal:                                 
  lda #$00      ;Code für "nächste Zeile
  sta ad017,x   ; lesen" in AD017-Tab   
  inx           ;AD017-Index+1          
  iny           ;LSTRETCH-Index+1       
  cpy #20       ;Mit Ende vgl.          
  bne nextline  ;Nein, also nochmal     
  rts           ;Sonst Ende             
Wie  Sie sehen füllt diese Routine ledi-
glich die AD017-Tabelle so oft mit  $01-
Werten,  wie  der Bytewert einer Sprite-
zeile aus LSTRETCH groß ist.  Auf  diese
Weise  können  wir die Verzerrung einfa-
cher handhaben, was uns  in  Beispiel  2
noch  nichts  nutzt,  da  hier immer nur
dieselbe LSTRETCH-Tabelle benutzt  wird,
was  uns aber bei den Beispielen 3 und 4
sehr zugute kommt.                      
3) PROGRAMMBEISPIELE 3 UND 4            
Diese beiden Beispiele bauen nun auf den
Grundstein, den wir mit den ersten  bei-
den   Programmen  legten,  auf.  "STRET-
CHER.3" ist ein  Programm,  in  dem  Sie
einen  kleinen Flugsaurier auf dem Bild-
schirm flattern sehen. Diesen können Sie
nun mit Hilfe eines Joysticks in Port  2
nach  links  und  rechts  über den Bild-
schirm bewegen. Drücken Sie den Joystick
jedoch nach oben und  unten,  so  können
Sie  unseren kleinen Freund wachsen oder
wieder schrumpfen lassen, also  fließend
auf  5-fache Größe dehnen und wieder auf
seine Ursprungsgröße  zusammenschrumpfen
lassen.  Hierzu haben wir lediglich eine
Joystickabfrage  miteingebaut,  die  bei
nach unten gedrücktem Joystick einen der
Werte  aus  der  Tabelle  LSTRETCH  um 1
erhöht, bei nach  oben  gedrücktem  Joy-
stick  einen  dieser  Werte  erniedrigt.
Dadurch, daß nun gleichzeitig auch  nach
jedem  Rasterdurchlauf  die  Routine ST-
CHART aufgerufen wird,  wird  somit  die
kleine Änderung durch die Joystickrouti-
ne direkt in die AD017-Tabelle  übertra-
gen,  womit  ein quasi stufenloser Dehn-
und Staucheffekt entsteht.  Hierbei  ist
es  übrigens wichtig, die richtige Spri-
tezeile für eine Vergrößerung zu wählen.
In der Regel nimmt man dazu ein iterati-
ves  Verfahren, das die Anzahl der Spri-
tezeilen, die eine Rasterzeile mehr dar-
gestellt werden als Andere, so gleichmä-
ßig verteilt, daß der Dehneffekt  beson-
ders flüssig erscheint. Dies sind jedoch
Feinheiten,  die  wir  hier nicht weiter
besprechen möchten,  da  sie  nicht  zum
Thema gehören.                          
Das  Beispiel "STRETCHER.4" ist nun eine
weitere Kombination aus den Beispielen 2
und 3. Wir haben hier acht Mal den klei-
nen  Flugsaurier auf dem Bildschirm, der
in Sinuswellenform quasi über den  Bild-
schirm  "schwabbelt".  Hierbei haben wir
uns eines einfachen Tricks bedient:  Wir
nahmen zunächst die LSTRETCH-Tabelle aus
"STRETCHER.2",  die  eine Art runde Ver-
zerrung  in  Vertikalrichtung   erzeugt.
Diese Tabelle wird nun zyklisch durchge-
rollt. Pro Rasterdurchlauf kopieren  wir
die  Bytes  0-19  um ein Byte nach vorne
und setzen den Wert von Byte  20  wieder
bei  0 ein. Dadurch entsteht der Effekt,
als würden die kleinen Saurier  als  Re-
flektion  auf  einer  gewellten Wassero-
berfläche sichtbar sein!                
Diese  einfache  Variation  zeigt,   mit
welch simplen Methoden man eindrucksvol-
le Effekte durch Spritedehnung  erzielen
kann. Sie soll Sie wieder anregen, eige-
ne Experimente mit unserer neuen Raster-
Effekt-Routine durchzuführen.           
                                 (ih/ub)
                IRQ-KURS                
     "Die Hardware ausgetrickst..."     
                (Teil 14)               
----------------------------------------
Unser Kurs neigt sich langsam  dem  Ende
zu, und als wahre "Raster-Feinschmecker"
haben  wir  uns das beste Stückchen Code
ganz für den Schluß aufgehoben. In  die-
sem  und  den  nächsten beiden (letzten)
Kursteilen werden wir  uns  mit  Raster-
Tricks beschäftigen, die es uns ermögli-
chen, den  Bildschirm  des  C64  HARDWA-
REMÄSSIG in alle Richtungen zu scrollen.
"Hardwaremässig"  heißt,  daß  wir nicht
etwa die  Softscroll-Register  des  VICs
beschreiben,  und  dann alle 8 Scollzei-
len/-spalten den gesamten Bildschirm  um
8 Pixel (oder einen Charakter) weiterko-
pieren, sondern daß wir vielmehr den VIC
derart austricksen,  daß  er  uns  diese
Arbeit  von  alleine  abnimmt,  und zwar
ohne, daß wir auch  nur  einen  einzigen
Taktzyklus  zum Kopieren von Grafikdaten
verschwenden müssen! Die hierzu  notwen-
digen  Routinen heißen "VSP", zum verti-
kalen Scrollen, "HSP", zum  horizontalen
Scrollen,  sowie "AGSP", die eine Kombi-
nation Kombination aus den beiden erste-
ren  dartsellt. Mit ihnen können wir den
gesamten  Bildschirm   ganz   problemlos
(auch  Hires-Grafiken!) in alle Richtun-
gen verschieben.  Im  heutigen  Kursteil
wollen wir mit der VSP-Routine beginnen:
1) FLD UND VSP - DIE (UN)GLEICHEN BRÜDER
Sie  werden  sich  jetzt sicher wundern,
warum hier der Begriff "FLD", auftaucht,
war das doch eine der "einfacheren" Rou-
tinen,  die  wir  schon zu Anfang dieses
Kurses besprochen hatten. Doch  wie  ich
schon   öfter  erwähnte,  ist  die  FLD-
Routine meist der Schlüssel zu den  kom-
pexeren  Rastertricks,  und leistete uns
auch schon manchen guten Dienst  um  Ti-
mingprobleme   extrem  zu  vereinfachen.
Diesmal jedoch dreht sich  alles  direkt
um unseren kleinen Helfershelfer, da die
VSP-Routine  sehr stark mit ihm verwandt
ist, wenn die Beiden nicht  sogar  iden-
tisch  sind.  "VSP"  steht für "Vertical
Screen Position", was der  einfach  Aus-
druck  für  das ist, was die Routine be-
wirkt: durch sie wird es uns ermöglicht,
den gesamten Bildschirm  ohne  jeglichen
Kopieraufwand  vollkommen frei nach oben
oder unten zu scrollen.  Und  jetzt  der
Clou  an  der  ganzen Sache: FLD und VSP
unterscheiden sich lediglich durch einen
einzigen  NOP-Befehl  voneinander.  Fügt
man   Letzterern  den  Verzögerungs-NOPs
nach der  IRQ-Glättung  der  FLD-Routine
hinzu,  so  erhält  man  eine voll funk-
tionstüchtige VSP-Routine,  die  gleich-
zeitig  noch  einen FLD-Effekt miteinge-
baut hat. Damit Sie genau wissen,  wovon
wir  hier sprechen, sollten Sie sich auf
dieser MD einmal  die  Programmbeispiele
"FLD" und "VSP1" anschauen. Ersteres ist
die  FLD-Routine,  so wie wir sie zu Be-
ginn dieses Kurses kennengelernt hatten.
Durch Bewegen des  Joysticks  nach  oben
und  unten  können wir mit ihr den Bild-
schirm  nach  unten  "wegdrücken".   Das
Prinzip,  das  dabei verfolgt wurde war,
daß die FLD-Routine den Beginn der näch-
sten Charakterzeile vor dem Rasterstrahl
herschob,  indem  sie  ihn  ständig  mit
neuen  vertikalen  Verschiebeoffsets  in
Register  $D011  fütterte. Da er deshalb
glaubte, sich noch nicht in der  richti-
gen  Rasterzeile  zu befinden, in der er
die nächste Charakterzeile zu lesen hat-
te, "vergaß" er solange sie zu zeichnen,
bis  wir  ihm  durch  Beenden  der  FLD-
Schleife  die  Möglichkeit  dazu  gaben,
endlich die Rasterzeile zu erreichen, in
der  er  nun  tatsächlich  die versäumte
Charakterzeile lesen und anzeigen  durf-
te.  Egal, wieviele Rasterzeilen wir ihn
dadurch "vertrödeln" ließen,  er  begann
dann  immer  bei der Charakterzeile, die
er eigentlich  als  nächstes  aufzubauen
gehabt hätte, so als wenn der FLD-Effekt
nie  aufgetreten wäre. War das die erste
Charakterzeile des Bildschirms, so konn-
te  man  auch  problemlos den Bildschirm
erst in der Mitte oder am unteren  Bild-
schirmrand  beginnen lassen (so arbeitet
übrigens auch der Effekt,  mit  dem  Sie
die Seiten, die Sie gerade lesen umblät-
tern).                                  
2) DAS PROGRAMMBEISPIEL VSP1            
Wollen  wir  uns  nun  einmal  die  IRQ-
Routine  des  Programmbeispiels   "VSP1"
anschauen. Im Prinzip nichts besonderes,
da  sie,  wie  schon erwähnt, fast iden-
tisch mit der FLD-Routine ist.  Sie  ist
ab  Adresse $1100 zu finden und wird wie
die meisten unserer IRQ-Routinen von der
Border-IRQ-Routine, die  wir  immer  zum
Abschalten  des unteren und oberen Bild-
randes benutzen initialisiert:          
fld      pha        ;Prozessorregs.     
         txa        ; auf Stapel retten 
         pha                            
         tya                            
         pha                            
         dec $d019  ;VIC-ICR löschen    
         inc $d012  ;Glättungs-IRQ      
         lda #<irq2 ; vorbereitem       
         sta $fffe                      
         cli        ;IRQs freigeben     
ch       nop        ;Insgesamt 13 NOPs  
         ...        ; zum Verzögern bis 
         nop        ; zum nächsten      
         jmp ch     ; Raster-IRQ        
irq2     pla        ;Statusreg. u.      
         pla        ; IRQ-Adr. vom      
         pla        ; Stapel werfen     
         dec $d019  ;VIC-ICR freigeben  
         lda #$f8   ;Rasterpos. f. Bor- 
         sta $d012  ; der IRQ festlegen 
         ldx #<bord ;IRQ-Vektoren       
         ldy #>bord ; auf Border-IRQ    
         stx $fffe  ; zurücksetzen (für 
         sty $ffff  ; nächst. Durchlauf)
         nop        ;Verzögern bis      
         nop        ; zum Raster-       
         nop        ; zeilenende        
         nop                            
         nop                            
         nop                            
         nop                            
         lda $d012  ;den letzen Cyclus  
         cmp $d012  ;korrigieren        
         bne onecycle                   
onecycle lda #$18 ;25-Zeilen-Bildschirm 
         sta $d011; einschalten         
         nop      ;Insgesamt 16 NOPs zum
         ...      ;Verzögern für FLD    
Es folgt nun  der  alles  entscheindende
siebzehnte   NOP-Befehl,  der  den  VSP-
Effekt auslöst:                         
         nop      ;NOP für VSP          
Nun gehts weiter mit dem  normalen  FLD-
Code.  Entfernen  Sie  das obige NOP aus
dem Code, so erhalten  Sie  wieder  eine
ganz normale FLD-Routine!               
         lda $02         ;Zeilenz. holen
         beq fldend      ;Wenn 0 -> Ende
         ldx #$00        ;Zeiger init.  
fldloop  lda rollvsp,x   ;FLD-Offs. zum 
         ora #$18        ;Verschieben d.
         sta $d011       ; Charakterz.  
         lda colors1,x   ;Blaue Raster  
         sta $d020       ; im FLD-Be-   
         sta $d021       ; reich darst. 
         nop             ;Bis Zeilen-   
         nop             ; mitte verzö- 
         nop             ; gern         
         nop                            
         nop                            
         nop                            
         nop                            
         lda colors2,x   ;Rote Raster   
         sta $d020       ; Im FLD-Be-   
         sta $d021       ; reich darst. 
         nop             ;Verzögern bis 
         nop             ; Zeilenende   
         bit $ea                        
         inx             ;Zeilenz.+1    
         cpx $02         ;Mit $02 vgl.  
         bcc fldloop     ;ungl.->weiter 
fldend   nop             ;Verz. bis     
         nop             ; Zeilenende   
         nop                            
         nop                            
         nop                            
         lda #$0e        ;Normale       
         sta $d020       ; Bildschirm-  
         lda #$06        ; farben ein-  
         sta $d021       ; schalten     
         pla             ;Prozessorregs.
         tay             ; zurückholen  
         pla                            
         tax                            
         pla                            
         rti             ; und ENDE     
Die   Tabelle  "ROLLVSP"  enthält  Soft-
scroll-Werte für Register $D011 (nur die
untersten 3 Bit sind genutzt),  die  die
vertikale  Verschiebung  immer  um  eine
Rasterzeile vor  dem  Rasterstrahl  her-
schieben,  so daß der FLD-Effekt entste-
hen kann.                               
Die Tabellen "Color1" und "Color2" geben
die Farben an, die  die  FLD-Routine  im
weggedrückten   FLD-Bereich   darstellt.
Dies ist nur als "Verschönerung"  neben-
bei gedacht.                            
3) DAS FUNKTIONPRINZIP VON VSP          
Intern  kann man sich die Vorgehensweise
des VIC beim  Aufbauen  des  Bildschirms
mit all seinen Rasterzeilen folgenderma-
ßen vorstellen: Trifft unser armer  Gra-
fikchip  auf  eine  Rasterzeile,  in der
eigentlich  die  nächste  Charakterzeile
erscheinen  sollte,  so bereitet er sich
auf den gleich folgenden Zugriff auf das
Video-RAM vor, und  zählt  schon  einmal
einen   internen  Adresszeiger  auf  die
gewünschte Adresse um  40  Zeichen  nach
oben,  um  die folgenden 40 Bytes recht-
zeitig lesen zu können.  Nun  vergleicht
er  die Rasterstrahlposition ständig mit
seiner Startposition für den Lesevorgang
und hält beim Erreichen von Selbiger den
Prozessor für 42 Taktzyklen an, um  sei-
nen Lesezugriff durchzuführen. Durch die
FLD-Routine  wurde nun jedoch der Beginn
der gesamten Charakterzeile ständig  vor
dem  VIC  hergeschoben,  weswegen er sie
auch schön brav erst  später  zeichnete.
Unsere  VSP-Routine verfügt nun über ei-
nen einzigen NOP mehr,  der  hinter  der
IRQ-Glättung eingefügt wurde. Das bedeu-
tet, daß der FLD-Schreibzugriff auf  Re-
gister $D011, mit dem der vertikale Ver-
schiebeoffset um eins erhöht wird, exakt
2 Taktzyklen später eintritt als  sonst.
In  genau  diesen beiden Taktzyklen aber
hat der VIC schon  die  interne  Adresse
auf  das  Video-RAM um 40 Zeichen erhöht
und wartet jetzt auf das  Erreichen  der
Startposition  für  den  Lesevorgang. Da
der   in   unserem   Programm   folgende
Schreibzugriff  auf $D011 diese Position
für den VIC nun  aber  eine  Rasterzeile
weiterschiebt,  kann  er  diese Position
nie erreichen. Das Endergebnis, das  wir
dadurch erhalten ist Folgendes:         
* Der  FLD-Effekt greift, und wir lassen
  den  VIC  die  nächste  Charakterzeile
  eine Rasterzeile später zeichnen.     
* Der interne Adresszeiger des  VIC  auf
  die  Daten der nächsten Charakterzeile
  wurde um 40 Zeichen (also eine Charak-
  terzeile) erhöht.                     
* Da der VIC beim Erreichen der nächsten
  Rasterzeile wieder  glaubt,  er  müsse
  sich  auf die Charakterzeile vorberei-
  ten, zählt er den internen  Adresszei-
  ger  um weitere 40 Bytes nach oben und
  wartet auf die richtige  Startposition
  zum Lesen der nächsten 40 Bytes.      
* Lassen  wir  ihn  nun  tatsächlich die
  Charakterzeile lesen,  indem  wir  die
  FLD(VSP)-Routine beenden, so stellt er
  zwar,  wie  beim  normalen FLD-Effekt,
  wieder  Charakterzeilen  dar,   jedoch
  wurde durch das zweimalige Erhöhen des
  Adresszeigers um 40 Bytes eine gesamte
  Charakterzeile   übersprungen!  Hatten
  wir den FLD-Effekt also z.B.  vor  Er-
  reichen der ersten Charakterzeile ein-
  setzen lassen, so zeichnet der VIC nun
  nicht die erste,  sondern  die  ZWEITE
  Charakterzeile, da er sich ja 40 Bytes
  zu  weit nach vorne bewegte!!! Die er-
  ste Charakterzeile wurde  somit  ÖBER-
  SPRUNGEN!                             
Im Klartext bedeutet das: für  jede  Ra-
sterzeile,  die wir die FLD(VSP)-Routine
länger laufen  lassen,  überspringt  der
VIC eine Charakterzeile. Auf diese Weise
können  wir  also auch die Daten, die in
der Mitte des Video-RAMs liegen  am  An-
fang  des  Bildschirms  anzeigen lassen.
Der VIC liest nun aber pro  Rasterdurch-
lauf  immer  25  Charakterzeilen. Lassen
wir ihn also durch unseren VSP-Trick  40
Bytes  zu  spät auf die vermeintliche 1.
Charakterzeile  zugreifen  (es   handelt
sich  um  die  1. Charakterzeile die auf
dem Bildschirm zu sehen ist, jedoch  mit
den  Daten,  die  eigentlich erst in der
zweiten Charakterzeile  des  Bildschirms
erscheinen sollten), so hört für ihn der
Bildschirm  auch  40  Bytes zu spät auf.
Das Ergebnis: in der 25. Bildschirmzeile
liest und stellt der VIC die  Daten  des
sonst  ungenutzten  Bereichs  des Video-
RAMs  im  Adressbereich  von  $07E8  bis
$07FF  dar  (wenn  wir  von der Stanard-
Basisadresse des  Video-RAMs  bei  $0400
ausgehen).  Dies  sind  24  Zeichen, die
somit am Anfang der letzten  Bildschirm-
zeile  zu  sehen sind. Hieraufhin folgen
16 weitere Zeichen,  die  sich  der  VIC
wieder aus der Anfangsadresse des Video-
RAMs holt.                              
Zum  besseren  Verständnis  sollten  wir
vielleicht  noch klären, wie der VIC auf
das  Video-RAM  zugreift.  Selbiges  ist
immer  1024 Bytes (also exakt 1KB) lang,
obwohl der  Bildschirm  nur  1000  Bytes
groß  ist und somit die letzten 24 Bytes
nie sichtbar werden. Wie Sie nun wissen,
kann der VIC immer nur 16KB der 64KB des
C64 adresssieren, wobei die Lage  dieses
Bereichs allerdings auch verschoben wer-
den kann. Das heißt also,  daß  der  VIC
insgesamt  zwar  Adressen in einem 64KB-
Raum (16 Adressbits sind hierzu  notwen-
dig)  adressieren, aber gleichzeitig im-
mer nur auf 16KB für Grafikdaten zugrei-
fen kann. Möchte man diesen 16KB-Bereich
verschieben,  so kann das mit den unter-
sten zwei Bits von Adresse  $DD00  getan
werden. Im Normalfall stehen beide auf 0
und  lassen  den  VIC deshalb im Bereich
von  $0000  bis  $3FFF  arbeiten.  Diese
Adressen  dienen  als  Basis für den VIC
die ihm von der NMI-CIA in die  obersten
zwei  Adressbits  für seine RAM-Zugriffe
eingeblendet werden. Die Zugriffsadresse
ist also 16 Bit groß, wobei  die  beiden
Bits  0  und  1  aus  $DD00 immer in den
obersten zwei Bits (14 und 15)  erschei-
nen.  Die  Bits 10-13 dieser Adresse be-
stimmen die Basisadresse des Video-RAMs.
Sie werden von den  Bits  4-7  der  VIC-
Adresse  $D018 geliefert. Steht hier der
Bitcode $0001 (so wie  nach  Einschalten
des  Computers),  so  wird also zusammen
mit den Bits aus $DD00 die Adresse $0400
angesprochen, die im Normalfall die  Ba-
sisadresse  für  das  Video-RAM ist. Die
restlichen, zur Adressierung  benötigten
10   Bits  kommen  aus  dem  oben  schon
erwähnten, VIC-internen Adresszähler. Er
ist exakt 10 Bits  lang,  womit  maximal
1KB-Bereiche  adressiert  werden können.
Dadurch erklärt sich auch, warum der VIC
durch austricksen mit  der  VSP-Routine,
in  der letzten Zeile die 24 sonst unge-
nutzten  Zeichen  des  Video-RAMs   dar-
stellt: Da der 10-Bit-Zähler nun auf den
Offset $03E8 zeigt, werden von dort auch
die  Daten  gelesen. Hierbei findet dann
aber nach dem 24. Byte ein Öberlauf  des
10-Bit-Zählers   statt,  womit  selbiger
wieder auf 0  zurückspringt  und  wieder
den  Anfang  des  Video-RAMs adressiert.
Dadurch erklärt sich auch, warum in  der
letzten Bildschirmzeile nach den Zeichen
aus  $07E8-$07FF  wieder  die Zeichen ab
Adresse $0400 erscheinen.               
Bleibt  noch  zu  erwähnen, daß dasselbe
für das Color-RAM bei $D800  gilt.  Dies
ist immer an einer Fixadresse, wobei die
untersten  10 Bit ebenfalls aus dem Cha-
rakterzeilen-Adresszähler  kommen.   Das
heißt  also, daß die 24 sonst unsichtba-
ren Zeichen ihre Farbe aus dem ebenfalls
sonst ungenutzten Color-RAM-Bereich  von
$DBE8-$DBFF erhalten.                   
5) PROBLEME DIE BEI VSP ENTSTEHEN KÖNNEN
Die Nachteile, die durch die  Charakter-
verschiebung   entstehen,  sollten  auch
nicht unerwähnt bleiben:                
Durch  die  Verschiebung  der Video-RAM-
Daten am Ende des Bildschirms um 24 Zei-
chen nach rechts, ist natürlich auch die
gesamte  Grafik  in  zwei  Teile zerlegt
worden, weswegen ein Scroller nicht ganz
so einfach durchzuführen ist. Um  diesen
Fehler  auszugleichen  müssen  wir einen
zweiten Video-RAM-Bereich verwalten,  in
dem  die  gesamte  Grafik  um 24 Zeichen
versetzt abgelegt sein  muß.  In  diesem
Video-RAM  sind  dann die ersten 24 Zei-
chen ungenutzt. Durch einen  stinknorma-
len Rasterinterrupt können wir dann pro-
blemlos vor Beginn  der  Charakterzeile,
an  der  der VIC-Adresszeiger überläuft,
auf  das  zweite  Video-RAM  umschalten,
wobei  dieses  dann zwar an der überlau-
fenden Adresse ausgelesen wird, was  je-
doch  durch  unsere  Verschiebung wieder
ausgeglichen wird.                      
Ein  weiterer Nachteil, der bei VSP ent-
steht, liegt auf der Hand:  Die  letzten
acht  Bytes  des jetzt sichtbaren Video-
RAMs sind die  Adressen,  in  denen  die
Spritepointer abgelegt werden. Das heißt
also, daß wir beim gleichzeitigen Anzei-
gen von  Grafikzeichen  in  diesen  acht
Bytes  mit  der  Spriteanzeige  Probleme
bekomen. Jedoch auch dies ist zu  bewäl-
tigen.  Entweder, indem man so geschickt
arbeitet, daß in  diesen  Bereichen  auf
dem  Bildschirm  nur Zeichen in der Hin-
tergrundfarbe zu sehen sind (bei schwar-
zem Hintergrund einfach das Color-RAM in
den  Adressen  von $DBF0-$DBFF ebenfalls
auf schwarz setzen). Oder aber indem wir
in diesen Zeichen  immer  nur  dann  die
eigentlichen  Video-RAM-Werte eintragen,
wenn sie ausgerechnet vom  VIC  benötigt
werden  (also  auch  exakt vor dem Lesen
der  Öberlauf-Charakterzeile),  und  an-
sonsten  die Spritepointer hineinschrei-
ben. Da der VIC schon 42 Taktzyklen nach
Beginn der Rasterzeile, in der die  Cha-
rakterzeile  beginnen  soll,  die Video-
RAM-Daten gelesen hat, können  wir  also
getrost wieder die Spritepointer zurück-
schreiben und haben  so  lediglich  eine
einzige  Rasterzeile, in der die Sprites
garnicht (oder nur  verstümmelt)  darge-
stellt werden können.                   
Soll  der  gesamte   Bildschirm   einmal
durchgescrollt  werden  können,  so  muß
davon ausgegangen werden, daß maximal 25
Rasterzeilen am Beginn  des  Bildschirms
ungenutzt  bleiben  müssen,  da in Ihnen
das Timing  für  den  VSP-Effekt  unter-
bracht  werden  muß.  Da pro Rasterzeile
der interne Adresspointer um  eine  Cha-
rakterzeile weitergezählt wird, muß also
im  Maximalfall  in  25 Rasterzeilen der
VSP-Effekt eingesetzt werden.  Will  man
den  Bildschirm nun konstant an eine be-
stimmten Position  beginnen  lassen,  so
muß nach der Anzahl der zu überspringen-
den Charakterzeilen wieder eine  normale
FLD-Routine  (ohne  das  zusätzlich NOP)
benutzt werden, um bis zur  25.  Raster-
zeile  nach  Bildschirmanfang  zu verzö-
gern, OHNE daß gleich alle 25 Charakter-
zeilen übersprungen werden.             
6) WEITERE PROGRAMMBEISPIELE            
Ausser der oben schon erwähnten  "VSP1"-
Routine  finden  Sie  auf dieser MD auch
noch zwei weitere Beispielprogramme, mit
den Namen "VSP2" und  "VSP3"  (wie  alle
unserer  Beispiele  werden  auch sie mit
"SYS4096" startet).  In  Ersterem  haben
wir  zusätzlich zum VSP-Effekt die unte-
ren  drei  Bits  von  $D011  zum   Soft-
scrollen  des  Bildschirms verwendet, so
daß der Bildschirm weich nach  oben  und
unten geschoben werden kann. Die Routine
"VSP3"  scrollt  das  gesamte  Video-RAM
ununterbrochen durch, wodurch quasi  ein
unendlicher   Horizontalscroller  durch-
geführt werden kann. Die 25  unbenutzten
Rasterzeilen  am  Bildschirmanfang,  die
für das VSP-Timing benötigt werden, sind
mit Sprites  hinterlegt,  in  denen  man
z.B.  in einem Spiel auch ein Scoreboard
unterbringen kann.                      
In der  nächsten  Folge  des  IRQ-Kurses
werden  wir  uns den horizonalen Hardwa-
rescroller "HSP" anschauen, bei dem  das
VSP-Prinzip  auf  die  Horizontale Bild-
schirmposition umgesetzt wurde.  (ih/ub)
                IRQ-KURS                
     "Die Hardware ausgetrickst..."     
                (Teil 15)               
----------------------------------------
Im letzten Kursteil hatten wir uns einen
ganz  besonderen Rastertrick angeschaut.
Anhand einer VSP-Routine hatten wir  ge-
lernt,  wie  einfach  es  ist, den Bild-
schirm des C64 HARDWAREMÄSSIG, also ohne
den  Prozessor  mit  großen  Bildschirm-
Verschiebe-Aktionen  zu  belasten,  nach
oben und unten zu scrollen. Dabei unter-
schied  sich  die  VSP-Routine von einer
FLD-Routine in nur einem NOP-Befehl, der
das  nötige  Timing  erzeugte,  um   den
gewünschten  Effekt auszulösen. Der FLD-
Effekt war, wie wir gesehen hatten  maß-
geblich daran beteiligt, daß der VIC das
Lesen  von  einigen Charakterzeilen ver-
gaß, weswegen wir  in  der  Lage  waren,
einzelne  Bereiche im Video-RAM zu über-
springen, und so den Bildschirm beliebig
nach oben und unten zu scrollen. In die-
sem  Kursteil soll es nun um einen nahen
Verwandten von VSP gehen. Wir werden den
HSP-Effekt besprechen. "HSP"  steht  für
"Horizontal  Screen  Position" und setzt
den VSP-Effekt quasi auf die Horizontale
um. Mit  ihm  können  wir  DEN  GESAMTEN
BILDSCHIRM  problemlos um bis zu 320 Pi-
xel nach rechts verschieben,  wobei  wir
den Prozessor nur läppische 3 Rasterzei-
len lang in Anspruch nehmen müssen.  Da-
mit  werden  schnell  scrollende Baller-
oder Jump'n Run Spiele, wie auf dem Ami-
ga  oder dem Super-NES von Nintendo auch
auf dem C64 möglich!                    
1) ZUM PRINZIP VON HSP                  
Wir  erinnern  uns:  Um  die VSP-Routine
funktionsfähig zu  machen,  mussten  wir
den  FLD-Effekt so anwenden, daß wir dem
VIC vorgaukelten, sich auf das Lesen der
nächsten Rasterzeile  vorzubereiten  und
seinen internen Lesezähler hochzuzählen,
um  die richtige Charakterzeilen-Adresse
anzusprechen. Als er nun seinen Lesevor-
gang  durchführen wollte machten wir ihn
durch FLD-Wegdrücken der Charakterzeilen
glauben, daß er doch noch nicht die ent-
sprechende  Zeile  erreicht hatte. Somit
übersprang er mehrere Adressen und somit
auch  Charakterzeilen.  Wir  ließen  ihn
also  die Charakterzeilen zu spät lesen,
was zu dem gewünschten Effekt führte.   
Einen  ähnlichen  Trick  können  wir nun
auch für die HSP-Routine  anwenden.  Wie
wir  ja  wissen, so liest der VIC ab der
Rasterposition $30 alle  8  Rasterzeilen
die  40  Zeichen,  die in den nächsten 8
Rasterzeilen zu sehen sein  sollen,  aus
dem Video-RAM, um sie anschließend anzu-
zeigen. Durch den FLD-Effekt  haben  wir
nun  schon  oft genung die erste Charak-
terzeile auf dem Bildschim vor ihm  her-
geschoben,  so  daß  der VIC diese Zeile
verspätet erreichte, und somit der Bild-
schirm nach unten weggedrückt wurde. Der
Witz  ist,  daß dieser Trick nun auch in
her Horizontalen funktioniert! Denn  so-
bald der VIC merkt, daß er sich in einer
Charakterzeile  befindet, in der er Zei-
chendaten zu Lesen und  Anzuzeigen  hat,
beginnt  er auch unverzüglich mit dieser
Arbeit, und das ohne noch auf die  hori-
zontale  Rasterposition  zu  achten,  um
ggf. festzustellen, daß er sich garnicht
am Zeilenanfang befindet! Wenn  wir  nun
also eine Art FLD-Routine einsetzen, die
nur für einen Teil der aktuellen Raster-
zeile vorschreibt, daß selbige noch kei-
ne  Charakterzeile  ist, und dann mitten
innerhalb dieser Zeile  von  uns  wieder
auf normale Darstellung zurückgeschaltet
wird,  so fängt der VIC auch prompt mit-
tendrin damit an die  Charakterdaten  zu
lesen  und  sofort auf den Bildschirm zu
bringen. Verzögern wir also  nach  einem
FLD  bis  zur Mitte der Rasterzeile, und
schalten dann wieder zurück, so wird der
Video-RAM Inhalt  um  exakt  20  Zeichen
nach  rechts versetzt auf dem Bildschirm
dargestellt, wobei die 20  letzten  Zei-
chen,  die  ja nicht mehr in diese Text-
zeile passen, automatisch  erst  in  der
nächsten  Zeile erscheinen. Es kommt so-
gar noch besser: aufgrund eines internen
Timers des VIC, der das Lesen  der  Cha-
rakterzeilen  mitbeeinflusst (es sei den
wir tricksen ihn aus) führt der VIC  den
Lesefehler  in JEDER WEITEREN Charakter-
zeile ebenso durch, so  daß  es  genügt,
lediglich  am Bildschirmanfang einmal um
einen bestimmten Wert zu  verzögern,  um
DEN  GESAMTEN  Bildschirm  wie gewünscht
nach rechts versetzt darzustellen!!!    
Um den Trick nun umzusetzen  müssen  wir
wiefolgt vorgehen: zunächst schalten wir
in   $D011  den  vertikalen  Verschiebe-
Offset (wird in  den  untersten  3  Bits
festgelegt)  auf  1,  so daß für den VIC
die erste Charakterzeile erst  eine  Ra-
sterzeile  nach  Beginn  des  sichtbaren
Bildschirmfensters folgt. Dieser  Beginn
liegt  normalerweise in Rasterzeile $30.
Durch die  Verschiebung  legen  wir  die
erste  vom VIC zu lesende Charakterzeile
jedoch in Rasterzeile $31. Verzögern wir
nun jedoch in Rasterzeile  $30  um  eine
bestimmte  Anzahl Taktzyklen, und schal-
ten wir dann die  horizontale  Verschie-
bung  mittendrin wieder auf 0 zurück, so
merkt der VIC  plötzlich,  daß  er  sich
doch  schon  in einer Charakterzeile be-
findet und fängt einfrig  damit  an  die
Charakterdaten  zu  lesen  und  auf  dem
Bildschirm   darzustellen.   Wohlgemerkt
obwohl  er sich schon nicht mehr am Zei-
lenanfang befindet, sondern  mitten  in-
nerhalb  dieser  Rasterzeile!  Für jeden
Taktzyklus,  den  wir  mehr   verzögern,
stellt  der  VIC  die  Charakterdaten um
jeweils ein Zeichen (also 8 Pixel)  wei-
ter  rechts  dar.  Schalten  wir also 10
Takte nach Beginn des linken  Bildrandes
die  vertikale  Verschiebung ab, so wird
die Charakterzeile exakt 10 Zeichen nach
rechts versetzt gezeichnet.  Dies  setzt
sich,  wie  oben schon erwähnt, über den
gesamten Bildschirm, also auch  für  die
folgenden  24  weiteren Charakterzeilen,
fort, womit auch der gesamte  Bildschirm
um 10 Zeichen versetzt angezeigt wird!  
2) DIE UMSETZUNG                        
Wie das Ganze dann aussieht, können  Sie
sich  in  den  Programmbeispielen "HSP1"
und "HSP2" anschauen. Sie  werden  beide
mit  ",8,1"  geladen  und  durch SYS4096
gestartet. HSP2 unterscheidet  sich  von
HSP1 nur darin, daß das Zeichenwirrwarr,
das  durch  den HSP-Effekt in der ersten
Rasterzeile  zu  sehen  ist,  mit  einem
schwarzen  Rasterbalken  unsichtbar  ge-
macht wurde.                            
Kommen wir nun also zu der Routine,  die
uns  diesen  Effekt erzeugt. Sie ist vom
Aufbau   eigentlich    recht    einfach.
Zunächst  einmal befindet sich eine Ini-
tialisierungsroutine ab  Adresse  $1000,
die wie die meisten unserer IRQ-Routinen
das ROM abschaltet, um den IRQ-Vektor am
Speicherende  verwenden  zu  können, und
anschließend   einen   Border-Raster-IRQ
initialisiert,  der uns zunächst einmal,
wie immer, den unteren und oberen  Bild-
schirmrand   abschaltet.  Sie  steht  ab
Adresse $1200 im Speicher  und  ist  ge-
folgt von einer Routine zum Auslesen des
Joysticks,  sowie der Routine "Control",
die die  Joystick-Daten  auswertet,  den
Bildschirmverschiebeoffset berechnet und
damit  die  HSP-IRQ-Routine beeinflusst.
Selbige ist ab Adresse $1100 zu  finden.
Dir Border-IRQ-Routine legt diese Adres-
se in den IRQ-Vektoren  bei  $FFFE/$FFFF
ab,  und  gibt dem VIC vor, den nächsten
Raster-IRQ in Rasterzeile  $2D  auszulö-
sen.                                    
Wird unsere HSP-Routine  nun  in  dieser
Rasterzeile  aufgerufen,  so glätten wir
zunächst den IRQ, nach der uns  mittler-
weile altbekannten Methode. Ab dem Label
"Onecycle"  steht nun unsere eigentliche
Routine, die wiefolgt aussieht:         
onecycle:                               
      lda #$19   ;Bildsch. 1 Zeile nach 
      sta $d011  ; unten scrollen       
      ldy #$08   ;Verzögerungsschleife  
wy    dey        ; um den richtigen     
      bne wy     ; Moment abzupassen    
      jsr cycles ;Verzögerung 12 Takte  
      lda #$18   ;Wert f. 1 Zeile zurück
redu1 beq redu2  ;2 oder 3 Take verz.   
redu2 bne tt     ;ans Ende verzweigen   
      nop        ;20 NOPs die für das   
      nop        ; Timing später SEHR   
      nop        ; WICHTIG sind, ob-    
      nop        ; wohl sie hier        
      nop        ; übersprungen werden!!
      nop                               
      nop                               
      nop                               
      nop                               
      nop                               
      nop                               
      nop                               
      nop                               
      nop                               
      nop                               
      nop                               
      nop                               
      nop                               
      nop                               
      nop                               
tt    sta $d011  ;Wert schreiben        
      lda #$0e   ;Bildschirmfarben set- 
      sta $d020  ; zen (innerhalb dieser
      lda #$06   ; Befehle wird die Cha-
      sta $d021  ; rakterz. gelesen!)   
Zu  Beginn  unserer   IRQ-Routine   wird
zunächst  also  eine vertikale Verschie-
bung um eine Rasterzeile in $D011 einge-
tragen  (Bits  0-2  enthalten  den  Wert
%001=$01). Dadurch, daß der  HSP-IRQ  in
Rasterzeile  $2D  aufgerufen  wurde, und
daß die IRQ-Glättung 2 Rasterzeilen ver-
braucht,  befinden  wir  uns nun also in
Rasterzeile $2F. Wir verzögern nun  noch
mit  der  folgenden Zählschleife und dem
JSR-Befehl um eine knappe weitere Zeile,
wobei exakt die Position abgepasst wird,
an der sich der  Rasterstrahl  genau  am
Anfang  des linken Randes des sichtbaren
Bildschirms befindet  (so  wie  bei  der
Routine  zum  Abschalten  der seitlichen
Bildschirmränder). Hierbei  gehöhrt  der
"LDA #$18"-Befehl ebenfalls zur Verzöge-
rung. Er initialisirt  den  Akku  jedoch
gleichzeitig schon mit dem Wert vor, den
wir  in  $D011  schreiben müssen, um die
vertikale Verschiebung wieder abzuschal-
ten  (die unstersten drei Bits enthalten
den Wert 0). Nun folgt wieder eine  sehr
ausgeklügelte  Verzögerung, um Taktgenau
eine ganz bestimmte Rasterposition abzu-
passen. Beachten Sie bitte, daß die Rou-
tine an dieser Stelle von der oben schon
erwähnten "Control"-Routine  modifiziert
wird,  um  die  jeweils gewünschte Bild-
schirmverschiebung  zu  erreichen.   Wir
haben  hier nun zwei Branch-Befehle, die
jeweils mit  Labels  versehen  sind, und
denen  20 NOP-Befehle folgen. Zum Schluß
steht dann der STA-Befehl, mit  dem  der
Wert  des  Akkus  in  $D011  eingetragen
wird, um nach der  gewünschten  Verzöge-
rung den VIC zum Lesen der Charakterzei-
le  zu  bewegen.  Schauen  wir  uns  nun
zunächst  die  beiden Branches und deren
Bedeutung an:                           
      lda #$18   ;Wert f. 1 Zeile zurück
redu1 beq redu2  ;2 oder 3 Take verz.   
redu2 bne tt     ;ans Ende verzweigen   
Durch  den  vorherigen  LDA-Befehl,  der
einen  Wert  ungleich  Null lädt, wissen
wir, daß das  Zero-Flag  in  jedem  Fall
gelöscht ist. Ausserdem scheint der "BEQ
REDU2"-Befehl unsinnig zu sein, zumal er
exakt zum nächsten Befehl weiterspringt.
Wie  aber  auch  schon  bei unserer IRQ-
Glättungsroutine  hat   dieser   Branch-
Befehl  die Aufgabe, exakt einen Taktzy-
klus  lang  zu  verzögern.  Ein  Branch-
Befehl benötigt mindestens zwei Takte um
ausgeführt  zu  werden. Trifft die abge-
fragte  Bedingung  nicht  zu,  so   wird
gleich beim nächsten Befehl fortgefahren
(so  wie  das  hier  auch der Fall ist).
Trifft  die  Bedingung  jedoch  zu,   so
dauert  der  Branch-Befehl einen Taktzy-
klus länger, in dem  der  Prozessor  den
Sprung-Offset   auf  den  Programmzähler
aufaddieren muß. Soll nun um eine gerade
Anzahl Zyklen verzögert werden, weil der
Bildschirm um eine gerade Anzahl Zeichen
verschoben werden soll,  so  steht  hier
ein  BEQ-Befehl,  der  nur 2 Zyklen ver-
braucht.  Soll  eine   ungerade   Anzahl
verzögert werden, so wird von der Routi-
ne "Control", im Label "REDU1" der Opco-
de  für  einen  BNE-Befehl  eingetragen,
womit die Routine einen Taktzyklus  län-
ger  dauert,  und  somit  auch  ungerade
verzögert. Der nun  folgende  BNE-Befehl
ist immer wahr und verzögert somit immer
3 Taktzyklen (da dies eine ungerade Zahl
ist,  verhält  es  sich  also eigentlich
umgekehrt  mit  der  Änderung  des  BEQ-
Befehls für gerade und ungerade Verzöge-
rung). Bei diesem Befehl wird von  "Con-
trol"  die Sprungadresse modifiziert. Je
nach dem, wieviele weitere Zyklen verzö-
gert  werden  müssen,  trägt die Routine
einen  Offset  auf  die  folgenden  NOP-
Befehle  ein.  Der Befehl verzweigt dann
nicht mehr auf das Label  "TT",  wo  der
Akkuinhalt  nach $D011 geschrieben wird,
sondern auf einen NOP-Befehl davor.  Ei-
ner  dieser Befehle verzögert dann immer
um 2 Taktzyklen. Hierzu sollten wir  ei-
nen  Blick auf die Routine "Contol" wer-
fen:                                    
control:                                
    ldx #$d0      ;Opcode für BNE in    
    stx redu1     ; REDU1 ablegen       
    lda xposhi    ;X-Verschiebungs-     
    sta xposhib   ; zähler kopieren     
    lda xposlo    ; (Low- und High-     
    sta xposlob   ;  byte)              
    and #$08      ;Bit 3 v. XLo ausmask.
    bne co1       ;<>0, dann            
    ldx #$f0      ;Opcode für BEQ in    
    stx redu1     ; REDU1 ablegen       
co1 lsr xposhib   ;X-Verschiebung       
    ror xposlob   ; (16 Bit) durch 4X   
    lsr xposhib   ; Rotieren nach rechts
    ror xposlob   ; mit 16 dividieren   
    lsr xposhib                         
    ror xposlob                         
    lsr xposhib                         
    ror xposlob                         
    sec           ;Subtraktion vorber.  
    lda #$14      ;20 NOPS              
    sbc xposlob   ; minus XPos/16       
    sta redu2+1   ;als Sprungoffset für 
                  ; BNE bei REDU2       
    lda xposlo    ;Alte X-Versch. laden 
    and #$07      ;unterste 3 Bits isol.
    ora #%00011000;Standard-Bits setzen 
    sta softmove+1;in hor.Scroll eintr. 
    rts                                 
Zu  Allererst trägt die Control-Routine,
die innerhalb des Border-IRQs aufgerufen
wird, in das Label "REDU1" den Wert  $D0
ein,  der  dem Opcode für den BNE-Befehl
entspricht. Hieran  anschließend  werden
die  Inhalte der Labels "XPOSLO" und "X-
POSHI" in die Labels "XPOSLOB"  und  "X-
POSHIB"  kopiert,  was  für  die Offset-
Berechnung später notwendig  ist.  Diese
Labels sind im Source-Code des Programms
auf  die Zeropage-Adressen $02, $03, $04
und $05 vordefiniert. "XPOSLO"  und  "X-
POSHI" enthalten einen 16-Bit Zähler für
die  horziontale Bildschirmverschiebung,
die von der Joystickabfrage,  die  eben-
falls während des Border-IRQs aufgerufen
wird,  den  Joystickbewegungen  entspre-
chend hoch  oder  runter  gezählt  wird.
Dieser  Zähler kann also einen Wert zwi-
schen 0 und 320 enthalten.  Da  mit  der
HSP-Routine   der   Bildschirm   nur  in
Schritten von einzelnen Zeichen (also  8
Pixeln) versetzt werden kann, muß unsere
Routine  zum  weichen  Scollen des Bild-
schirms auch noch den horizontalen  Ver-
schiebeoffset des Bildschirms verändern.
Diese   Verschiebung  wird  in  Register
$D016 des  VICs  festgelegt,  wobei  die
untersten  3 Bits unseres XPOS-Wertes in
die untersten 3  Bits  dieses  Registers
gelangen  müssen. Die Bits 3-9 des XPOS-
Wertes  enthalten  nun   (dreimal   nach
rechts  verschoben)  die Anzahl Zeichen,
und somit auch Taktzyklen, die die  HSP-
Routine verzögern muß, damit der VIC die
Charakterzeile  erst  an der gewünschten
Position liest. Ist also das 3. Bit (das
das 0. Bit der Anzahl ist)  gesetzt,  so
muß  eine ungerade Anzahl Zeichen verzö-
gert werden. In  dem  Fall  enthält  das
Label  "REDU1" schon den richtigen Wert,
nämlich den Opcode für  den  BNE-Befehl,
der zusammen mit dem BNE-Befehl bei "RE-
DU2" sechs  (also  eine  gerade  Anzahl)
Taktzyklen   verbraucht.   Ist   Bit   3
gelöscht, so enthält der Akku  nach  dem
"AND #$08"-Befehl den Wert 0 und es wird
vor  dem Weitergehen im Programm der Op-
code für den BEQ-Befehl in "REDU1"  ein-
getragen.  Damit wird in der HSP-Routine
um 2+3=5  (also  eine  ungerade  Anzahl)
Taktzyklen  verzögert.  Nun muß noch er-
mittelt  werden,  wieviele   zusätzliche
NOPs  zur Verzögerung notwendig sind. Da
ein  NOP-Befehl   immer   2   Taktzyklen
braucht,  wird nur die halbe Anzahl NOPs
benötigt, um enstsprechend viele Zeichen
(Taktzyklen) lang zu verzögern. Da zudem
noch die 3 Bit Verschiebeoffset in  XPOS
stehen,  muß  dieser Wert durch 16 divi-
diert werden, um den gewünschten Wert zu
erhalten. Diese Berechnung wird  an  der
Kopie  von  XPOS, also in den Labels "X-
POSLOB"  und   "XPOSHIB"   durchgeführt.
Nachdem   im  Low-Byte  dieser  Register
hiernach die Anzahl der benötigten  NOPs
stehen,  kann  nun die Sprungadresse für
den  BNE-Befehl  bei  "REDU2"  berechnet
werden.  Da Branch-Befehle immer nur ein
Byte mit dem relativen  Offset  zum  ak-
tuellen Programmzähler enthalten, müssen
wir  also  lediglich  angeben,  wieviele
NOPs übersprungen  werden  müssen.  Dies
wird  durch  die Gesamtanzahl NOPs minus
der benötigten  Anzahl  NOPs  errechnet,
und  in  Adresse  "REDU2"+1 eingetragen.
Die Verzögerung sollte nun  also  sauber
funktionieren.                          
Zu guter Letzt wird noch der horizontale
Verschiebeoffset   für   das  horzontale
Softscrolling  ermittelt,   indem    aus
"XPOSLO" die untersten drei Bits ausmas-
kiert werden. Da diese Bits im  Register
$D016  landen  müssen,  das auch für die
38/40-Zeichendarstellung und den  Multi-
colormodus zuständig ist, setzen wir die
entsprechenden  Bits  mit  dem folgenden
OR-Befehl. Der resultierende  Wert  wird
in  das  Label "SOFTMOVE"+1 eingetragen,
das in der HSP-Routine kurz vor dem oben
gezeigten Codestück steht:              
softmove lda #$00                       
         sta $d016                      
Hier wird also lediglich  der  Verschie-
beoffset   in  den  Operanden  des  LDA-
Befehls eingetragen,  der  dann  in  der
HSP-Routine  die  Bildschirmverschiebung
jeweils wie benötigt in $D016 einträgt. 
Damit  hätten  wir alle Einzelheiten der
HSP-Routine besprochen.  Wie  Sie  sehen
funktioniert  sie  sogar  noch einfacher
als die VSP-Routine. Vielleicht  experi-
mentieren  Sie  einmal ein wenig mit den
Programmbeispielen und  versuchen  einen
horizontalen  Endlosscroller  daraus  zu
machen.  Der   HSP-Effekt   funktioniert
übrigens  genauso  wie  VSP auch mit HI-
RES-Grafik. Im nächsten Kursteil  werden
wir  auch  das  noch  sehen, und die er-
staunlichste IRQ-Raster-Routine  kennen-
lernen  die je entdeckt wurde: die AGSP-
Routine nämlich,  die  eine  Kombination
aus  HSP  und VSP darstellt, und mit der
es problemlos möglich ist den kompletten
Bildschirm in ALLE Richtungen zu  scrol-
len,  ohne  große Kopieraktionen mit dem
Prozessor durchführen zu müssen!!!      
                                 (ih/ub)
                IRQ-KURS                
     "Die Hardware ausgetrickst..."     
                (Teil 16)               
----------------------------------------
Herzlich Willkommen zum 16. Teil unseres
Kurses  über die Rasterstrahlprogrammie-
rung. In den letzten  beiden  Kursteilen
hatten  wir  uns  mit  zwei IRQ-Routinen
beschäftigt,  die  das   hardwaremässige
Scrollen  des Bildschirms jeweils in der
Horizontalen (HSP-Routine) und  Vertika-
len (VSP-Routine) ermöglichten. Der Clou
an  diesen  beiden Routinen war, daß des
GESAMTE Bildschirm  vom  VIC  verschoben
wurde,  und  wir keinen einzigen Taktzy-
klus zum  Kopieren  von  Bildschirmdaten
verschwenden mussten. In diesem Kursteil
wollen  wir nun das "Sahnestückchen" der
Raster-IRQ-Programmierung    besprechen:
die   Kombinattion  aus  HSP-  und  VSP-
Routine, genannt "AGSP". Diese Abkürzung
steht für "Any  Given  Screen  Position"
und bedeutet, daß der Bildschirm in jede
Richtung verschoben werden kann, und das
natürlich HARDWAREMÄSSIG, also wie schon
bei  HSP und VSP ohne auch nur einen Zy-
klus zum Kopieren von Bildschirmdaten zu
verschwenden!                           
Stellen  Sie  sich vor, was für Möglich-
keiten sich hierdurch für  Spiele  erge-
ben:  so kann man z.B. problemlos Spiele
programmieren, die sich über  einer  be-
liebig  großen Oberfläche abspielen, die
in  alle  Richtungen  flüssig  gescrollt
werden  kann, ohne dabei mehr als 30 Ra-
sterzeilen pro Frame zu verbrauchen.  25
Rasterzeilen  lang ist der Prozessor ei-
gentlich nur mit dem Abpassen der  opti-
malen  Rasterstrahlpostion  für den HSP-
Effekt beschäftigt, der, wie  wir  schon
im  14. Kursteil sehen konnten, pro Cha-
rakterzeile, die der Bildschirm horizon-
tal  gescrollt werden soll, eine Raster-
zeile für das Timing verbraucht.        
Damit  Sie  sehen,  was  mit  der  AGSP-
Routine alles  möglich  ist,  haben  wir
Ihnen natürlich wieder zwei Beispielpro-
gramme vorbereitet. Sie heissen  "AGSP1"
und  "AGSP2"  und  werden  wie immer mit
",8,1" geladen und durch  ein  "SYS4096"
gestartet.  Desweiteren  finden  Sie auf
dieser MD eine  Demo  mit  Namen  "AGSP-
Demo",  in  der am eindrucksvollsten die
Möglichkeiten  der  AGSP-Routine  demon-
striert  werden.  Sie  sehen hier einige
Hiresgrafiken,  die  in  Sinuswellenform
auf-  und  abschwingen  und gleichzeitig
von links nach rechts scrollen.  Hierbei
werden jeweils neue Grafiken in die Gra-
fik einkopiert, so daß der Eindruck ent-
steht, daß der Bildschirm weitaus größer
sei,  als  eigentlich sichtbar. Das Demo
laden  Sie  mit  LOAD"AGSP-DEMO",8   und
starten es durch "RUN".                 
1) DAS AGSP-PRINZIP                     
Kommen wir nun also  zum  Arbeitsprinzip
von  AGSP,  das eigentlich recht schnell
erläutert ist:  Zunächst  einmal  passen
wir  die  erste  Rasterzeile  des  Bild-
schirms ab, an der die erste  Charakter-
zeile  gelesen werden muß. Hier nun las-
sen wir eine VSP-Routine, so wie wir Sie
im letzten Kurteil schon  kennenlernten,
zunächst  dem VIC vorgaugeln, daß er auf
den Beginn der  Charakterzeile  noch  zu
warten  hat,  indem wir eine horizontale
Bildschirmverschiebung  um  1  Pixel  in
$D011 angeben. Inmitten dieser Zeit soll
nun  die  VSP-Routine diese Verschiebung
wieder Aufheben, um so  den  VIC  sofort
die  Charakterdaten lesen zu lassen, und
ihn somit dazu  zu  zwingen,  den  Bild-
schirm  nach  rechts versetzt darzustel-
len. Die Anzahl Taktzyklen, die bis  zum
Zurückschalten  verzögert werden müssen,
muß dabei der  Anzahl  Zeichen  entspre-
chen, die der Bildschirm verschoben wer-
den soll. Hieran anschließend lasen  wir
eine  HSP-Routine folgen, die den Beginn
der nächsten Charakterzeilen vor dem VIC
herdrückt, um so  auch  die  zeilenweise
Verschiebung  des  Bildschirmanfangs  zu
erreichen. Da diese Routine  maximal  25
Rasterzeilen   benötigt  (nämlich  dann,
wenn die Darstellung erst 25  Charakter-
zeilen  später beginnen soll, müssen die
ersten 25  Rasterzeilen  des  sichtbaren
Bildschirms  immer  für  das  Timing der
AGSP-Routine verwendet werden, auch wenn
stellenweise weniger  Rasterzeilen  dazu
benötigt werden (z.B. wenn nur eine Zei-
le Verschiebung erzeugt werden muß). Für
diese Fälle müssen wir mit  Hilfe  einer
normalen  FLD-Routine  den Bildschirm um
die fehlende  Anzahl  Rasterzeilen  nach
unten drücken, damit der Bildschirm auch
immer  in  ein und derselben Rasterzeile
beginnt. Diese FLD-Routine wird in unse-
rem  Programmbeispiel  den VSP- und HSP-
Routinen vorgeschaltet. Sie  sorgt  also
gleichzeitig  für  immer gleiches Timing
und zudem für die  richtige  Positionie-
rung  in  der  Horzontalen.  Aus  diesem
Grund sind dann die ersten 25 Rasterzei-
len  des Bildschirms auch nicht zum Dar-
stellen von Bildschirmdaten  verwendbar.
In  unserem Programmbeispielen haben wir
hier jedoch  Sprites  untergebracht,  in
denen die Scrollrichtung angezeigt wird.
In  einem  Spiel  könnte  sich hier z.B.
auch eine Score-Anzeige befinden.       
2) DAS PROGRAMM                         
Wollen  wir  uns  nun die Funktionsweise
von AGSP  anhand  des  Programmbeispiels
"AGSP1" anschauen. Die Routinen zur Joy-
stickabfrage und Scrollgeschwindigkeits-
verzögerung  sollen hier nicht Thema un-
seres Kurses sein, sondern lediglich die
Routine, die uns den Bildschirm beliebig
im sichtbaren Fenster positioniert.  Zur
angesprochenen   Scrollgeschwindigkeits-
verzögerung sei erwähnt,  daß  wir  hier
beim Drücken und Loslassen des Joysticks
die Scrollrichtung verzögern, bzw. nach-
laufen lassen, so daß  der  Scrolleffekt
zunächst  träge anfängt und beschleunigt
und beim Loslassen erst abgebremst  wer-
den muß, bis er zum Stillstand kommt.   
Die Initialisierungsroutine zu AGSP1 bei
$1000 legt nun zunächst einen Border-IRQ
in  Zeile  $FB fest, wobei, wie schon so
oft in  unseren  Beispielen,  der  Hard-
IRQ-Vektor bei $FFFE/$FFFF benutzt wird.
Diese  Border-Routine (bei $1400) schal-
tet nun den  oberen  und  unteren  Bild-
schirmrand  ab  und  initialisiert einen
Raster-IRQ für  Rasterzeile  $18,  wobei
dann   unsere   eigentliche  IRQ-Routine
"AGSP" angesprungen wird.  Sie  befindet
sich  an  Adresse  $1200 und soll im nun
Folgenden ausführlich  beschrieben  wer-
den. Die AGSP-Routine soll nun der Reihe
nach folgende Arbeiten verrichten:      
* Verwaltung des Sprite-Scoreboards, das
aus zwei Spritereihen zu je acht Sprites
bestehen soll.                          
* Glätten des  IRQs,  um  ein  möglichst
exaktes Timing zu bewirken.             
* FLD-Routine benutzen, um die fehlenden
Rasterzeilen, die  von  VSP  ggf.  nicht
benötigt werden, auszugleichen          
*  VSP-Routine charakterweisen zum hoch-
und runterscrollen des Bildschirms  ein-
setzen                                  
* HSP-Routine zum charakterweisen links-
und rechtsscrollen des Bildschirms  ein-
setzen                                  
* horizontales und vertikales Softscrol-
ling zur exakten  Bildschirmverschiebung
durchführen                             
a) DIE SPRITELEISTE                     
Kommen wir nun also zum ersten Teil  der
AGSP-Routine,  die  zunächst das Sprite-
Scoreboard verwaltet. Wir verwenden hier
zwei Sätze zu  je  acht  Sprites,  deren
Spritedaten  in  den Adressen von $0C00-
$1000 zu  finden  sein  sollen  (Sprite-
Pointer  $30-$3f).  Um  die Pointer zwi-
schen beiden leichter umschalten zu kön-
nen   benutzen   wir   zwei   Video-RAM-
Adressen. Für die erste Zeile, die eh in
einem  unsichtbaren  Bildbereich  liegt,
wird das Video-RAM an Adresse $0000 ver-
schoben, so daß wir die Spritepointer in
den Adressen $03F8-$03FF  liegen  haben.
Für  die zweite Spritezeile wird das Vi-
deo-RAM an die (normale)  Adresse  $0400
gelegt,  womit  wir die Spritepointer an
den Adressen $07F8-$07FF vorfinden.  Da-
durch,  daß  durch  den  VSP-Effekt auch
diese Spritepointer als einzelne Zeichen
auf dem  Bildschirm  auftauchen  können,
müssen  wir die Pointer in jedem Raster-
durchlauf (Frame) wieder neu setzen,  da
die  Pointer  auf dem Bildschirm ja dur-
chaus auch  Bildschirmzeichen  enthalten
können.  Doch kommen wir nun endlich zum
Source-Code  der  AGSP-Routine,  der  in
seinem  ersten  Teil zunächst einmal die
Spriteinitialisierungen   vornimmt   und
eigentlich  einfach  zu  verstehen  sein
sollte:                                 
agsp:    pha       ;IRQ-Beginn -> also  
         txa       ; Prozessorregs.     
         pha       ; retten             
         tya                            
         pha                            
         lda #$1e  ;Y-Position aller    
         sta $d001 ; Sprites auf Raster-
         sta $d003 ; zeile $1E (dez. 30)
         sta $d005 ; setzen             
         sta $d007                      
         sta $d009                      
         sta $d00b                      
         sta $d00d                      
         sta $d00f                      
         lda #$01  ;Die Farbe aller     
         sta $d027 ; Sprites auf 1, also
         sta $d028 ; 'weiß' setzen      
         sta $d029                      
         sta $d02a                      
         sta $d02b                      
         sta $d02c                      
         sta $d02d                      
         sta $d02e                      
         lda #$ff  ;Alle Sprites ein-   
         sta $d015 ; schalten           
         lda #$80  ;Hi-Bit d. X-Pos. von
         sta $d010 ; Sprite 8 setzen    
         lda #$00  ;Sprite-Multicolor   
         sta $d01c ; ausschalten        
         clc       ;C-Bit f.Add. löschen
         lda #$58  ;XPos Sprite 0       
         sta $d000 ; setzen             
         adc #$18  ;$18 (dez.24) add.   
         sta $d002 ; u. XPos Spr.1 setz.
         adc #$18  ;$18 (dez.24) add.   
         sta $d004 ; u. XPos Spr.2 setz.
         adc #$18  ;usw.                
         sta $d006                      
         adc #$18                       
         sta $d008                      
         adc #$18                       
         sta $d00a                      
         adc #$18                       
         sta $d00c                      
         adc #$18  ;$18 (dez.24) add.   
         sta $d00e ; u. XPos Spr7 setz. 
Bis  hierhin hätten wir nun die wichtig-
sten Spritedaten initialisiert. Dadurch,
daß diese in  jedem  Frame  neu  gesetzt
werden,  können Sie im AGSP-Bereich pro-
blemlos auch noch acht  weitere  Sprites
sich  bewegen  lassen.  Sie müssen deren
Spritedaten dann nur am Ende  der  AGSP-
Routine  wieder in die VIC-Register ein-
kopieren. Nun folgt noch  die  Initiali-
sierung  der Spritepointer. Zunächst die
für die Sprites der ersten  Spritezeile,
deren Pointer ja im Video-RAM ab Adresse
$0000 untergebracht sind, und sich somit
in  den  Adressen von $03f8-$03ff befin-
den. Anschließend werden die Spritepoin-
ter für die zweite Spritereihe eingetra-
gen, die sich  im  normalen  Bildschirm-
speicher  bei  $0400,  in  den  Adressen
$07f8-$07ff befinden. Das  abschließende
schreiben des Wertes 3 in Register $DD00
stellt sicher, daß der 16K-Adressbereich
des  VICs  auf  den unteren Bereich, von
$0000 bis $3FFF, gelegt ist:            
         ldx #$30  ;Spr.Pointer Spr0=$30
         stx $03f8 ; bis Spr7=$37 in    
         inx       ; die Pointeradressen
         stx $03f9 ; $03F8-$03FF ein-   
         inx       ; tragen             
         stx $03fa                      
         inx                            
         stx $03fb                      
         inx                            
         stx $03fc                      
         inx                            
         stx $03fd                      
         inx                            
         stx $03fe                      
         inx                            
         stx $03ff                      
         inx       ;Spr.Pointer Spr0=$38
         stx $07f8 ; bis Spr8=$3F in    
         inx       ; die Pointeradressen
         stx $07f9 ; $07F8-$07FF ein-   
         inx       ; tragen             
         stx $07fa                      
         inx                            
         stx $07fb                      
         inx                            
         stx $07fc                      
         inx                            
         stx $07fd                      
         inx                            
         stx $07fe                      
         inx                            
         stx $07ff                      
         lda #$03  ;VIC-Adressraum auf  
         sta $dd00 ; $0000-$3FFF schalt.
b) DAS GLÄTTEN DES IRQS                 
Der nun folgende Teil  der  AGSP-Routine
ist für das Glätten des IRQs verantwort-
lich. Ausserdem wird hier der Border-IRQ
wieder als  nächste  IRQ-Quelle  festge-
legt,  womit diese Arbeit nun auch erle-
digt wäre:                              
     dec $d019   ;IRQ-ICR freigeben     
     lda #$1d    ;Nächster Raster-IRQ   
     sta $d012   ; bei Zeile $1D        
     lda #<irq2  ;Adresse Glättungs-    
     sta $fffe   ; IRQ in $FFFE/$FFFF   
     lda #>irq2  ; eintragen            
     sta $ffff                          
     cli         ;IRQs erlauben         
ch:  nop         ;Insgesamt 23 NOPs, in 
     ...         ; denen der Glättungs- 
     nop         ; IRQ ausgelöst wird   
     bne ch                             
Beachten  Sie  bitte,  daß  das Programm
hier nicht mehr normal fortgesetzt wird,
sondern daß innerhalb der  23  NOPs  auf
den  Glättungs-IRQ  bei  "IRQ2" gewartet
wird.  Selbiger  folgt   gleich   diesem
Stückchen  Source-Code,  wobei  ihm  ein
RTS-Befehl vorangestellt  ist,  der  mit
dem Label "Cycles" assoziiert wird. Die-
ser RTS-Befehl wird später  im  AGSP-IRQ
desöfteren  zum  Verzögern angesprungen.
Zusammen mit dem aufrufenden  JSR-Befehl
werden  so  12 Taktzyklen verbraucht (je
Befehl 6 Zyklen):                       
cycles:                                 
     rts         ;Verzög.-Unterroutine  
irq2:pla         ;(IRQ-) Prozessorstatus
     pla         ; und Rücksprungadresse
     pla         ; vom Stapel werfen    
     dec $d019   ;VIC-ICR freigeben     
     lda #$f8    ;Nächster Raster-IRQ   
     sta $d012   ;bei Zeile $F8         
     lda #<border;Adresse der Border-IRQ
     sta $fffe   ; -Routine in Vektor   
     lda #>border; bei $FFFE/$FFFF ein- 
     sta $ffff   ; tragen               
     lda #$c8    ;40-Zeichen-Darst.     
     sta $d016   ; einschalten          
     lda #$06    ;Video-RAM nach $0000  
     sta $d018   ; verschieben (für Spr.
                   Pointer 1.Spr.Zeile, 
                   sh. oberste 4 Bits!) 
     nop         ;verzögern             
     lda $d012   ;Und den letzten Takt- 
     cmp #$1d    ;Zyklus korrigieren    
     beq line    ; (IRQ-Glättung)       
line:lda #$00                           
     ...                                
c) DIE FLD-ROUTINE                      
Nun  folgt  der  wichtige Teil der AGSP-
Routine. Zunächst einmal noch ein  wenig
Verwaltungsarbeit.  Wir  setzen hier den
Bildschirmbereich, in dem das VSP-Timing
unergebracht ist auf schwarz und  setzen
schon  einmal die Y-Positionen der Spri-
tes für die zweite Spritezeile des  Sco-
reboards (wie Sie aus unseren Kursteilen
über die Spriteprogrammierung wissen, so
wird die neue Y-Koordinate erst dann vom
VIC  übernommen, wenn die ersten Sprites
fertig gezeichnet sind  -  somit  können
wir  die  neuen  Koordinaten  irgendwann
setzen, wenn der VIC die  alten  Sprites
noch zeichnet - was hier der Fall ist): 
line     lda #$00   ;Bildschirm und Rah-
         sta $d020  ; mein auf 'schwarz'
         sta $d021  ; schalten          
         jsr cycles ;3*12=36 Zyklen     
         jsr cycles ; verzögern         
         jsr cycles                     
         bit $ea    ;3 Zyklen verz.     
         ldy #$1e+21;YPos=YPos+21       
         sty $d001  ; und für die Spr0- 
         sty $d003  ; Spr7 setzen       
         sty $d005                      
         sty $d007                      
         sty $d009                      
         sty $d00b                      
         sty $d00d                      
         sty $d00f                      
Nach diesen mehr organisatorisch wichti-
gen  Aufgaben folgt nun die FLD-Routine,
die das Timing zur VSP-Routine  ausglei-
chen soll:                              
fld:  ldx #$27    ;Max. Anz. FLD-Durchl.
      ldy #$01    ;Index d011/d018-Tab. 
fldlp:jsr cycles  ;12 Zyklen verz.      
      lda field1,y;Wert für $d011 holen 
      sta $d011   ; und setzen          
      lda field2,y;Wert für $d018 holen 
      sta $d018   ; und setzen          
      nop         ;6 Zyklen verz.       
      nop                               
      nop                               
      iny         ;Tab-Index+1          
      dex         ;FLD-Durchläufe-1     
      cpx <fldcnt ;=erforderliche Anz.  
      bne fldlp   ; Durchl.?Nein->Weiter
      nop         ;Sonst 14 Zyklen      
      jsr cycles  ; verzögern           
      lda field2,y;und letzten Wert für 
      iny         ; $d018 setzen        
      sta $d018                         
 (Anm. d. Red.: Bitte wählen Sie nun den
  2. Teil des Kurses aus dem Textmenu)  
            IRQ-Kurs - 2.Teil           
----------------------------------------
Die  FLD-Schleife  benötigt  für   einen
Durchlauf  exakt  eine  Rasterzeile.  In
jedem Durchlaub wird vor Beginn der Cha-
rakterzeile  durch  die  horizonale Ver-
schiebung in $D011  der  Charakterzeile-
nanfang   vor   dem   Rasterstrahl  her-
gedrückt. Dies bewähltigen wir mit Hilfe
einer Liste namens "Field1".  Sie  wurde
im  Hauptprogramm der AGSP-Routine vori-
nitialisiert und befindet sich an Adres-
se  $0100.  Es  stehen  dort  Werte  für
$D011, die die zur normalen  Bildschirm-
darstellung  notwenidigen  Bits  gesetzt
haben, und deren Bits für der horizonta-
le  Bitverschiebung  jeweils um eine Ra-
sterzeile erhöht werden,  und  bei  acht
Rasterzeilen  Verschiebung  wieder auf 0
zurückgesetzt sind. Die Tabelle  ist  64
Einträge lang und enthält somit für jede
Rasterzeile   ab  dem  Beginn  der  FLD-
Routine einen  passenden  Wert  für  die
$D011-Verschiebung.  Sie  wird ebenso in
der VSP-Routine verwendet.  Die  Tabelle
"Field2"  erfüllt einen ähnlichen Zweck:
Sie enthält Werte  für  Register  $D018,
befindet   sich  an  Adresse  $0140  und
enthält ebenfalls 64  Werte.  Auch  ihre
Werte gelangen in jeder Rasterzeile (al-
so auch jedem FLD-Durchlauf) in das  Re-
gister  $D018,  womit  wir den Adressbe-
reich des  Video-RAMs  verschieben.  Die
Tabelle  enthält  20  Einträge,  die das
Video-RAM an Adresse  $0000  legen,  ge-
folgt von 44 Einträgen, die es an Adres-
se $0400 verschieben.  Auf  diese  Weise
schaltet  Sie  also exakt in der 21. Ra-
sterzeile nach  Beginn  der  FLD-Routine
auf  den  Bildschirm bei $0400 um, womit
wir die Spritepointer  auch  auf  diesen
Bereich  umschalten. Nach dieser 21. Ra-
sterzeile hat der VIC nämlich gerade die
erste Spritereihe  fertiggezeichnet  und
wir  bringen  ihn  so also dazu die auch
noch die zweite Reihe mit anderen  Poin-
tern  zu zeichnen. Der Grund, warum dies
über eine  Tabelle  geschehen  muß,  und
nicht  etwa  durch Abpassen der entspre-
chenden Position und dem dann  folgenden
Umschalten  liegt  auf der Hand: Braucht
die der FLD-Routine folgende VSP-Routine
z.B. 25 Rasterzeilen, um den  Bildschirm
25  Charakterzeilen tiefer darzustellen,
so läuft unsere FLD-Routine  nur  einmal
durch  und  endet, wenn die Sprites noch
längst  nicht  ganz  fertig   gezeichnet
sind.  Umgekehrt kann es auch passieren,
daß die VSP-Routine keine Zeit benötigt,
weil keine Verschiebung  notwendig  ist,
und  deshalb  die FLD-Routine 25 Raster-
zeilen lang laufen muß, damit der  Bild-
schirm  an  derselben  Position  wie  im
letzten Frame erscheint. In dem Fall muß
das  Umschalten  von   der   FLD-Routine
durchgeführt werden. Benutzen allerdings
beide  Routinen ein und dieselbe Tabelle
und denselben Index darauf, so übernimmt
automatisch die Routine die Umschaltung-
saufgabe,  die  gerade an der Reihe ist,
ohne, daß wir etwas dazutun müssen! Dies
mag verwirrender klingen als es ist:  Im
Endeffekt stellt die Tabelle sicher, daß
immer  in  der 21. Rasterzeile seit FLD-
Beginn, die  Spritepointer  umgeschaltet
werden  -  unabhängig davon, ob sich der
Prozessor zu diesem  Zeitpunkt  noch  in
der  FLD-  oder schon in der VSP-Routine
befindet!                               
d) DIE VSP-ROUTINE                      
Nach FLD folgt die VSP-Routine, die sich
von  der  ersteren nur darin unterschei-
det, daß sie mit zwei Zyklen Verzögerung
die Änderungen  in  $D011  einträgt  und
somit  nicht nur den Beginn der nächsten
Charakterzeile, sondern  auch  den  VIC-
internen  Adresszeiger  selbiger  erhöht
und  somit  eine  Charakterzeile   über-
springt:                                
vsp:  inx           ;Alter FLD-Zähler=  
      stx <vspcnt   ; VSP-Zähler+1      
      nop           ;8 Takte verzögern  
      nop                               
      nop                               
      nop                               
vsplp:nop           ;8 Takte verzögern  
      nop                               
      nop                               
      nop                               
      ldx field1+3,y;Wert aus d011-Tab+3
      stx $d011     ; nach $D011 kop.   
      nop           ;Nochmals 6 Takte   
      nop           ; verz. (Ausgleich  
      nop           ; zu FLD)           
      lda field2,y  ;Wert aus d018-Tab  
      sta $d018     ; nach $D018 kop.   
      nop           ;4 Zyklen bis Ende  
      nop           ; verz.             
      iny           ;Tab-Index+1        
      dec <vspcnt   ;VSP-Zähler-1       
      bne vsplp     ;<>0 -> Weiter      
      bit $ea       ;Sonst 7 Takte      
      nop           ; verzögern         
      nop                               
Wie  Sie  sehen,  ist  dies quasi unsere
FLD-Routine. Einziger Unterschied  liegt
in der Art, wie die beiden Tabellen aus-
gelesen und geschreiben werden.  Um  den
VSP-Effekt  zu  erzielen  kann dies hier
nicht  mehr  direkt   aufeinanderfolgen.
Ausserdem wird hier nicht mehr der näch-
ste Tabellenwert von  "Field1"  gelesen,
sondern der dritte Wert danach. Dies tun
wir, um in jedem Fall eine $D011-Wert zu
schreiben, der die Charakterzeile minde-
stens 1 Rasterzeile vor den Rasterstrahl
drückt. Durch die Zeit die zwischen  FLD
und  VSP  vergeht haben wir nämlich auch
schon eine Charakterzeile verloren,  und
damit  der  Y-Index nicht unnötig erhöht
werden muß greifen wir einfach auf einen
weiter entfernten Tabellenwert zu  (wie-
viele Rasterzeilen wir die Charakterzei-
le vor uns herschieben ist ja egal -  es
zählt  nur, daß sie vor uns hergeschoben
wird)!    Die    Anzahl     der     VSP-
Schleifendurchläufe  wird durch den FLD-
Zähler  ermittelt.  In  der  FLD-Routine
wurde  das  X-Register  mit dem Wert $27
initialisiert.  Nach  Abzug   der   FLD-
Durchläufe  enthält  das  X-Register nun
noch  die  erforderliche   Anzahl   VSP-
Durchläufe,  die  in  dem Label "VSPCNT"
(Zeropageadresse $069) bgelegt wird  und
von nun an als Zähler dient.            
e) DIE HSP-ROUTINE                      
Nun  folgt  dem  ganzen  noch  die  HSP-
Routine, die Sie ja noch aus dem letzten
Kursteil   kennen.  Wir  schreiben  hier
zunächst den nächsten Wert  der  Field1-
Tabelle  in $D011 und verzögern dann bis
zum gewünschten Punkt um den geschriebe-
nen  $D011-Wert-1  zu schreiben, mit dem
die horizontale Bildverschiebung erzielt
wird:                                   
HSP:  ldx field1+3,y;nächst. d011-Wert  
      stx $d011     ;schreiben          
      jsr cycles    ;Anfang sichtb.     
                    ; Bildschirm abwart.
      dex           ;d011-Wert-1        
redu1:beq redu2     ;Ausgleich für unge-
redu2:bne tt        ; rade Zyklen       
      nop           ;Insgesamt 20       
      ...           ; NOPs für das      
      nop           ; HSP-Timing        
tt    stx $d011     ;akt.Z. einschalt.  
Hier also drücken wir zunächst den  Cha-
rakterzeilenbeginn  vor den Rasterstrahl
und warten bis zum benötigten  Zeitpunkt
um  die vertikale Bildschirmverschiebung
um  eine  Rasterzeile  herunterzustellen
und  so  den HSP-Effekt zu erzielen. Die
merkwürdige BEQ/BNE-Folge dient dem Aus-
gleichen  eines  ggf. ungeraden Verzöge-
rungszeitraums. Das Label "tt" wird  von
der  Timingroutine  verändert um so ver-
schiedene  Zeitverzögerungen   innerhalb
der 20 NOPs zu erreichen.               
f) DAS SOFTSCROLLING                    
Dies ist die leichteste Aufgabe  unserer
IRQ-Routine.  Es  werden  hier lediglich
die Softscrollingwerte in der  Horizona-
len und Vertikalen in die Register $D016
und $D011 eingetragen. Die zu schreiben-
den Werte wurden von der  Timing-Routine
berechnet  und in den Operanden der LDA-
Befehle bei "HORIZO" und "VERTIC" einge-
tragen:                                 
horizo   lda #$00 ;Versch. vom linken   
         sta $d016; Bildrand            
vertic   lda #$00 ;Versch. vom oberen   
         sta $d011; Bildrand            
         ldx #$57 ;Verzögerungsschleife 
w1       dex      ;bis Bildanfang       
         bne w1                         
         ldy #$17 ;Video-RAM bei $0400  
         sty $d018; einsch.             
         lda $d011;$D011 laden          
         and #$1F ; relev. Bits ausmask.
         sta $d011; und schreiben       
         lda #$0e ;Bildschirmfarben     
         sta $d020; zurücksetzen        
         lda #$06                       
         sta $d021                      
         pla      ;Prozessorregs. vom   
         tay      ; Stapel holen und IRQ
         pla      ; beenden.            
         tax                            
         pla                            
         rti                            
Soviel nun also zu unserer  IRQ-Routine.
Sie  eredigt  für uns die entsprechenden
Aufgaben  zur  Bildverschiebung.  Aller-
dings  muß sie auch irgendwie mit Basis-
werten gefüttert werden, um  das  Timing
für  alle  Fälle genau aufeinander anzu-
stimmen. Dies übernehmen weitere Steuer-
routinen,  die wir im nächsten, und dann
auch letzten Teil dieses Kurses  bespre-
chen werden.                            
                                    (ub)
                IRQ-KURS                
     "Die Hardware ausgetrickst..."     
                (Teil 17)               
----------------------------------------
Herzlich  Willkommen zum 17. und letzten
Teil unseres IRQ-Kurses. In dieser  Aus-
gabe  möchten  wir  Ihre  Ausbildung ab-
schließen und Ihnen die letzten  Geheim-
nisse  der  AGSP-Routine  erläutern, mit
deren Besprechung wir ja schon im  letz-
ten Monat begannen.                     
Dort hatten wir zunächst die IRQ-Routine
besprochen, die es uns  ermöglicht,  den
AGSP-Effekt  durchzuführen.  Sie bestand
aus einer  geschickten  Kombination  der
Routinen FLD, VSP und HSP, sowie einigen
Befehlen   zum  Softscrollen  des  Bild-
schirms. Zudem  hatten  wir  noch  einen
einfachen  Sprite-Multiplexer  mit inte-
griert. Wie  Sie  also  sehen,  ist  die
AGSP-Routine  auch  ein  gutes  Beispiel
dafür,  wie  die  Raster-IRQ-Effekte  in
Kombination  miteinander  ein  perfektes
Zusammenspiel ergeben können.           
Prinzipiell  ist  die  im  letzten Monat
besprochene AGSP-IRQ-Routine also in der
Lage, uns den Bildschirm um jeden belie-
bigen  Offset  in  X-  und Y-Richtung zu
verschieben (in X-Richtung um 0-319,  in
Y-Richtung  um  0-255  Pixel). Damit wir
jedoch eine SPEZIELLE  Verschiebung  um-
setzen   können,  muß  die  AGSP-Routine
natürlich auch mit speziellen Parametern
gefüttert werden, die von einer  anderen
Routine in der Hauptschleife des Prozes-
sors vorberechnet werden  müssen.  Diese
Routine  exisitiert  natürlich  auch  in
unseren Programmbeispielen  "AGSP1"  und
"AGSP2",  sie  heißt "Controll" und soll
das Thema dieses Kursteils sein.        
1) DIE PARAMETER DER AGSP-ROUTINE       
Wollen wir zunächst einmal klären,  wel-
che  Parameter unsere AGSP-Routine benö-
tigt. Da es eine IRQ-Routine ist, können
wir selbige natürlich nicht  so  einfach
übergeben. Sie müssen teilweise in Zero-
pageadressen oder sogar direkt  im  Code
der IRQ-Routine eingetragen werden. Ins-
gesamt gibt es 5 Stellen, die wir ändern
müssen, damit die AGSP-Routine auch  die
Parameter  bekommt, die sie benötigt, um
eine ganz bestimmte Verschiebung  durch-
zuführen.  Die  Verschiebungen in X- und
Y-Richtungen werden wir im folenden  mit
XPos  und  YPos  bezeichnen. Unsere Bei-
spielprogramme verwalten auch  gleichna-
mige Variablen in der Zeropage. Sie wer-
den von der Joystickabfrage  automatisch
auf  die  zwei benötigten Werte gesetzt,
um die die AGSP-Routine  den  Bildschirm
in beide Richtungen verschieben soll. Da
dies  in  jedem  Frame  erneut geschieht
wird so  der  Eindruck  einer  flüssigen
Verschiebung    erzielt.   Da   die   X-
Verschiebung Werte größer als  256  auf-
nehmen  muß,  benötigen wir für den Wert
XPos also zwei Speicherstellen, die  von
unserem  Beispielprogramm in den Zeropa-
geadressen   $02/$03   in    Lo/Hi-Byte-
Darstellung  verwaltet  werden. Für YPos
genügt uns ein Byte, das in der  Zeropa-
geadresse $04 zu finden ist.            
Wollen  wir  nun jedoch zunächst heraus-
stellen,  welche  Parameter  aus  diesen
beiden  Werten  berechnet werden müssen,
damit  die  AGSP-Routine  die   korrekte
Bildschirmverschiebung durchführt:      
a) FLDCNT FÜR FLD UND VSP               
Wie  Sie  sich sicherlich noch erinnern,
so  benutzte  unsere  AGSP-Routine  eine
FLD-Routine,  um  das  Timing  zur  VSP-
Routine auszugleichen, für den Fall, daß
letztere weniger als 25  Charakterzeilen
vertikale         Bildschirmverschiebung
durchführen sollte. Um nun die  korrekte
Anzahl Zeilen zu verschieben, hatte sich
die  FLD-Routine  eines Zählers bedient,
der ihr angab, wieviel Mal sie  durchzu-
laufen  hatte.  Zur besseren Erläuterung
des Zählers hier noch  einmal  der  Kern
der FLD-Routine:                        
fld:  ldx #$27    ;Max. Anz. FLD-Durchl.
      ldy #$01    ;Index d011/d018-Tab. 
fldlp:jsr cycles  ;12 Zyklen verz.      
      lda field1,y;Wert für $d011 holen 
      sta $d011   ; und setzen          
      lda field2,y;Wert für $d018 holen 
      sta $d018   ; und setzen          
      nop         ;6 Zyklen verz.       
      nop                               
      nop                               
      iny         ;Tab-Index+1          
      dex         ;FLD-Durchläufe-1     
      cpx <fldcnt ;=erforderliche Anz.  
      bne fldlp   ; Durchl.?Nein->Weiter
Der besagte FLD-Zähler befindet sich nun
im X-Register, das mit dem Wert $27 vor-
initialisiert  wird.  Dieser Wert ergibt
sich aus der  Anzahl  Rasterzeilen,  die
für das VSP-Timing benötigt werden. Nor-
malerweise sollten dies 25  Rasterzeilen
sein. Da wir jedoch ein Spritescoreboard
mit  einer  Gesamthöhe von 42 (=$2a) Ra-
sterzeilen darstellen,  muß  auch  diese
Anzahl  Zeilen  übersprungen werden. Der
FLD-Zähler enthält diesen Wert minus  3,
da  ja auch schon durch die IRQ-Glättung
2  Rasterzeilen  verbraucht  wurden  und
nach  der  FLD-Routine der Zähler wieder
für die VSP-Routine um  1  erhöht  wird.
Dieser Zähler im X-Register wird nun pro
Rasterzeile  einmal herabgezählt, solan-
ge, bis  er  dem  Wert  in  FLDCNT  ent-
spricht,  womit die FLD-Schleife verlas-
sen wird. Aus dem verbleibenden Wert  im
X-Register,  der  dann genau dem FLDCNT-
Wert entspricht, ergibt sich der VSPCNT,
der  angibt,  wie  oft  die  VSP-Routine
durchlaufen  werden  muß.  Dieser Zähler
muß nicht eigens berechnet werden. Somit
ist VSPCNT also einer der  5  Parameter,
die  die  AGSP-Routine  benötigt. Er be-
rechnet sich aus YPos dividiert durch 8.
b) REDU1 UND REDU2 FÜR HSP              
Nachdem  also der Parameter für die VSP-
Routine und  somit  der  Chrakterzeilen-
Verschiebung  geklärt ist, wollen wir zu
den Parametern für die HSP-Routine  kom-
men.  Dies  sind  keine Parameter im ei-
gentlichen Sinne, sondern Änderungen  im
Code  der HSP-Routine, die eine Verzöge-
rung der gewünschten Dauer  durchführen.
Wie  Sie  sich  vielleicht  erinnern, so
wird mit jedem Taktzyklus, den die  HSP-
Routine das Zurücksetzen des Bildschirms
auf "Charakterzeile zeichnen" verzögert,
die Darstellung desselben um ein Zeichen
(also  8  Pixel) nach rechts verschoben.
Das heißt also, daß wir  XPOS/8  Taktzy-
klen verzögern müssen, um die gewünschte
Verschiebung zu erzielen. Auch hier zei-
ge ich Ihnen wieder einen Auszug aus der
HSP-Routine, der der  besseren  Erläute-
rung dienen soll:                       
HSP:  ldx field1+3,y;nächst. d011-Wert  
      stx $d011     ;schreiben          
      jsr cycles    ;Anfang sichtb.     
                    ; Bildschirm abwart.
      dex           ;d011-Wert-1        
redu1:beq redu2     ;Ausgleich für unge-
redu2:bne tt        ; rade Zyklen       
      nop           ;Insgesamt 20       
      ...           ; NOPs für das      
      nop           ; HSP-Timing        
tt    stx $d011     ;akt.Z. einschalt.  
Die HSP-Routine setzt die Y-Verschiebung
des Bildschirms nun zunächst hinter  den
Rasterstrahl,  um  so  dem VIC vorzugau-
keln, er befände sich noch nicht in  ei-
ner  Rasterzeile.  Anschließend wird mit
dem JSR-, dem DEX-,  und  den  BEQ-/BNE-
Befehlen auf den Anfang des linken Bild-
schirmrandes gewartet, wobei die  letzen
beiden  Befehle  ausschließlich  für das
taktgenaue HSP-Timing herangezogen  wer-
den.  An den Labels "REDU1" und "REDU2",
wird unsere  Parameterberechnungsroutine
später den Code modifizieren, so daß das
Timing  exakt  dem  gewünschten  XPos/8-
Offset entspricht.  Hierzu  dienen  auch
die 20 NOP-Befehle, die genau 40 Zyklen,
bzw.  Zeichen  verzögern, wodurch im ex-
tremfall alle Zeichen übersprungen  wer-
den können. Der anschließende STX-Befehl
setzt  dann  die  Darstellung wieder auf
Charakterzeilenbeginn,  womit  der   VIC
dazu gezwungen wird, sofort eine Charak-
terzeile zu lesen und  anzuzeigen.  Also
erst  hier  wird  der eigentliche Effekt
ausgeführt. Um  nun  das  Einsetzen  der
Charakterzeile  genau  abzutimen, müssen
wir also um XPOS/8 Taktzyklen verzögern.
Diese Verzögerung kann recht haarig  um-
zusetzen  sein,  wenn  es  sich dabei um
eine  ungerade  Anzahl  Zyklen  handelt.
Diese  Aufgabe  soll  der Branch-Befehl,
beim Label REDU1 lösen. Zunächst  einmal
sollte   erwähnt   werden,  daß  das  X-
Register nach dem DEX-Befehl IMMER einen
Wert ungleich null enthält, da  dort  ja
der  benötigte Wert für $d011 steht, der
immer größer als 1 ist, womit durch  den
DEX-Befehl nie auf 0 herabgezählt werden
kann.  Das  Zeroflag  ist beim Erreichen
des Labels REDU1  also  immer  gelöscht.
Wie  auch  schon  beim  Glätten des IRQs
benutzen wir nun also den Trick mit  den
Branch-Befehlen,  um  ggf. eine ungerade
Verzögerung zu  erzielen.  Wie  Sie  von
dort  vielleicht  noch  wissen, benötigt
ein  Branch-Befehl  immer  mindestens  2
Taktzyklen. Trifft die abgefragte Bedin-
gung nun zu, so dauert  die  Abarbeitung
des  Branchbefehls  immer  einen  Zyklus
mehr, also 3 Zyklen. Soll der Bildschirm
nun um eine gerade Anzahl  Zeichen  nach
rechts  verschoben  werden, so trägt die
Parameterberechnungsroutine  bei   REDU1
den   Assembler-Opcode  für  einen  BEQ-
Befehl  ein.  Da  diese  Bedingung   nie
erfüllt  ist,  werden  nur 2 Zyklen ver-
braucht und  direkt  mit  dem  folgenden
Befehl  bei REDU2 fortgefahren. Muß eine
ungerade  Anzahl  Taktzyklen   verzögert
werden, so setzt die Parameterberechnung
bei  REDU1  den  Opcode  für  einen BNE-
Befehl ein.  Da  diesmal  die  Bedingung
zutrifft,  dauert  die  Abarbeitung  des
Befehls 3 Taktzyklen, wobei jedoch eben-
falls  mit  REDU2 fortgefahren wird. Da-
durch  dauert  die  Routine  nun   einen
Taktzyklus mehr, womit wir eine ungerade
Verzögerung erzielt haben.              
Der  Branch-Befehl  bei  REDU2  muß  nun
ebenfalls  modifiziert  werden.  Hierbei
bleibt der Opcode jedoch immer derselbe,
also ein BNE-Befehl, der immer wahr ist.
Es wird  lediglich  der  Operand  dieses
Befehls  geändert, so daß nicht mehr auf
das Label "TT" gesprungen wird,  sondern
auf   einen  der  zuvor  stehenden  NOP-
Befehle, wodurch die entsprechende  HSP-
Verzögerung   sichergestellt  wird.  Der
Operand eines Branch-Befehles  kann  nun
ein  Bytewert  zwischen  -127  und  +128
sein, der den Offset angibt, um den  der
Branch-Befehl   den  Programmzähler  des
Prozessors  erhöhen,   bzw.   erniedigen
soll.  Steht  hier also der Wert $05, so
werden die  nächsten  5  Bytes  im  Code
übersprungen.  Da  ein  NOP-Befehl immer
ein Byte lang ist, werden also in  unse-
rem  Fall exakt 5 NOPs übersprungen, wo-
mit noch 15 NOPs ausgeführt werden, die 
15*2=30 Taktzyklen verzögern, und  somit
den  Bildschirm  in der Horzontalen erst
ab dem  dreißigsten  Charakter  beginnen
lassen.  Der  Sprungoffset  für den BNE-
Befehl berechnet sich  danach  also  aus
der   Formel:  'Anzahl NOPs'-XPOS/8/2  =
20-XPOS/16.                             
Mit Hilfe dieser  beiden  Branch-Befehle
hätten  wir  nun  also das entsprechende
Timing für die HSP-Routine korrekt umge-
setzt.  Beachten Sie nur bitte noch fol-
genden   Umstand:   Dadurch,   daß   der
Branch-Befehl bei REDU2 immer ausgeführt
werden  muß,  also  daß  seine Bedingung
immer wahr sein  muß,  müssen  wir  hier
einen  BNE-Befehl verwenden. Gerade aber
WEIL dessen  Bedigung  immer  wahr  ist,
benötigt  er  auch  immer  3 Taktzyklen,
also eine ungerade Anzahl, womit er  den
Ausgleich  des  Branch-Befehls bei REDU1
wieder zunichte machen würde. Damit dies
nicht geschieht,  muß  die  Parameterum-
rechnungsroutine die Opcodes genau umge-
kehrt einsetzen, wie  oben  beschrieben:
also  einen BNE-Befehl, wenn eine gerade
Anzahl Zyklen verzögert werden soll, und
einen BEQ-Befehl, wenn die Anzahl der zu
verzögernden Zyklen  ungerade  ist!  Ich
erläuterte  dies im obigen Fall zunächst
anders, um Sie nicht noch zusätzlich  zu
verwirren!                              
c) PARAMETER FÜR DAS SOFTSCROLLING      
Damit  wären  also alle Parameterberech-
nungen, die  Raster-IRQ-Effekte  betref-
fend,  abgehandelt.  Da  HSP-  und  VSP-
Routine den Bildschirm jedoch immer  nur
in 8-Pixel-Schritten verschieben, müssen
wir  diesen nun noch mit den ganz norma-
len Softscroll-Registern um die fehlende
Anzahl  Einzelpixel  verschieben.   Auch
dies   wird  noch  innerhalb  des  AGSP-
Interrupts durchgeführt,  nämlich  genau
am  Ende desselben. Hier hatten wir zwei
Labels mit den Namen "HORIZO" und  "VER-
TIC" untergebracht, die jeweils auf eine
LDA-STA-Befehlsfolge  zeigten,  und  den
entsprechenden Verschiebeoffset  in  die
VIC-Register  $D011 und $D016 eintrugen.
Hier  nochmal  ein  Codeauszug  aus  dem
AGSP-IRQ:                               
horizo:  lda #$00 ;Versch. vom linken   
         sta $d016; Bildrand            
vertic:  lda #$00 ;Versch. vom oberen   
         sta $d011; Bildrand            
Um  nun  die Werte dieser Verschiebungen
zu ermitteln, müssen wir  lediglich  je-
weils  die untersten 3 Bits aus XPos und
YPos ausmaskieren und in die  Oparanden-
Bytes  der LDA-Opcodes eintragen. Da die
Register $D011  und  $D016  jedoch  auch
noch  andere  Aufgaben erfüllen, als le-
diglich  das  Softscrolling  des   Bild-
schirms  zu setzen, müssen auch zur kor-
rekten Bildschirmdarstellung  notwendige
Bits in diesen Registern mitgesetzt wer-
den. Auch dies wird unsere  Parameterbe-
rechnungsroutine übernehmen.            
2) DIE AGSP-PARAMETER-ROUTINE "CONTROLL"
Kommen  wir nun endlich zu der Unterrou-
tine, die die  benötigten  Parameter  in
umgerechneter  Form  in die AGSP-Routine
einbettet, und letztere zu einer korrek-
ten   Anzeige  des  Bildschirms  bewegt.
Nachdem wir die  Funktionprinzipien  der
Parameterübergabe  nun  ausführlich  be-
sprochen haben,  besteht  die  Controll-
Routine  nur  noch  aus ein paar "Reche-
naufgaben". Sie  wird  von  der  Border-
IRQ-Routine  aufgerufen  und  wertet die
Einträge in XPos sowie YPos  aus.  Diese
Werte werden, wie schon erwähnt, von der
Joystickabfrage   in  der  Hauptschleife
unseres  Programmbeispiels  entsprechend
gesetzt und verwaltet.                  
Dadurch,  daß  Controll während des Bor-
der-IRQs aufgerufen  wird,  stellen  wir
gleichzeitig  auch sicher, daß die AGSP-
Routine zu einem  Zeitpunkt  modifiziert
wird,  zu dem sie nicht ausgeführt wird,
und vermeiden so ihr Fehlverhalten.     
Hier nun also die Controll-Routine,  die
als  erstes die erforderlichen Werte für
die Y-Verschiebung berechnet.  Dies  ist
zunächst  der  Wert  für FLDCNT, gefolgt
von dem entsprechenden Eintrag  für  die
vertikale    Softscrolling-Verschiebung,
die in VERTIC+1 eingetragen werden  muß.
FLDCNT   berechnet   sich   einfach  aus
YPos/8.  Der  Softscrollwert  entspricht
den  untersten  3  Bits  von  YPos, also
(YPos AND $07). Hierbei muß jedoch durch
das HSP-Timing der benötigte Wert  minus
1   ins  Softscrollregister  eingetragen
werden. Das hängt  damit  zusammen,  daß
die  Softscrollveränderung  ja ebenfalls
in  Register  $D011  eingetragen  werden
muß,  was  ja  zugleich Dreh- und Angel-
punkt aller anderen  Raster-IRQ-Routinen
ist.  Die gewünschte Änderung führen wir
mit Hilfe einer speziellen  Befehlsfolge
durch,  um  uns  Überlaufsvergleiche  zu
sparen. Zum Schluß steht in  jedem  Fall
der  richtige  Wert  im Akku, in den wir
dann mittels ORA-Befehl noch die Bits  3
und  4 setzen, die den 25 Zeilen-Schirm,
sowie den Bildschirm selbst einschalten,
was bei $D011 ja ebenfalls noch  berück-
sichtigt werden muß:                    
Controll:                               
    lda <ypos    ;YPOS holen            
    lsr          ; durch 8              
    lsr          ; dividieren           
    lsr                                 
    sta <fldcnt  ;und in FLDCNT ablegen 
    clc          ;C-Bit f. Add. löschen 
    lda <ypos    ;YPos holen            
    and #$07     ;Unterste Bits ausmask.
    eor #$07     ; umkehren             
    adc #$1a     ; Ausgleichswert add.  
    and #$07     ; wieder maskieren     
    ora #$18     ; Bilschirmbits setzen 
    sta vertic+1 ; und in AGSP ablegen  
Der ORA-Befehl am Ende ist übrigens  von
wichtiger Bedeutung. Wie Sie ja  wissen,
unterscheiden sich die  beiden  Beispiel
"AGSP1" und "AGSP2" nur darin,  daß  die
erste Version einen Textbildschirm,  die
zweite Version einen  Multicolor-Grafik-
Schirm scrollt.  Der  programmtechnische
Unterschied   zwischen   diesen   beiden
Routinen besteht nun  lediglich  in  der
Änderung  des  oben  aufgeführten   ORA-
Befehls. In AGSP2 wird hier mit dem Wert
$78 verknüft, womit zusätzlich auch noch
Hires-  und   Extended-Background-Color-
Mode eingeschaltet werden. Dies ist  der
EINZIGE Unterschied zwischen den  beiden
Beispielen, der  eine  große  Auswirkung
auf das Erscheinen hat! Die AGSP-Routine
selbst ändert  sich  durch  die  Grafik-
darstellung nicht!                      
(Anm. d. Red.: Bitte wählen Sie nun den 
 2. Teil der IRQ-Routine aus dem Menu)  
            IRQ-KURS (2.Teil)           
----------------------------------------
Als nächstes behandelt die  Routine  den
XPos-Wert. Hierzu legt sie sich zunächst
eine Kopie von Low- und Highbyte in  den
Labels AFA und AFB an, die den Zeropage-
adressen $07 und  $08  zugewiesen  sind.
Anhand des 3. Bits aus dem Low-Byte  von
XPos  kann  ermittelt  werden,  ob  eine
gerade (Bit gelöscht) oder ungerade (Bit
gesetzt)  Anzahl  Taktzyklen   bei   HSP
verzögert werden muß. In letzterem  Fall
muß  in  REDU1  der  Opcode  für   einen
BEQ-Befehl eingesetzt werden. Im anderen
Fall steht dort ein BNE-Opcode, der ganz
am  Anfang   des   folgenden   Sourcode-
Segmentes dort eingetragen wird:        
    ldx #$d0     ;Opcode für "BNE" in   
    stx redu1    ; REDU1 eintragen      
    lda <xpos+1  ;Hi-Byte XPos holen    
    sta <afb     ; und nach AFB kop.    
    lda <xpos    ;Lo-Byte XPos holen    
    sta <afa     ; nach AFA kop.        
    and #$08     ;3.Bit ausmask.        
    bne co1      ;<>0 -> alles ok,weiter
    ldx #$f0     ;Sonst Opcode für "BEQ"
    stx redu1    ; in REDU1 eintr.      
Nachdem der einzelne  Taktzyklus  korri-
giert wurde, müssen  wir  nun  noch  den
Sprungoffset  für  den  BNE-Befehl   bei
REDU1 ermitteln. Hierzu wird  die  Kopie
des 16-  Bit-Wertes  von  XPos  zunächst
durch   16   dividiert,    das    daraus
resultierende  8-Bit  Ergebnis  muß  nun
noch von dem Wert  20  (der  Anzahl  der
NOPs  in  der  HSP-Routine)  subtrahiert
werden, bevor  es  in  REDU2+1  abgelegt
werden kann:                            
co1:lsr <afb     ;Wert in AFA/AFB 4 Mal 
    ror <afa     ; nach rechts rotieren 
    lsr <afb     ; (=Division durch 16) 
    ror <afa                            
    lsr <afb                            
    ror <afa                            
    lsr <afb                            
    ror <afa                            
    sec          ;C-Bit f. Sub. setzen  
    lda #$14     ;Akku=20 NOPS          
    sbc <afa     ;Ergebnis subtr.       
    sta redu2+1  ;u. abl.               
Folgt nun nur noch  die  Berechnung  des
horizontalen  Softscrollwertes.   Dieser
entspricht  den  untersten  3  Bits  von
XPos. Da  wir  auch  hier  ein  Register
($D016)  beschreiben,  das   auch   noch
andere Aufga- ben erfüllt, müssen wir in
Ihm ebenfalls noch ein Bit  seten,  näm-
lich das dritte,  daß  die  40  Zeichen/
Zeile-Darstellung einschaltet. In  AGSP2
setzen wir hier zusätzlich auch noch das
vierte  Bit,  daß  den  Multicolor-Modus
einschaltet:                            
   lda <xpos    ;XPos holen             
   and #$07     ;unterste 3Bits ausmask.
   ora #$08     ;40 Zeichen/Zeile       
   sta horizo+1 ;und ablegen            
   rts          ;ENDE                   
Das ist nun alles wichtige gewesen,  was
noch zu AGSP  zu  sagen  war.  Wir  sind
damit auch  wirklich  beim  allerletzten
Teil unseres Kurses über die Raster-IRQ-
Programmierung angelangt. Ich  hoffe  es
hat Ihnen die  letzten  17  Monate  viel
Spaß bereitet, in  die  tiefsten  Tiefen
unseres    "Brotkasten"    vorzudringen,
"where (almost) no man has gone before"!
Einen besonderen Dank  möchte  ich  auch
meinem Kollegen Ivo Herzeg (ih) ausspre-
chen, ohne dessen unermüdlichen Bemühun-
gen um extaktes Rastertiming dieser Kurs
bestimmt nicht zustande gekommen wäre.  
                                  (ih/ub



Valid HTML 4.0 Transitional Valid CSS!