16-FARBEN-SCROLLING           
                 Teil II                
 Im  ersten  Teil  dieses  Kurses  wurde  viel
 Theoretisches  gesagt. Um  nun  langsam ( !) zur  Praxis  zu  kommen, wollen  wird  diesmal  anhand  eines  Source-Codes, welchen
 ich  ausführlich  dokumentieren  und  erklä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, obwohl  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 richtiggesetzt ( muß  jeden  Rasterdurchlauf  reinitialisiert  werden!) und  auf  die
 entsprechende  Raster-Y- Position  gewartet ( 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. geschrieben  wird, erniedrigen  wir  das
 Register $ d02 f, 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 $ d02 f! 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 #$5 f. 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,$ d02 f
 reinitialisiert ( 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  durchfü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  nachdem, 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)