Magic Disk 64

home to index to html: MD9402-KURSE-16_FARBEN_SCROLL.html
          16-FARBEN-SCROLLING           
                 Teil II                
Im ersten Teil dieses Kurses wurde  viel
Theoretisches gesagt. Um nun langsam (!)
zur Praxis zu kommen, wollen wird  dies-
mal anhand eines  Source-Codes,  welchen
ich ausführlich  dokumentieren  und  er-
klären werde, einen Schritt in  Richtung
"Linecrunching",   so   wie's   wirklich
funktioniert, machen.                   
Der    "Linecrunching-Sourcecode"    als
sequentielles  File,  wurde  nur   wenig
dokumentiert. Doch sollte er für  jeden,
der folgendes Kapitel "durchackert" hat,
leicht verständlich sein.               
Um kurz zu wiederholen:                 
Horizontale  Bildverschiebung  erreichen
wir, indem wir  den  VIC  dazu  bringen,
eine neue Cursor-Zeile  aufzubauen,  ob-
wohl die alte noch  nicht  abgeschlossen
wurde und der  darstellende  Elektronen-
Strahl  gerade  am  sichtbaren   Bereich
unterwegs ist. So läßt sich durch Timing
eine X-Versetzung auf  Cursor-Positionen
genau erreichen.                        
Vertikal dupliziert sich die Trickserei!
Und zwar simuliert man  für  den  oberen
Screen-Bereich  beliebig  viel   Cursor-
Zeilen, dir nur  eine  Rasterzeile  hoch
sich (Verhältnis  1:8).  So  werden  die
Zeilen "geschrumpft" und der  Bildscirm-
Inhalt  nach  oben  gezogen.  Was   oben
fehlt kommt (so  wie  beim  Horizontalen
Versetzen rechts) unten wieder rein.    
Gut, nun wird's ernst. Sehn' wir uns die
Sache mit dem $d011-Register genauer an:
      lda #$1b  ;Register               
      sta $d011 ;reinitialisieren       
      lda #$2c                          
fl1   cmp $d012 ;auf Rasterzeile #$2c   
      bne fl1                           
      ldx #4                            
fl2   dex       ;extaktes Timing        
      bne fl2                           
Bis  jetzt  wurde  nur  $d011   richtig-
gesetzt (muß jeden  Rasterdurchlauf  re-
initialisiert  werden!)  und   auf   die
entsprechende   Raster-Y-Position    ge-
wartet   (hängt   ja   bekanntlich   vom
3-Bit-Wert  im  $d011-Register  ab.  Die
Schleife  danach  bringt  nur  genaueres
Timing, damit der Rasterstrahl  in  etwa
dort ist, wo wir  ihn  brauchen  (dieses
Timing  ist  noch   nicht   auf   Zyklen
ganau; die Schwankungen liegen  von  1-4
Taktzyklen).                            
fl4   lda dtab,x ;$d011-Wert-Tabelle    
      dec $d02f  ;$d02f(?) erniedrigen  
      sta $d011  ;Wert schreiben        
      inc $d02f  ;(?) wieder erhöhen    
      nop        ;Timing-nops!          
      .                                 
      . u.s.w.                          
      .                                 
Zuerst wird der entsprechende $d011-Wert
aus  einer  angelegten  Tabelle   (kommt
noch!) ausgelesen. Danach  folgt  wieder
was  typisch  "C64-Mäßiges":  Bevor  nun
der ausgelesene Wert ins $d011-Reg.  ge-
schrieben  wird,  erniedrigen  wir   das
Register $d02f, um es danach  wieder  zu
erhöhen. Rein sinnlos, oder ?  Doch  wer
die  beiden  "Sinnlosigkeiten"  aus  dem
Code entfernt, wird sich  wundern:  Kein
Linecrunching ohne $d02f!  Warum  ?  Wer
den C64 so lange und gut kennt wie  ich,
fragt so was nicht. Er wundert sich  gar
nicht mal.                              
Danach kommt wieder  Rasterzeitfüllendes
Timen. Erwähnt sei, daß  ein  NOP-Befehl
genau 2 Taktzyklen benötigt,  wohingegen
ein BIT $XX-Befehl 3  braucht.  So  läßt
sich auf  Zyklen  genau  verzögern.  Ein
entsprechendes   Beispiel   finden   wir
später  beim  X-Scrollen,  da  wir  dort
den Rasterstrahl ja an  jeder  möglichen
X-Position austricksen werden.          
      .                                 
      .       ...und weiter:            
      .                                 
      inx           ;Pointer erhöhen    
up    cpx #2        ;fertig ?           
      bne fl4                           
fll4  lda dtab+1,x  ;aus Tabelle+#1     
      dec $d02f     ;wie gehabt         
      sta $d011                         
      inc $d02f                         
      nop                               
      .                                 
      .                                 
      .                                 
Ab  "fll4"  passiert  anscheinend  ganau
dasselbe wie zuvor, doch: Wir lesen  den
$d011-Wert aus der  Tabelle+1.  Warum  ?
Folgende  Rasterzeilen  wird   sozusagen
nur Zeit verbraucht,  um  die  Lücke  zu
füllen. Die Lückenspanne ist linear  zur
Y-Versetzung.  Wenn  viele  Zeilen  "ge-
staucht" werden, ist die Spanne klein  -
und  umgekehrt.  Und  dadurch,  daß  wir
aus der Tabelle+1  lesen,  passiert  gar
nichts. Allerdings müssen wir  in  $d011
etwas schreiben, da wir  sonst  mit  dem
Soft-Scrolling   in   Y-Richtung   nicht
zurechtkommen.                          
      .                                 
      .       ...und weiter:            
      .                                 
      inx       ;Pointer erhöhen        
      cpx #28   ;Zeilen-Limit ?         
      bne fll4  ;zurück!                
      ldx #1                            
fl5   dex       ;wieder timen...        
      bne fl5                           
      lda #$59  ;Neuer Fix-Wert für     
      sta $d011 ;$d011                  
      ldx #$4f  ;x-Reg.für Raster-Check 
      lda #$5f  ;$d011-Wert in Akku     
fl6   cpx $d012 ;Rasterzeile schon      
      bne fl6   ;erreicht ?             
      ldx #3                            
fl7   dex       ;und wieder timen...    
      bne fl7                           
      sta $d011 ;jetzt in $d011!        
Linecrunching  ist  abgeschlossen  (max.
28 Rasters!) und zwischen den  gewohnten
"Austimereien" wurde  der  Fixwert  #$59
in $d011  geschrieben  und  anschließend
nochmal #$5f. Das war  die  Vorbereitung
für das X-Scrolling,  dem  jetzt  nichts
mehr im Wege steht...                   
      lda #208  ;Border eng             
      ora xsoft ;mit Xsoft verknüpft    
      sta 53270 ;ins X-Scroll-Register  
      lda #$0f                          
      sta $d02f ;$d02f zurücksetzen     
      ldx #3                            
jumpi dex       ;zum Xten mal Timen     
      bne jumpi                         
Alles ist nun vorbereitet:  "Softscroll-
3-Bit-Byte"   (0-7)   verknüpft,   $d02f
re-initialisiert (für nächsten Durchlauf
notwendig!)  und   wieder   Verzögerung.
Warum zwei mal ins $d011-Reg.geschrieben
wird, ist auch ganz einfach: Durch diese
besondere  Zusammensetzung  der   beiden
Werte und  auch  dem  Zeitraum  zwischen
den  beiden,  erreichen  wir,  daß   der
Prozessor nun den  nächsten  Befehl  auf
einer  fixen  Raster-X-Position   durch-
führt. D.h. das relativ   ungenaue  (auf
1-4  Zyklen  genaue)  Timing  ist  jetzt
auf 1 Taktzyklus genau.  Und  genau  das
ist absolut notwendig für  den  X-Trick,
für das sog. Hard-Scrolling...          
      lda #$59    ;Wert für $d011       
xhard bne jumpl+0 ;variabler jump       
jumpl cmp #$c9    ;22 x "cmp #$c9"      
      cmp #$c9                          
      .                                 
      .                                 
      cmp #$c9                          
      bit $ea     ;versteckter "NOP"    
      sta $d011                         
Im Grunde genommen sind diese 22  platz-
füllend  wirkenden  "cmp  #$c9"   wieder
Timer-Elemente. Und zwar ist  der  Jump-
BNE-Befehl  ausschlaggebend:  Gesprungen
wird immer (da #$59 ja unequal 0  ist!),
und zwar  soweit,  wie  wir  in  xhard+1
schreiben. Logische  Werte  wären  0-39,
da  wir  eine  Cursor-Auflösung  von  40
Zeichen haben.                          
Wir wissen, daß wir eine Verzögerung von
0-39 Taktzyklen brauchen, um den  Screen
an alle möglichen Position versetzen  zu
können. Genau  das  erledigen  die  "CMP
#$c9" Befehle. Wenn wir uns  den  Opcode
dieses Befehls in einem Monitor ansehen,
merken wir, daß der Opcode  des  Befehls
"CMP" den Wert #$c9 besitzt. Das  heißt,
wir können den BNE-Jump irgendwo in  die
Liste steuern, und  der  Prozessor  wird
immer  "CMP  #$c9"  entdecken,  egal  ob
der Wert nach dem "BNE" (xhard+1) gerade
oder ungerade ist.                      
Schließlich wird  die  "CMP  #$c9"-Liste
noch mit einem "BIT #$ea" abgeschlossen.
Erraten! #$ea ist  der  Opcode-Wert  für
"NOP". Nun liest der Prozessor je  nach-
dem, ob es  eine  gerade  oder  ungerade
Sprungadresse  ist,  folgende   Befehls-
Serien...                               
  gerade:      ungerade:                
   ...          ...                     
   cmp #$c9     cmp #$c9                
   cmp #$c9     cmp #$c9                
   cmp #$c9     cmp #$24 ;#$24 = Opcode 
   bit #$ea     nop              für BIT
Diese  Lösung  scheint  auf  den  ersten
Blick  vielleicht  ein  wenig  aufwendig
und  kompliziert,  doch  wenn  man   das
Prinzip und die Problemstellung  richtig
verstanden hat, so  entdeckt  man  einen
kleinen Touch von Genialität dahinter.  
Gut, auch diesmal war wieder  'ne  Menge
"Stuff" dabei,  der  Köpfe  zum  Rauchen
bringen  kann.  Doch   Assembler-Tüftler
werden  mit   Hilfe   des   Source-Files
bald den Durchblick haben.              
Der HAMMER und  die  KRONE  im  Teil  3:
Scroll-Action pur  für  eigene  Projekte
und  ein   super   Editor   für   bunte,
riesengroße Scrollfields...             
                                 (hs/wk)



Valid HTML 4.0 Transitional Valid CSS!