Sprite-Stretching

Aus C64-Wiki
Zur Navigation springenZur Suche springen
Baustelle Dieser Artikel befindet sich im Aufbau und ist vorläufig als Entwurf einzustufen.


Sprite-Stretching: Von links nach rechts: Normales Sprite, klassisch Y-expandiertes Sprite mit doppelter Höhe, gestretchtes Sprite mit dreifacher Höhe, gestretchtes Sprite mit dreifacher Höhe, bei dem aber die einzelnen Zeilen unterschiedlich stark gestretcht wurden, gestretchtes Sprite mit dreifacher Höhe, bei dem zusätzlich in jeder Zeile der Spritedatenzeiger geändert wurde.

Unter Sprite-Stretching wird eine Technik verstanden, mit der die Sprites in vertikaler Richtung beliebig in die Länge gezogen werden können. Mit einer Variante dieser Technik ist es auch möglich, Sprites um bis zu vier Zeilen zu schrumpfen. Dies wird dann als Sprite-Crunching bezeichnet. Gelegentlich wird diese Variante selbst bereits als Sprite-Crunching bezeichnet, auch wenn das Sprite gar nicht 'gegrunched' sondern vergrößert wird.


Mit klassischer Sprite-Y-Expansion kann man die Höhe eines Sprites verdoppeln. Dabei wird jede Zeile zweimal angezeigt, die Auflösung des Sprites ändert sich dadurch nicht. Mit Sprite-Stretching kann man für jede Zeile einzeln wählen, wie oft sie angezeigt wird, auch mehr als zweimal. Und man kann hierbei auch die Auflösung maximieren.


Techniken[Bearbeiten | Quelltext bearbeiten]

Sprite-Stretching basiert auf zeitlich abgepassten Veränderungen von Registers 23 ($D017) des VIC. In diesem Register wird für jedes Sprite gespeichert, ob die Zeilen des Sprites einfache oder doppelte Höhe haben. Die Auflösung des Sprites ändert sich dabei allerdings nicht.

Damit eine Spritezeile die doppelte Höhe hat, muss von Taktzyklus 56 der entsprechenden Rasterzeile bis Taktzyklus 16 der folgenden Rasterzeile das entsprechende Bit von Register 23 gesetzt sein. Ist auch nur in einem einzigen dieser Taktzyklen das Bit gelöscht, hat die Zeile einfache Höhe, wobei Taktzyklus 16 hier eine Sonderrolle spielt. Die X-Koordinate des Sprites spielt dabei keine Rolle.

Hierbei muss man beachten, dass die obere der beiden Rasterzeilen (für die erste Sprite-Zeile) genau der Y-Koordinate des Sprites entspricht. Die Zeile wird aber erst in der unteren der beiden dargestellt. So ist zum Beispiel die oberste Zeile des Bildschirms in der Standardeinstellung Rasterzeile 51, die Y-Koordinate eines Sprites, das gerade eben noch am oberen Rand vollständig dargestellt wird, lautet aber 50.

Timing der Y-Expansion eines Sprites: Oben: Wenn in mindestens einem der Taktzyklen 56 bis 15 das Expansionsbit gelöscht ist, wird die Spritezeile in einfacher Höhe dargestellt. Unten: Wenn in allen Taktzyklen von 56 bis 15 das Expansionsbit gesetzt ist, wird die Spritezeile in doppelter Höhe dargestellt. In beiden Fällen darf in Taktzyklus 16 keine Änderung des Expansionsbits stattfinden.

Für das Sprite-Stretching gibt es zwei Möglichkeiten: Zum einen kann man in den Taktzyklen nach Taktzyklus 16 ein gesetztes Bit kurzfristig löschen und gleich darauf wieder setzen. Dadurch wird der VIC ausgetrickst und vergisst, dass er bereits eine Spritezeile gezeichnet hat. Das hat zur Folge, dass die Zeile noch zwei weitere Male gezeichnet wird, also insgesamt dreimal. Wiederholt man diesen Trick, kann man damit jede Spritezeile beliebig oft wiederholen. Das ist das klassische Sprite-Stretching.

Die zweite Technik ist komplizierter und hängt mit der Sonderrolle von Taktzyklus 16 zusammen. (Man beachte, dass die hierfür benötigten Veränderungen exakt in Taktzyklus 15 erfolgen müssen, damit sie in Taktzyklus 16 aktiv sind.) Hierbei wird der Index in die Spritedaten nicht, wie normalerweise, um drei Schritte erhöht, sondern um bis zu acht. Und es ist sogar möglich, diesen damit zu erniedrigen. Hierbei kann es passieren, dass das Ende eines Sprites vom VIC nicht mehr korrekt erkannt wird und deswegen das Sprite ein zweites Mal (oder öfter) angezeigt wird.

Mit der zweiten Technik ist es auch möglich, Sprites zu kürzen.

Zudem kann man die klassische Y-Expansion auch nur auf einige Zeilen anzuwenden. Auch dies ist eine Art Sprite-Stretching, weshalb diese Technik hier mit aufgenommen wurde. Diese Technik muss auch immer dann angewandt werden, wenn beim klassischen Sprite-Stretching einzelne Zeilen nicht gestretcht werden sollen.

Zeilenweise Y-Expansion[Bearbeiten | Quelltext bearbeiten]

Mit klassischer Y-Expansion eines Sprites werden alle Zeilen doppelt so hoch. Man kann aber auch für jede Zeile einzeln bestimmen, wie hoch sie sein soll. Im nachfolgenden Programm werden nur die zweite und die vierte Zeile des Sprites expandiert.

Hierfür wird nur ein grobes Raster-Timing benötigt, da die Veränderung von Register 23 irgendwann in den Taktzyklen 16 bis 55 erfolgen muss. Probleme bereiten hier allerdings Badlines: In Badlines benötigt man entweder exaktes Raster-Timing (und darf dann Sprite 0 nicht verwenden), oder aber man muss sich damit abfinden, dass man in Badlines die Höhe einer Zeile nicht mehr frei steuern kann. (Das nachfolgende Programm macht letzteres.)

Ausgabe des nebenstehenden Programms mit Vergrößerung: Beim Sprite wurden nur die zweite und die vierte Zeile vergrößert.
* = $c000
         lda #100       ; Sprite initialisieren
         sta $d000
         sta $d001

         lda #11
         sta 2040

         ldy #$00
-        lda pilz,y
         sta 704,y
         iny
         cpy #63
         bne -

         lda #$01
         sta $d015

         sei            ; Interrupt abschalten, der stört nur

loop     lda #100       ; grobe Synchronisation mit Zeile 100
-        cmp $d012
         bne -

         nop            ; Acht Takte warten, damit wir sicher jenseits
         nop            ; des problematischen Takt 16 sind.
         nop            ; Der nachfolgende STA-Befehl
         nop            ; schreibt in einem der Taktzyklen 18-24.

         lda #0         ; Expansions-Bit löschen, damit 1. Zeile
         sta $d017      ; Höhe 1 hat.

         ldx #9         ; 52 Taktzyklen warten (=1 Zeile)
-        dex
         bne -
         nop
         nop
         nop

         lda #1         ; Expansions-Bit setzen, damit 2. Zeile
         sta $d017      ; Höhe 2 hat.

         ldx #21        ; 110 Taktzyklen warten (=2 Zeilen)
-        dex
         bne -
         nop
         nop

         lda #0         ; Expansions-Bit löschen, damit 3. Zeile
         sta $d017      ; Höhe 1 hat.

         ldx #9         ; 52 Taktzyklen warten (=1 Zeile)
-        dex
         bne -
         nop
         nop
         nop

         lda #1         ; Expansions-Bit setzen, damit 4. Zeile
         sta $d017      ; Höhe 2 hat.

         ldx #9         ; 52 Taktzyklen warten (=1 Zeile)
-        dex            ; Das Expansions-Bit kann auch schon in
         bne -          ; der ersten Zeile einer doppelten Zeile gelöscht
         nop            ; werden, solange es nicht wieder gesetzt wird.
         nop            ; Dies ist hier notwendig, da die nächste Zeile
         nop            ; eine Badline ist.

         lda #0         ; Expansions-Bit löschen, damit ab Zeile
         sta $d017      ; 5 alle Zeilen die Höhe 1 haben.

         jmp loop

pilz !by 0,63,192,3,255,248,14,252,252,63,223,207,115,207,239,127,255,255
     !by 126,62,126,48,126,12,0,126,0,0,62,0,0,126,0,0,191,0,0,62,128,0
     !by 62,0,0,63,0,0,127,0,0,255,0,0,254,0,1,254,0,73,252,68,255,255,255

Sprite-Stretching[Bearbeiten | Quelltext bearbeiten]

In der einfachsten Form des Sprite-Stretching wird jede Zeile gleich oft wiederholt. Hierfür muss am Beginn der Zeile bis Taktzyklus 55 das Expansions-Bit in Register 23 des VIC gesetzt sein. Dadurch wird die Zeile zweimal angezeigt. In den Zeilen danach wird für jede zusätzliche Zeile wie folgt vorgegangen: Nach Taktzyklus 15 wird kurz das Expansions-Bit gelöscht und dann gleich wieder gesetzt. Das hat zur Folge, dass der VIC "glaubt", er habe die Zeile noch nie ausgegeben und muss sie noch zweimal ausgeben.

Probleme bereiten hierbei die Badlines, da man zwischen Taktzyklus 15 und Taktzyklus 56 maximal zwei Taktzyklen zur Verfügung hat (und das auch nur, wenn Sprite 0 nicht benutzt wird). Das ist zu wenig, um das Expansions-Bit zu löschen und wieder zu setzen. In der Regel wird man deswegen die Badlines komplett abschalten.

Das nachfolgende Programm geht einen anderen Weg: Die Position des Sprites und der Vergrößerungsfaktor wurden so gewählt, dass die Badlines immer in eine Zeile fallen, in der keine Aktion notwendig ist, da ohnehin dort die Sprite-Zeile gewechselt wird. Dadurch wird man allerdings recht unflexibel: Das Sprite kann zwar in X-Richtung beliebig bewegt werden, in Y-Richtung aber nur noch in 4er-Schritten.

Ausgabe des nebenstehenden Programms: Das Sprite wurde in Y-Richtung vierfach vergrößert.
* = $c000
         lda #100        ; Sprite initialisieren
         sta $d000
         sta $d001

         lda #11
         sta 2040

         ldy #$00
-        lda pilz,y
         sta 704,y
         iny
         cpy #63
         bne -

         lda #$01
         sta $d015

         sei              ; Interrupt abschalten, der stört nur

         ldy #21          ; 21 Zeilen vergrößern

loop     lda #100         ; Auf Zeile 100 warten
-        cmp $d012
         bne -

         nop              ; Sicherstellen, das Zyklus 16
         nop              ; erreicht wurde
         nop
         nop

         lda #1           ; Expansions-Bit setzen
         sta $d017        ; => doppelte Höhe

         ldx #9           ; 52 Taktzyklen warten
-        dex
         bne -
         nop
         nop
         nop

         lda #0           ; Expansions-Bit löschen und gleich
         sta $d017        ; wieder setzen
         lda #1           ; => dreifache Höhe
         sta $d017

         ldx #9           ; 46 Taktzyklen warten
-        dex
         bne -

         lda #0           ; Expansions-Bit löschen und gleich
         sta $d017        ; wieder setzen
         lda #1           ; => vierfache Höhe
         sta $d017

         lda loop+1       ; Anfang vier Zeilen weitersetzen
         clc
         adc #4
         sta loop+1

         dey              ; Wiederholen, bis alle Zeilen des
         bne loop         ; Sprites abgerbeitet wurden

         lda #100         ; Schleife für neuen Durchlauf initialisieren
         sta loop+1
         jmp loop

pilz !by 0,63,192,3,255,248,14,252,252,63,223,207,115,207,239,127,255,255
     !by 126,62,126,48,126,12,0,126,0,0,62,0,0,126,0,0,191,0,0,62,128,0
     !by 62,0,0,63,0,0,127,0,0,255,0,0,254,0,1,254,0,73,252,68,255,255,255
Die Werte, die die Expansions-Bits in jeder Zeile annehmen müssen, damit bei einem Sprite die Zeilen der Reihe nach 4x, 2x, 1x, 1x und 3x wiederholt werden. Zahlen in Klammern können weggelassen werden.

Um jede Spritezeile für eine beliebige Anzahl an Zeilen zu verlängern, geht man wie folgt vor: In der Rasterzeile vor einer neuen Spritezeile setzt man das Expansions-Bit, wenn die Spritezeile mindestens einmal wiederholt werden soll, sonst setzt man es auf 0. Zudem löscht man das Expansions-Bit in jeder Zeile und setzt es direkt danach wieder, außer in den letzten beiden Rasterzeilen einer Spritezeile (und damit auch nicht in einer Einzelzeile).

Möchte man beispielsweise die Zeilen eines Sprites der Reihe nach auf 4, 2, 1, 1 und 3 Zeilen verlängern, so geht man, wie im nebenstehenden Diagramm beschrieben vor: Vor der ersten Spritezeile setzt man das Expansions-Bit, da jetzt ein mehrzeiliger Block folgt. Dort verlängert man diesen zweimal mit der Aus-An-Kombination. Dann hat man eine Zeile, in der man nichts machen muss.

In der darauffolgenden Zeile setzt man das Expansions-Bit für den kommenden mehrzeiligen Block. Das kann man allerdings in diesem Fall weglassen, da das Bit ohnehin schon gesetzt ist. Da dieser Block nun nur zwei Zeilen hoch sein soll, braucht man hier keine Verlängerung. Erst am Ende setzt man das Expansions-Bit wieder auf 0, damit die Zeile danach eine Höhe von 1 bekommt. Und so weiter.

Wichtig ist dabei, dass das Ändern des Bits immer in den Taktzyklen 16 bis 55 erfolgt. In manchen Fällen ist es zwar möglich, das auch noch nach Taktzyklus 55 zu tun, aber da muss man wissen, was man tut, sonst funktioniert es am Ende nicht. Auf alle Fälle muss aber Taktzyklus 15 umschifft werden, da sonst Sprite-Crunching ausgelöst wird.


Sprite-Crunching[Bearbeiten | Quelltext bearbeiten]

Geschichte[Bearbeiten | Quelltext bearbeiten]

Sprite-Stretching wurde zum ersten Mal von der Demo-Gruppe Triad im Demo Cocktail verwendet. Veröffentlicht wurde das Demo am 26. Februar 1989.

Sprite-Crunching wurde erst 1997 zum ersten Mal eingesetzt. Crossbow von der Gruppe Crest konnte damit einen neuen Rekord an gleichzeitig auf dem Bildschirm vorhandenen Sprites aufstellen: 144 Sprites.

Siehe hierzu auch die Zeittafeln der C64-Demos, C64-Anwendungen und C64-Spiele.

Webseiten[Bearbeiten | Quelltext bearbeiten]