Raster-IRQ: Timing-Probleme

Für diesen Beitrag wurde das CBM prg Studio verwendet.
weitersagen ...
Tweet about this on TwitterShare on FacebookShare on Google+Share on LinkedIn

CBM prg StudioZeit ist ALLES!

Wer schon etwas mit dem Rasterzeileninterrupt experimentiert hat, dem ist bestimmt aufgefallen, dass nicht immer alles so läuft, wie man es sich gedacht hat. Beim Raster-IRQ ist das Timing meistens immens wichtig, wenn das nicht stimmt erreicht man häufig nicht den gewünschten Effekt.

Um dem Beitrag korrekt folgen zu können ist es wichtig, dass die Beispiele ganz genau übernommen werden, ein Taktzyklus mehr oder weniger und schon kommt evtl. etwas vollkommen anderes heraus. Außerdem setze ich wieder ein PAL-System voraus, wie es bei den in Deutschland verkauften C64-Modellen verwendet wurde.
Da ich auch vergleichen wollte, wie sich die Beispiele auf unterschiedlichen Systemen & Emulatoren verhalten, habe ich sie mit WinVICE 2.4 x64, CCS64 3.9 und Emu64 Beta 5.0.8 (alle drei unter Win8 64Bit & Win7 64Bit), sowie auf dem Turbo Chameleon mit Beta 8f und mit einem echten C64, C64C & C64G getestet. An den entsprechenden Stellen weise ich auf evtl. Unterschiede hin.

Da  WinVICE so schön zum CBM prg Studio passt und sich damit einiges besser debuggen lässt, beziehe ich mich in diesem Beitrag hauptsächlich darauf. Zur Sicherheit sollten eure „C64 Modell Einstellungen…„ so aussehen:

RasterIRQ_001
VICE-Einstellung

 

 

 

 

 

 

 

 

 

Außerdem gehe ich davon aus, dass ihr den Rahmen nicht abgeschaltet habt. Den brauchen wir jetzt nämlich.


Die 1 Pixel-Linie im Rahmen
Um uns die Timing-Probleme vor Augen zu führen, stellen wir uns doch einfach mal die simple Aufgabe eine 1 Pixel hohe Line durch Veränderung der Farbe im oberen Rahmen zu erzeugen. Also flugs ein Programm zusammengeschustert, ist ja ganz simpel:

Das Programm richtet zunächst unseren Interrupt  MyIRQ: ein (Adresse im RAM-Vector hinterlegen, Raster-IRQs erlauben und erste Zeile eintragen). Nach Freigabe der Interrupts bleibt das Programm in einer Endlosschleife ‚hängen‚.

Im Interrupt  MyIRQ: wird dann wieder geprüft, ob dieser ein Rasterzeilen- oder Timer-Interrupt ist. Timer-Interrupts werden dann über  $EA31 verarbeitet. Beim Raster-IRQ wird geprüft, ob dieser in unserer gewünschten Zeile LINESTART stattgefunden hat, falls ja, wird die Rahmenfarbe auf weiß gesetzt und die direkt folgende Zeile  LINEEND als nächster Raster-IRQ festgelegt, dann wird zum Ende gesprungen. Wurde der aktuelle Raster-IRQ nicht bei LINESTART ausgelöst, geht es bei doBlack: weiter. Dort wird geprüft, ob es sich um die Rasterzeile LINEEND handelt, falls nicht geht es zum Ende, sonst wird die Rahmenfarbe auf schwarz gesetzt und als nächste Rasterzeile für den IRQ wieder LINESTART eingetragen. Die Routine läuft dann zum Ende weiter, dort wird der IRQ bestätigt, die Register werden vom Stack geholt und die Routine wird verlassen.

Nach dem Start sehen wir zwar den Ansatz einer Linie, aber gleichzeitig flackert auch eine ganze Menge.

Dieses flackern tritt in allen Emulationen (inkl. Turbo Chameleon 64) auf!

Dieses auffällige Flackern tritt ausnahmslos in allen von mir getesteten Emulationen auf. Auch das Turbo Chameleon 64 zeigt diesen Effekt. Die drei verschiedenen, von mir verwendeten C64 Modelle, verhalten sich aber anders! Sie zeigen größtenteils ein relativ ruhiges Streifenmuster, das nur ab und zu von einem ‚weißblitzer‚ überlagert wird. Da haben wir es wieder, nichts kann die ‚echte‚ Maschine ersetzen. Aber auch das ‚Bild‚ der echten C64-Modelle haben wir so nicht gewollt!

Sobald ihr LINEEND = LINESTART+1 in +2 ändert, hört das nervige Flackern endlich auf, aber das Ergebnis ist trotzdem nicht das Gewünschte.

RasterIRQ_003
Kein Flackern mehr, aber die Linie passt immer noch nicht

Die Linie ist zwei Pixel hoch und rechts zuckt sie noch immer (s. rote Markierung), außerdem ist die Linie hier sogar drei Pixel hoch. Bevor ihr jetzt die Pixel nachzählt, beachtet, dass ich die Einstellung „Doppelte Größe…„ verwende (1 echter Pixel = 2*2 VICE Pixel). Wer genau hinsieht dem fällt auch auf, dass die Linie teilweise als Ganzes flackert und ab und zu auch der gesamte BS weiß aufblitzt. Bevor wir dass in Angriff nehmen, aktiviert bitte zunächst die Funktion ‚Rahmen debuggen‚ von WinVICE. Wählt dazu den Menüpunkt „Einstellungen -> VIC-II Einstellungen…„ aus und markiert im Dialog die entsprechende Funktion.

Rahmen debuggen
Rahmen debuggen

Bestätigt den Dialog mit OK, speichert eure WinVICE Einstellungen und startet das Programm erneut.

WinVICE mit aktiviertem 'Rahmen debuggen'
WinVICE mit aktiviertem ‚Rahmen debuggen‚

Das Aussehen von WinVICE hat sich merklich geändert. Wir sehen nun den gesamten Rasterzeilen-Bereich und müssen feststellen, dass es nicht nur rechts, sondern auch links flackert und sich unsere Linie (zumindest teilweise) in vier Rasterzeilen befindet. Links wäre das Flackern evtl. kein Problem, dies findet im nicht sichtbaren Bereich statt. Wir sehen es ja nur, da wir den ‚Rahmen debuggen‚ und so den kompletten Überblick haben. Diese Einstellung ermöglicht euch also auch einen Blick auf den normalerweise ‚unsichtbaren‚ Bereich (früher war das nur mit Monitoren möglich, bei denen ihr die Bildlage frei einstellen konntet). Der Rasterstrahl legt also erstmal ein gutes Stück zurück, bevor er in den für uns sichtbaren Bereich vordringt.

 

Die Probleme
Wer sich die Mühe macht und im vorangegangenen Bild die Anzahl der Pixel vom höchsten Punkt unserer Linie bis zum oberen Rand zählt, der wird auf 33 Zeilen kommen (beachtet die „Doppelte Größe„!). Wir möchten die Linie in  #$20 = 32 beginnen lassen, da die Zählung mit 0 beginnt ist dies also schon mal die korrekte Zeile. Aber die Linie beginnt viel zuweit rechts! Dies ist auch der Grund, weshalb unser erstes Beispiel so stark geflackert hat. Bis wir die folgende Zeile als neuen Interrupt festgelegt haben, befindet sich der Rasterstrahl schon längst in dieser. Daher wird erst beim nächsten Bildaufbau unser gewünschter IRQ erzeugt und es kommt zum unschönen Flackern des gesamten Rahmens. Durch die Verdopplung der Zeilenhöhe LINEEND = LINESTART+2 haben wir uns mehr Zeit verschafft und das Problem umschifft. Aber wie erwähnt, blitzt hin und wieder immer noch der Rahmen weiß auf und die Linie flackert.

Um unserer Aufgabe (eine 1 Pixel hohe Linie) wieder etwas näher zukommen, verzichten wir jetzt erstmal auf den zweiten IRQ. Bei ‚Raster-IRQ: PAL oder NTSC‚ wurde behauptet auf einem PAL-System stehen uns 63 Taktzyklen je Zeile zur Verfügung. Also ändern wir unser Programm so, dass nach dem Setzen von weiß 63 Zyklen gewartet wird, bevor wir zurück auf schwarz schalten. Versuchen wir also eine durchgängige Linie zu erzeugen.

Die einzige (nenneswerte) Änderung hat hinter MyIRQ_Next: stattgefunden. Da wir nur noch einen IRQ verwenden, prüfen wir nicht mehr in welcher Zeile er auftritt. Wir setzen direkt die Farbe auf weiß und warten solange bis eine vollständige Zeile vergeht, und schalten abschließend zurück auf schwarz. Jetzt haben wir eine vollständige Linie, auch wenn sie noch einen Umbruch hat. Hier kommt es also auf die korrekte Anzahl Taktzyklen an, daher habe ich die als Kommentar ins Listing aufgenommen. Gewöhnt euch besser daran, für wirklich spektakuläre Effekte werdet ihr häufig die präzise Anzahl an Zyklen beachten müssen. Daher müsst ihr obige Routine auch anpassen, wenn ihr diese z. B. auf einem NTSC-System testen wollt. Werft ggf. nochmal einen Blick auf die Tabelle am Ende von ‚Raster-IRQ: PAL oder NTSC‚.

Eine komplette Linie, allerdings noch nicht stabil und mit Umbruch.
Eine komplette Linie, allerdings noch nicht stabil und mit Umbruch.

Wie ihr im Vergleich zu oben seht, ist unsere Problemstelle schon weiter nach links gewandert, aber sie beginnt immer noch viel zu spät und ihr Anfang ist noch nicht ganz stabil. In den Windows-Emulatoren ist die Linie relativ stabil, wohingegen auf den C64-Modellen und beim TC64 Beginn & Ende der Linie schnell flackern. Dies ist die zweite Auffälligkeit zwischen den Software-Emulatoren und echter Hardware bzw. dem Turbo Chameleon 64.
Wer ganz genau hinsieht, wird außerdem bemerken, dass die Linie ca. alle 8-9 Sekunden etwas mehr flackert. Um dass genauer zu sehen, ändert im obigen Programm die Warteschleife doch einfach mal von ldx #$0A auf ldx #$01. Jetzt ist die Linie zwar nicht mehr vollständig, aber das Problem sollte etwas besser zu sehen sein, die Linie ‘spring‚ alle paar Sekunden. Sobald ihr das Problem wahrgenommen habt, ändert die Schleife bitte wieder auf #$0A. Als zweites solltet ihr bemerken, dass der Rahmen nicht mehr komplett weiß aufblitzt. Dies wurde durch den Verzicht auf den zweiten Raster-IRQ und die feste Wartezeit für weiß erreicht.

 

Warum beginnt die Linie so spät und warum springt sie?
Wie ihr euch spätestens durch die vorangegangenen Beispiele denken könnt, ‚läuft‚ der Rasterstrahl unabhängig von unserem Programm über den Bildschirm. Er bewegt sich immer mit der selben Geschwindigkeit. Wenn jetzt der IRQ für unsere Zeile ausgelöst wird und wir würden direkt die Farbe auf weiß ändern, dann müsste die Linie doch eigentlich links mit weiß beginnen. Wie wir gesehen haben, ist dem aber nicht so. Bevor unser Befehl ausgeführt wird, geschieht noch eine ganze Menge. Eigentlich solltet ihr einiges schon wissen, aber wir begeben uns nochmal zurück an den Anfang zu ‚Interrupts‚, um die einzelnen Punkte aufzulisten.

  1. Tritt direkt vor dem Raster-IRQ ein anderer Interrupt (z. B. der Timer-Interrupt) auf, dann beginnt die entsprechende Routine (beim Timer wird nach $EA31 gesprungen). Sobald ein IRQ auftritt, werden bekanntlich weitere Interrupts gesperrt und erst durch ein RTI oder CLI wieder erlaubt. Tritt nun ein IRQ unmittelbar vor erreichen unserer Rasterzeile auf, dann vergeht natürlich einiges an Zeit, bis Interrupts wieder erlaubt werden und der Raster-IRQ endlich verarbeitet werden kann. Dies ist der Grund, weshalb unsere Linie alle 8 bis 9 Sekunden springt.
     
  2. Sobald der Rasterstrahl unsere gewünschte Zeile erreicht, wird ein IRQ ausgelöst. Allerdings verarbeitet der Prozessor erstmal den aktuellen Befehl, bevor er sich tatsächlich um den Interrupt kümmert. Da Befehle bis zu acht (bei Verwendung von illigalen Op-Codes) Taktzyklen benötigen, haben wir hier schon wieder eine Quelle für einen Versatz. Dieses Verhalten ist außerdem für das häufige Flackern des Linienanfangs verantwortlich. Da es ja bei jedem Raster-IRQ ein anderer Befehl sein kann, der gerade verarbeitet wird. Bei uns im Emulator ist die Linie relativ stabil, da wir einfach in einer Endlosschleife hängen. Fügt ihr aber auch nur ein NOP am Ende der IRQ-Routine vors RTI ein, dann flackert auch unserer Anfang wieder recht heftig.

  3. Dann springt der Prozessor zu der an  $FFFE/FF  hinterlegten Adresse $FF48. Dort werden die Register auf dem Stack gesichert, kontrolliert ob es sich um ein BRK oder IRQ handelt und dann beim IRQ per indirektem Sprung zu der Adresse von  $0314/15 gesprungen. Das alles kostet auch Zeit.
     
  4. Jetzt sind wir in unserer Interrupt-Routine, aber bevor wir die Farbe ändern, prüfen wir ob es sich überhaupt um einen Rasterzeileninterrupt handelt, wenn nein geht es zur System-Routine und erst dann wird die Farbe in den Akku geladen und endlich für den Rahmen gesetzt.

Es werden also eine ganze Menge Befehle ausgeführt, bevor wir endlich zum Setzen der Farbe kommen. Außerdem können uns auch noch externe Einflüsse (wie der Timer-IRQ) dazwischen funken. Es gilt jetzt diese Probleme zu minimieren. Für den Moment versuchen wir die Zeit bis zu unserem Interrupt so kurz wie möglich zu halten.

Da wir den Befehl, den die CPU gerade am Wickel hat, nicht direkt beeinflussen können, kümmern wir uns erstmal um die restlichen Probleme.

 

Bye-bye, ROM!
Ein Haufen Zeit geht für die ROM-Routine an $FF48 drauf. Es wäre doch viel schöner, wenn wir direkt zu unserer Routine kämen. Da es sich hier aber um das ROM handelt können wir dort keine anderen Werte eintragen. Aber zum Glück hat der C64 ja den 6510 und der bietet an der Zero-Page-Adresse $01 die Möglichkeit das ROM zu deaktivieren.

Sobald das ROM ausgeschaltet ist, können wir etwas in  $FFFE/FF  ablegen. Bei einem Interrupt ist es der CPU nämlich egal, ob sich die Adresse im ROM oder RAM befindet. Es wird einfach zu der an  $FFFE/FF hinterlegten Adresse gesprungen.

Ändern wir unser Programm nun dementsprechend:

Als erstes müssen wir noch daran denken, auch den Timer-IRQ zu deaktivieren. Ohne KERNAL-ROM gibt es natürlich auch keine Systemfunktion bei $EA31 mehr und wir können mit dem Timer im Moment nichts anfangen. Außerdem lösen wir damit auch unser 1. Problem. Ohne Timer-IRQ springt auch unsere Line alle 8 bis 9 Sekunden nicht mehr. Dann deaktivieren wir das KERNAL-ROM und damit auch automatisch das BASIC-ROM! Zum Schluß legen wir die Adresse unserer IRQ-Routine an $FFFE/FF, statt wie bisher bei $0314/15, ab.

Jetzt können wir noch die Interrupt-Routine bereinigen. Da wir keinen Timer-IRQ mehr haben, können wir die Prüfung rausschmeißen. Eigentlich müssten wir jetzt selbst fürs Retten der Register sorgen, aber da unser Programm in einer Endlosschleife hängt und kein anderer IRQ mehr auftreten kann verzichten wir aufs Retten und Zurückholen der Register. Dass geht nur hier, normalerweise bringt ihr den C64 damit zum Absturz!

 

Hier nochmal das komplette Programm…

 

Wir nähern uns endlich dem linken Rand. Der Linienbeginn (und damit auch deren Ende) flackern zwar, aber wir haben es fast geschafft.

Wie man mit 'vollem' Rahmen sieht, sind wir fast am Ziel
Wie man mit ‚vollem‚ Rahmen sieht, sind wir fast am Ziel

Ich habe hier WinVICE mal auf ‚Voller Rahmen‚ gestellt um zu zeigen, dass wir es fast geschafft haben.


Was können wir jetzt tun um endlich ganz nach links zu gelangen?

NICHTS!  😥

Wir haben das ROM und den Timer-IRQ ausgeschaltet!!
Wir verzichten aufs Retten und Zurückholen der Register, obwohl das normalerweise nicht möglich ist!!!
Wir setzen direkt zu Beginn des Interrupts die Farbe auf weiß!!!!
Es gibt nichts mehr, was wir einsparen könnten!!!!!

Unser bisheriges Vorgehen führt einfach nicht zum Erfolg, wir müssen nach einer anderen Lösung Ausschau halten.

Seid jetzt bitte nicht enttäuscht, hier habt ihr viel Grundlagen-Wissen erhalten, dass ihr unbedingt für den nächsten Schritt zum Rasterzeilen-Profi braucht!
OK, ich nerve euch teilweise damit, aber ich kann einfach nicht anders: Programmieren lernt man nur in der Praxis. Es gehört einfach dazu Fehlschläge zu erleiden und Enttäuschungen zu überwinden. Nur wer dass übersteht, wird zu einem wirklich guten und fähigen Programmierer werden.

Jetzt halte ich es wie Kill Bill: Vol. 1 und lasse euch etwas ratlos und unzufrieden zurück.

 
Fortsetzung folgt…  😉

 


Schrott!!Naja...Geht so...Ganz gut...SUPER! (7 Bewertungen | Ø 5,00 von 5 | 100,00%)

Loading...


 

<<< zurück | weiter >>>

 

weitersagen ...
Tweet about this on TwitterShare on FacebookShare on Google+Share on LinkedIn

Schreibe einen Kommentar

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

Protected by WP Anti Spam