Magic Disk 64

home to index to html: MD9405-KURSE-IRQ-KURS_7.html
             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)



Valid HTML 4.0 Transitional Valid CSS!