Sprite-Stretching
Dieser Artikel befindet sich im Aufbau und ist vorläufig als Entwurf einzustufen. |
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.
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.)
* = $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.
* = $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
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.