Erstellt: 24. November 2013 (zuletzt geändert: 8. März 2020)

Raster-IRQ: Sprites & Timing

Die Auswirkung von Sprites auf das Timing

C64 Studio, AMCE & TASM

Ich denke, dies ist nun der letzte Beitrag zum Raster-IRQ und dem Timing. Hiernach sind alle Grundlagen einmal angesprochen worden. Bei den Demo-Effekten werde ich natürlich noch Spielereien vorstellen und es wird Themen (z. B. den FLIinfoFlexible Line InterpretationIm 'Flexible Line Interpretation'-Modus können, im Gegensatz zum normalen Multicolor-Modus, sogar alle 16 Farben in jeder 4*8 Pixel großen 'Zelle' verwendet werden.-Modus) geben, die wieder eng mit den Rasterzeilen & IRQs verbunden sind. Aber alles Weitere setzt einfach nur auf die bisherigen Informationen auf.

…und wieder: Basis ist das standard PAL-System mit 63TZ!

Das Problem mit den Sprites

Wer den Beitrag L.O.V.E. – Level 2: Die Landung 004 gelesen hat, der ist bereits darüber gestolpert, dass Sprites das Timing beeinflussen. Um dies jetzt genauer zu untersuchen, erzeugen wir wieder einen stabilen Rasterzeileninterrupt, zeichnen eine durchgängige Linie in den Rahmen und fügen dann noch einige Sprites hinzu.

Das obige Programm stellt euch wohl vor keine größeren Probleme mehr, es erzeugt aktuell die von uns erwartete Ausgabe. Wir haben den oberen & unteren Rahmen geöffnet, sehen unsere weiße Linie und acht Testsprites. Wir werden das Programm gleich über die Konstanten SPRITESACTIVE und SPRITESYPOS steuern und kontrollieren, was genau passiert.

Ausgangspunkt für unseren Test (Rahmen-Debuggen ist aktiv!)
Ausgangspunkt für unseren Test (Rahmen-Debuggen ist aktiv!)

Wie gesagt, soweit, sogut.

Wir wollen nun nur Sprite-0 sehen und zwar eine Zeile höher. Ändert dazu die Konstanten:

Das Sprite eine Zeile höher positioniert.
Das Sprite eine Zeile höher positioniert.

Kaum verschieben wir das Sprite-0 um eine Zeile nach oben, ragt unsere Linie um 5 Taktzyklen in die nächste Zeile. Wir verbrauchen also 5 TZ mehr, sobald wir das Sprite aktivieren. Aber warum wirkt es sich auf die darüber befindliche Rasterzeile aus? Das Sprite wurde doch direkt unter unserer Linie positioniert?
Ändert erstmal die Spriteanzeige in SPRITESACTIVE = %00010000, um Sprite-4 (also das fünfte – die Zählung startet bei 0!) zu aktivieren und startet das Programm nochmal.

Sprite-4 zeigt KEINE Auswirkung
Sprite-4 zeigt KEINE Auswirkung

Komisch, oder? Sprite-4 hat keine Auswirkung auf unsere Linie, aktiviert jetzt zusätzlich noch Sprite-0 SPRITESACTIVE = %00010001 und schaut euch das folgende Ergebnis an:

ABER: Sprite-4 & 0 = doppeltes Unheil
ABER: Sprite-4 & 0 = doppeltes Unheil

Jetzt ragt unsere Linie sogar doppelt so weit, um ganze 10 Taktzyklen, in die nächste Zeile!

Was geht denn da ab?

Der VIC-II muss natürlich nicht nur die Daten für den Ausgabebereich (das führt bekanntlich zu den Bad Lines), sondern auch die Spritedaten irgendwann mal holen. Dies macht er für die Sprites 0 bis 2 am Ende einer Rasterzeile und für die restlichen zu deren Beginn. Daher hat das einzelne Sprite-0 unsere Linie im ersten Bild um 5 TZ verschoben, Sprite-4 allein zeigt keine Auswirkung, da sich in der Zeile keine sichtbaren Daten befanden. Dass Sprite-4 aber auch seine Zeit fordert, seht ihr im letzten Bild, sobald unsere Linie durch Sprite-0 in die nächste Zeile ragt, wird die Linie dank Sprite-4 nochmal auf insg. 10 TZ verlängert.

Für das Holen der Spritedaten gilt etwas Ähnliches wie für die Bad Lines (Daten für Ausgabebereich holen). Der VIC-II benötigt auch für die Sprites zusätzliche „Bus-Zeit“. Dafür hält er wieder den Prozessor an. Ihr erinnert euch bestimmt, 3TZ bevor er den Bus benötigt setzt der VIC das Signal BA auf LOW. Dadurch weiß die CPU, dass sie gleich erstmal Pause hat. Der VIC benötigt 2TZ für das Sprite, somit erklären sich unsere 5TZ aus dem erstem Beispiel mit dem schwarzen Sprite (3TZ für die Ankündigung + 2TZ für die eigentliche Arbeit).

Schauen wir uns mit SPRITESACTIVE = %00000011 mal an, was zwei benachbarte Sprites für eine Auswirkung haben.

Es kommen nur 2TZ hinzu
Es kommen nur 2TZ hinzu

Sprite-0 & 1 benötigen zusammen nur 7TZ. Das liegt daran, dass der VIC-II ja bereits den Bus übernommen hat und daher die 3TZ für die Übernahme entfallen, es kommen nur noch 2TZ für die zusätzliche Arbeit hinzu. Dies könnt ihr durch aktivieren weiterer Sprites z. B. mit SPRITESACTIVE = %00001111 überprüfen…

Test mit 4 Sprites
Test mit 4 Sprites

Sind die Sprites 0 bis 3 aktiv, dann werden insg. 11TZ benötigt (3TZ Übernahme + 4 * 2TZ für die Arbeit). Das passt ja zu unseren Beobachtungen von eben.

Um euch nun komplett zu verwirren, versucht mal SPRITESACTIVE = %00001011.

Ein Sprite weniger, aber immer noch 11TZ!??!
Ein Sprite weniger, aber immer noch 11TZ!??!

Wir verzichten auf Sprite-1, aber gewinnen dadurch keine TZ, es werden immer noch 11TZ benötigt!!! Die Reihenfolge der Sprites hat also auch noch eine Auswirkung auf die verbrauchte Zeit.
Vergleichen wir obgies Bild mal mit der Ausgabe nach SPRITESACTIVE = %00000111:

Erst jetzt haben wir 2TZ „zurückbekommen“
Erst jetzt haben wir 2TZ „zurückbekommen“

Auch hier sind drei Sprites aktiv, aber dieses Mal verlieren wir nur 9TZ.

Ihr solltet also auch darauf achten, welche Sprites, wann aktiv sind, wenn eure Rasterzeit knapp wird. Natürlich werden auch in Bad Lines die zusätzlichen Taktzyklen für die Sprites benötigt, dadurch habt ihr dort noch weniger Zeit.

Ein Blick aufs Timing

Nun folgen noch einige Tabellen, in denen ihr einen genaueren Blick aufs Timing werfen könnt. Diese Tabellen habe ich nicht selbst entwickelt, ich habe sie nur etwas vereinfacht.

 

Wie ihr seht werkeln die CPU und der VIC-II in einer „normalen“ Rasterzeile jeder für sich und kommen sich somit auch nicht ins Gehege. Der VIC muss nie in Phase-2 tätig werden, daher stehen uns hier die vollen 63TZ zur Verfügung.

 

Aktivieren wir jetzt (wie oben im Beispiel) nur das Sprite-0, dann ändert sich die Sachlage. Der VIC kündigt in Zyklus 55 die Bus-Übernahme an, d. h. die CPU kann in Taktzyklus 55 bis 57 nur noch Schreibzugriffe ausführen, beim ersten Lesen wird der Prozessor gestoppt. In Zyklus 58 liest der VIC zunächst wie gewohnt den Sprite-Pointer (hier gehen wir ja von Sprite-0 aus). Um die drei BYTEs mit den Spritedaten für diese Zeile zu laden, benötigt er aber auch drei weitere Bus-Zugiffe. Daher hat er die CPU gestoppt und kann nun in Zyklus 58 auch noch ein Byte holen, da ihm jetzt beide Phasen zur Verfügung stehen. Die beiden letzten Bytes holt er sich in Phase-1 und 2 vom Takt 59.

 

Hier das vertraute Timing in einer Bad Line. Der VIC kündigt in Zyklus 12 an, dass er den Bus haben muss. Damit kann die CPU in Zyklus 12-14 nur noch schreibend auf den Bus zugreifen. Dann benutzt der VIC die zusätzliche Bus-Zeit, um die 40 Zeichen für die Ausgabe zu holen. Anschließend steht der Bus wieder der CPU zur Verfügung. Zählt ihr die obigen „x“e, dann kommt ihr wieder auf die vertrauten 23TZ.

 

Na dass sieht ja mal übel aus. In einer Bad-Line mit allen acht Sprites können wir evtl. noch einen Befehl absetzen, dann ist unsere Zeit abgelaufen.

Wenn ihr jetzt Sehnsucht nach noch mehr Tabellen und technischen Details habt, empfehle ich euch den deutschsprachigen Text ‚Der MOS 6567/6569 Videocontroller (VIC-II) und seine Anwendung im Commodore 64‚ von Christian Bauer. Dieser Text wird in verschiedenen Foren als die Referenz zum VIC-II angepriesen und ich kann mich dieser Meinung nur anschließen. Daher möchte ich (eigentlich muss ich) jedem, der den VIC-II wirklich verstehen will, nahelegen sich diesen Text durchzulesen und beim Programmieren von Rastereffekten griffbereit zu haben. Ein weiterer Hinweis auf den referenz Status ist, dass ihr den Text auch bei den WinVice-Docs findet.

Zusammenfassung

Wir müssen also beachten, dass aktive Sprites uns auch Taktzyklen in der Rasterzeile stibitzen und zwar in jeder Zeile, in der das jeweilige Sprite aktiv ist. Wie ihr an den Diagrammen erkennen könnt, werden die Sprites 0-2 am Ende der Rasterzeile und die Spites 3-7 am Beginn der Zeile verarbeitet.
Solltet ihr bei obigen Tests darüber gestolbert sein, dass ein einzelnes Sprite-3 auch unsere Linie beeinflußt, dass liegt natürlich an den 3TZ, in denen der VIC die Bus-Übernahme ankündigt. Die Daten für Sprite-3 werden dann direkt mit dem Beginn der Rasterzeile gelesen.
Da zwischen zwei Sprites nur 2TZ liegen gewinnt man keine Zeit, wenn Sprite 1, 3 oder 5 deaktiviert, das Sprite davor und danach aber aktiviert ist. Die 2TZ reichen einfach nicht, um den Bus freizugeben und wieder zu belegen. Das könnt ihr mit unserem Beispiel auch direkt kontrollieren.

RasterIRQ_021
Kein Zeitgewinn, trotz fehlender Sprites!

Ihr solltet also, sofern es möglich ist, in euren Programmen die Sprites immer aufsteigend verwenden, falls ihr Raster-Effekte einplant. Die max. benötigte Rasterzeit beträgt also 16-19TZ (3TZ „Übernahme“ + 8 * 2TZ „Arbeit“). Dies ist der Fall, wenn alle Sprites oder auch nur die Sprites 0, 2, 4, 6, 7 aktiviert sind.

Timing über Sprite-0

Abschließend möchte ich noch eine Möglichkeit aufzeigen, mit der man das Timing mit Hilfe von Sprite-0 erzielen kann. Dieses Vorgehen hat Pasi „Albert“ Ojala in C= Hacking Volume 1 Issue 3 beschrieben. Das Prinzip dahinter ist eigentlich ganz einfach. Durch aktivieren von Sprite-0 in einer normalen Rasterzeile, muss der VIC den Bus übernehmen. Dies kündigt er in TZ 55 an. Durch das Auslösen der richtigen Befehle zu dieser Zeit kann das Timing syncronisiert werden. Aber schauen wir uns dazu mal ein kleines Beispiel an.

Wir richten hier, wie gewohnt, unseren Raster-IRQ ein. Aber dieses Mal platzieren wir Sprite-0 so, dass die letzte Spritezeile genau mit der Zeile unseres Rasterzeileninterrupts übereinstimmt (s. lda #RASTER-$14 ;Sprite-0 positionieren). Dabei ist einzig die Y-Position wichtig! Außerdem sollten wir darauf achten, dass das Sprite nicht in der Y-Richtung verdoppelt wurde.
Bei myIRQ warten wir zunächst etwas. Die Zeit nutzen wir, um die Farbe für die Linie ins X-Register und die aktuelle Rahmenfarbe in den Akku zu holen.
Dann kommen die beiden wichtigen INC-Befehle:
Diese werden zum Zeitpunkt der Bus-Übernahme durch den VIC-II ausgeführt. Durch die interne Funktionsweise (ein DEC würde übrigens auch klappen) sorgen sie für das exakte Timing.
Anschließend schreiben wir den Inhalt des X-Registers in die Rahmenfarbe, warten die entsprechende Anzahl an Taktzyklen ab und setzten den Rahmen wieder auf die ursprüngliche Farbe zurück. Zum Schluß wird der IRQ bestätigt und zur System-Routine gesprungen.

Startet ihr das Programm nun, bekommt ihr wieder mal eine stabile Linie zu Gesicht:

Timing über Sprite-0
Timing über Sprite-0

Verzichtet ihr jetzt aber auf das Sprite-0, in dem ihr z. B. die Zeile stx $D015 ;Sprite-0 aktivieren auskommentiert und startet dann das Programm erneut. So fängt die Linie wieder zu Zittern an.
Diese Art des Timings kann unseren stabilen Raster-IRQ aber nicht wirklich ersetzen. Laßt ihr z. B. ein BASIC-Programm laufen, kommt wieder alles aus dem Tritt. Versucht ihr das mit dem Demo-Programm vom Anfang, dann seht ihr, dass dort alles stabil bleibt.


Damit sind wir am Ende unserer Reise durch die Geheimnisse des Raster-IRQs und VIC-Timings angelangt. Ich hoffe ihr habt euch nicht zu sehr gelangweilt und konntet den einen oder anderen Nutzen aus den Texten ziehen.
Weitere Beiträge, was man damit nun so alles anstellen kann, werden, wie eingangs bereits erwähnt, natürlich noch folgen.


Schrott!!Naja...Geht so...Ganz gut...SUPER! (15 Bewertungen | Ø 4,40 von 5 | 88,00%)

Loading...


Zurück

2 Gedanken zu „Raster-IRQ: Sprites & Timing“

  1. Kurze Info; beim Einbauen von Sprite-Timing in meinen Emulator ist mir aufgefallen, dass in dieser Zeile eine Null fehlt: “Ändert erstmal die Spriteanzeige in SPRITESACTIVE = %0001000, um Sprite-4 (also das fünfte – die Zählung startet bei 0!)”

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Protected by WP Anti Spam