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.

RASTER          = $00               ;Hier beginnt die Linie

SPRITESACTIVE   = %11111111         ;welche Sprites sollen aktiviert werden
SPRITESYPOS     = $12               ;Y-Position für alle Sprites

;*** Startadresse 
*=$0801
;** BASIC-Zeile: 2018 SYS 2064:NEW
 !word main-2, 2018 
 !byte $9e
 !text " 2064:"
 !byte $a2,$00,$00,$00

main
 sei                                ;IRQs sperren

 lda #<myIRQ                        ;Adresse unserer Routine in
 sta $0314                          ;den RAM-Vektor
 lda #>myIRQ
 sta $0315

 lda #%00000001                     ;Raster-IRQs vom VIC-II aktivieren
 sta $d01a

 lda #RASTER                        ;Hier soll unsere Interrupt auftreten
 sta $d012                      

 lda $d011                          ;Zur Sicherheit höchstes BIT
 and #%01111111                     ;für die Rasterzeile löschen
 sta $d011

 lda #%0111111                      ;Timer-IRQs abschalten
 sta $dc0d
 lda $dc0d                          ;zur Sicherheit bestätigen

 lda #$00                           ;Muster für den 'offenen' Rahmen
 sta $3fff

 lda #%00000001                     ;Sicherheitshalber auch den
 sta $d019                          ;Raster-IRQ bestätigen

 lda #$0B
 sta $d021

 lda #$19                           ;X-Position
 sta $02                            ;auf Zero-Page merken

 ldx #$07                           ;alle 8 Sprites
spriteLoop
 lda #spriteData/64                 ;Spriteadresse berechnen
 sta $07f8,X                        ;Spritepointer
 txa
 sta $d027,X                        ;Farbe
 asl                                ;SpriteNr * 2
 tay                                ;ins Y-Register
 lda $02                            ;X-Pos in den Akku
 sta $d000,Y                        ;und fürs jeweilige Sprite setzen
 clc
 adc #$20                           ;X-Pos für nächstes Sprite erhöhen
 sta $02                            ;und auf der Zero-Page speichern
 lda #SPRITESYPOS                   ;Y-Position immer gleich
 sta $d001,Y
 dex                                ;Schleifenzähler verringern
 bpl spriteLoop                     ;solange positiv, nächstes Sprite

 lda #SPRITESACTIVE                 ;Sprites aktivieren
 sta $d015              
 cli                                ;Interrupts erlauben
 
 rts                                ;zurück zum BASIC



*=$0c00
myIRQ
;*** Wenn wir hier landen, sind bereits 38-45 Taktzyklen in der
;*** aktuellen Rasterzeile (ab jetzt als STARTROW bezeichnet) vergangen!

;*** zweiten IRQ einrichten
;*** da die Zeit bei aktivierten ROM nicht reicht, können wir den
;*** 2. Raster-IRQ erst in der übernächsten Zeile (STARTROW+2) bekommen.
 lda #<doubleIRQ                    ;(2 TZ) 2. Raster-IRQ einrichten
 sta $0314                          ;(4 TZ)
 lda #>doubleIRQ                    ;(2 TZ)
 sta $0315                          ;(4 TZ)
 tsx                                ;(2 TZ) Stackpointer im X-Reg. retten
 stx doubleIRQ+1                    ;(4 TZ) und fürs zurückholen sichern!
 nop                                ;(2 TZ)
 nop                                ;(2 TZ)
 nop                                ;(2 TZ)
 lda #%00000001                     ;(2 TZ) 1. Raster-IRQ später bestätigen
                                    ;------
                                    ;26 TZ
                                    
;*** Jetzt sind 64-71 Taktzyklen vergangen und wir sind
;*** aufjedenfall in nächsten Rasterzeile (STARTROW+1)!
;*** Verbraucht wurden dort 1-8 TZ
 inc $d012                          ;(6 TZ) 2. IRQ in der übernächsten Zeile STARTROW+2
                                    ;       $D012 wurde bereits automatisch erhöht
 sta $d019                          ;(4 TZ) IRQ bestätigen
 cli                                ;(2 TZ) Interrupts für den 2. Raster-IRQ
                                    ;       wieder freigeben

;*** Wir befinden uns in Rasterzeile STARTROW+1 und 
;*** haben bisher 13-20 Zyklen verbraucht

;*** etwas Zeit verschwenden...
 ldx #$08                           ;            2 TZ
 dex                                ;8 * 2 TZ = 16 TZ
 bne *-1                            ;7 * 3 TZ = 21 TZ
                                    ;1 * 2 TZ =  2 TZ
                                    ;          ------
                                    ;           41 TZ

;*** Bis hier sind 54-61 Taktzyklen vergangen, jetzt auf den IRQ warten...
;*** Der nächste Rasterinterrupt wird während dieser NOPs auftreten!
 nop                                ;2 TZ (56)
 nop                                ;2 TZ (58)
 nop                                ;2 TZ (60)
 nop                                ;2 TZ (62)
 nop                                ;2 TZ (64)
 nop                                ;2 TZ (66)

doubleIRQ
;*** Wir sind nun in Rasterzeile STARTROW+2 und
;*** haben bisher genau 38 oder 39 Taktzyklen benötigt!!
;*** Wir können so sicher sein, da der IRQ während der NOPs auftrat.

;*** Jetzt exakt soviele Taktzyklen 'verschwenden', wie in 
;*** dieser Zeile noch zu verarbeiten sind (also 24 oder 25).
 ldx #$00                           ;(2 TZ) Platzhalter für 1. Stackpointer
 txs                                ;(2 TZ) Stackpointer vom 1. IRQ wiederherstellen
 lda #SPRITESACTIVE                 ;(2 TZ)
 sta $d015                          ;(4 TZ)
 bit $01                            ;(3 TZ)
 lda #$0e                           ;(2 TZ) 
 ldy #$01                           ;(2 TZ)
 ldx $d012                          ;(4 TZ)
 cpx $d012                          ;(4 TZ) sind wir noch in Rasterzeile STARTROW+2?
                                    ;======
                                    ;25 TZ = 63 oder 64 TZ!!!

 beq myIRQMain                      ;(3 TZ) wenn JA einen letzten Takt 'verschwenden'
                                    ;(2 TZ) sonst einfach weiterlaufen...

;*** Wir beginnen also immer exakt nach 3 TZ in der dritten Rasterzeile (STARTROW+3)
;*** nach dem 1. Raster-IRQ (den hatten wir ja für Zeile STARTROW festgelegt)
myIRQMain
                                    ; 3TZ (s. oben)
 ldx #$ae                           ;ein paar Leerzeilen einfügen

 dex
 bne *-1  

 ;Linie 'zeichnen' (Rahmen + Hintergrund!)             
 sty $d021
 sty $d020
 ldx #$0b
 dex
 bne *-1
 bit $02
 sta $D020
 lda #$0b
 sta $d021

 lda $d011
 ora #%00001000                     ;Anzahl der Zeilen wieder auf 25
 sta $d011

 lda #<openBorderIRQ                ;IRQ zum Öffnen des Rahmens einrichten
 sta $0314
 lda #>openBorderIRQ
 sta $0315 
 lda #$f9                           ;Zeile für IRQ 
 sta $d012

 lda #%00000001                     ;IRQ bestätigen
 sta $d019

 pla                                ;Register vom Stack holen
 tay
 pla
 tax
 pla

 rti                                ;IRQ verlassen

openBorderIRQ
 lda $d011                      
 and #%11110111                     ;Anzahl der Zeilen auf 24
 sta $d011

 lda #$00                           ;Sprites abschalten, sonst tauchen die
 sta $d015                          ;im unteren Rahmen nochmal auf

 lda #<myIRQ                        ;Original IRQ-Vektor setzen
 sta $0314
 lda #>myIRQ
 sta $0315

 lda #RASTER                        ;ursprüngliche Rasterzeile zurücksetzen
 sta $d012

 lda #%00000001                     ;IRQ bestätigen
 sta $d019

 jmp $ea31                          ;zum Schluß zum 'Timer-Interrupt' springen

*=$0fc0
spriteData                          ;Test-Sprite
 !byte $fe,$fe,$fe
 !byte $fe,$fe,$fe
 !byte $fe,$fe,$fe
 !byte $fe,$fe,$fe
 !byte $fe,$fe,$fe
 !byte $fe,$fe,$fe
 !byte $fe,$fe,$fe
 !byte $fe,$fe,$fe
 !byte $fe,$fe,$fe
 !byte $fe,$fe,$fe
 !byte $fe,$fe,$fe
 !byte $fe,$fe,$fe
 !byte $fe,$fe,$fe
 !byte $fe,$fe,$fe
 !byte $fe,$fe,$fe
 !byte $fe,$fe,$fe
 !byte $fe,$fe,$fe
 !byte $fe,$fe,$fe
 !byte $fe,$fe,$fe
 !byte $fe,$fe,$fe
 !byte $fe,$fe,$fe

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:

SPRITESACTIVE   = %00000001     ;welche Sprites sollen aktiviert werden
SPRITESYPOS     = $11           ;Y-Position für alle Sprites
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.

Legende
-------
P-1 VIC = Phase 1 hier gehört der Bus dem VIC
P-2 CPU = Phase 2 der Bus sollte der CPU zur Verfügung stehen
P-2 VIC = Phase 2 der VIC hat den Bus übernommen

c = Videospeicher & Farb-RAM Zugriff
g = Zugiff auf Zeichensatz oder Bitmap
r = RAM-Refresh
p = Spritepointer lesen
s = Spritedaten lesen
x = CPU Zugriff (lesen / schreiben)
X = evtl. CPU Schreibzugriff (Lesezugriff stoppt den Prozessor)

 

normale Zeile (aktive Sprites: keine)
         111111111122222222223333333333444444444455555555556666 
123456789012345678901234567890123456789012345678901234567890123 Zyklen
p p p p p rrrrrgggggggggggggggggggggggggggggggggggggggg  p p p  P-1 VIC
                                                                P-2 VIC
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx P-2 CPU

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.

 

normale Zeile (aktive Sprites: 0)
         111111111122222222223333333333444444444455555555556666
123456789012345678901234567890123456789012345678901234567890123
p p p p p rrrrrgggggggggggggggggggggggggggggggggggggggg  psp p  P-1 VIC
                                                         ss     P-2 VIC
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxXXX  xxxx P-2 CPU

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.

 

Bad Line (aktive Sprites: keine)
         111111111122222222223333333333444444444455555555556666 
123456789012345678901234567890123456789012345678901234567890123 Zyklen
p p p p p rrrrrgggggggggggggggggggggggggggggggggggggggg  p p p  P-1 VIC
              cccccccccccccccccccccccccccccccccccccccc          P-2 VIC
xxxxxxxxxxxXXX                                        xxxxxxxxx P-2 CPU

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.

 

Bad Line (aktive Sprites: ALLE)
         111111111122222222223333333333444444444455555555556666 
123456789012345678901234567890123456789012345678901234567890123 Zyklen
pspspspspsrrrrrgggggggggggggggggggggggggggggggggggggggg  pspsps P-1 VIC
ssssssssss    cccccccccccccccccccccccccccccccccccccccc   ssssss P-2 VIC
          xXXX                                        XXX       P-2 CPU

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.

RASTER          = $20               ;Hier den IRQ auslösen

;*** Startadresse 
*=$0801
;** BASIC-Zeile: 2018 SYS 2064:NEW
 !word main-2, 2018 
 !byte $9e
 !text " 2064:"
 !byte $a2,$00,$00,$00

main
 sei                                ;IRQs sperren

 lda #<myIRQ                        ;Adresse unserer Routine in
 sta $0314                          ;den RAM-Vektor
 lda #>myIRQ
 sta $0315

 lda #%00000001                     ;Raster-IRQs vom VIC-II aktivieren
 sta $d01a

 lda #RASTER                        ;Hier soll unsere Interrupt auftreten
 sta $d012                      

 lda $d011                          ;Zur Sicherheit höchstes BIT
 and #%01111111                     ;für die Rasterzeile löschen
 sta $d011

 lda #%0111111                      ;Timer-IRQs abschalten
 sta $dc0d
 lda $dc0d                          ;zur Sicherheit bestätigen

 lda #%00000001                     ;Sicherheitshalber auch den
 sta $d019                          ;Raster-IRQ bestätigen

 lda #RASTER-$14                    ;Sprite-0 positionieren
 sta $d001                          ;Y-Pos. setzen (X-Pos. ist egal)
 ldx #$00
 stx $d017                          ;Y-Vergrößerung zur Sicherheit abschalten
 inx
 stx $d015                          ;Sprite-0 aktivieren
 
 cli                                ;Interrupts erlauben

 rts                                ;zurück zum BASIC



myIRQ
 lda $d020                          ;aktuelle Farbe 'merken'
 ldx #$01                           ;Farbe für die Linie ins X-Reg
 nop
 nop

 inc $ffff                          ;hier geschieht das Timing
 inc $ffff                          ;hier geschieht das Timing

 stx $d020                          ;Linie beginnen
 ldx #$0a                           ;etwas warten
 dex
 bne *-1
 nop
 sta $d020                          ;Linie beenden

 lda #%00000001                     ;IRQ bestätigen
 sta $d019

 jmp $ea31                          ;zur System-Routine

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! (14 Bewertungen | Ø 4,64 von 5 | 92,86%)

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