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)