Der Rasterzeileninterrupt

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 StudioWie ein Rasterzeileninterrupt prinzipiell funktioniert

Nachdem geklärt wurde, was ein Interrupt eigentlich ist und was dabei passiert, geht es nun um den, für Spiele- und Demo-Programmierer wohl wichtigsten Interrupt des C64, den Rasterzeileninterrupt. Dieser ist zunächstmal ein ganz normaler IRQ, aber woher kommt er und wann tritt er auf?

Auslöser des Rasterinterrupts ist der Grafikchip VIC-II. Um zu verstehen was dieser Rasterzeileninterrupt genau ist und wann er auftritt, ist es hilfreich zu wissen, wie das Bild, dass man auf dem Fernseher (Monitor) sieht, eigentlich entsteht. Da heutige LCDs und TFTs anders funktionieren, benutze ich zur Erklärung die Funktionsweise eines guten alten Röhrenfernsehers, wie er zur Zeit des C64 verwendet wurde.

 

Grundprinzip eines Röhrenfernsehers
Ein Röhrenfernseher besteht im Prinzip nur aus einer ‚Kanone‚ die sog. Kathode, die einen Elektronenstrahl auf die beschichtete Innenseite des Bildschirms schießt. Treffen die Elektronen jetzt auf die Beschichtung, so beginnt diese an der Stelle zu leuchten und man sieht einen Punkt. Um ein ganzes Bild darzustellen wird der Strahl durch die sog. Ablenkeinheit ‚bewegt‚. Er wandert so (wenn man von vorne auf den Ferseher schaut) erstmal von links nach rechts. Ist er am Ende der Zeile angelang, wird er abgeschaltet und an den Beginn der nächsten Zeile gebracht (dies nennt sich horizontal blank). Dann wird diese Zeile ‚geschrieben‚ usw. Ist der Strahl rechts unten, an der letzten Position angelangt, wird er auch wieder abgeschaltet, aber dann ganz zurück nach links oben an die erste Position gebracht (dies ist der sog. vertical blank).

Rasterzeileninterrupt: Funktionübersicht Röhrenfernseher
Funktionübersicht Röhrenfernseher

Beim Erstellen des Bildes, bestimmt die Intensität des Strahls, ob bzw. wie stark der jeweilige Punkt leuchten soll. Die Sonne muss schließlich heller dargestellt werden als eine Wand im dunklen Zimmer.
Bei einem Farbbildschirm gibt es drei Strahlen und die Beschichtung besteht pro Bildpunkt aus drei unterschiedlichen ‚Punkten‚ je Farbe. Leuchten diese drei Punkte zusammen auf, so ergeben sie (durch additive Farbmischung) die Farbe eines einzelnen Bildpunktes.

Nah an der Röhre.
Nah an der Röhre.

Die drei Farben sind euch bestimmt schon bekannt, es handelt sich um ROT, GRÜN und BLAU häufig auch kurz als RGB bezeichnet. Geht ihr sehr dicht an einen Röhrenfernseher heran oder benutzt eine Lupe, dann könnt ihr die Farben auch direkt erkennen.

 

Das war jetzt natürlich wieder sehr vereinfacht, ganz so simpel ist der Vorgang dann doch nicht, aber weitere technische Details (wie z. B. die Halbbilder) spare ich mir. Für uns als Programmierer ist erstmal wichtig, dass der Strahl von links nach rechts und von oben nach unten läuft. Die Geschwindigket mit der er dass macht hängt von der verwendeten Fernsehnorm ab. In Deutschland (und den meisten Ländern Europas) ist es PAL, in Amerika aber NTSC. Bei PAL wird das Bild 50 mal in der Sekunde aufgebaut. Diese 50Hz kennt ihr bestimmt auch von unserer Netzspannung, ‚aus‚ der Steckdose kommen 230V mit 50Hz. Auf einem NTSC-Fernseher sind es sogar 60 Bilder pro Sekunde! (In den USA kommen 110V mit 60Hz aus der Steckdose). Die höhere Bildwiederholrate wird aber durch eine geringere Zeilenanzahl ‚erkauft‚. NTSC-Geräte haben üblicherweise 480 Zeilen, PAL-Systeme dagegen 576. Der Unterschied zwischen PAL und NTSC, wird uns später bei der Programmierung noch weiter beschäftigen. Es kann passieren, dass ihr eure Programme für die jeweilige Fernsehnorm anpassen müsst.

 

Wenn nicht explizit etwas Anderes erwähnt wird, gehe ich immer von einem PAL-C64 aus!

 

Wie ein Rasterzeileninterrupt entsteht
Der VIC-II zählt im Register 18 ($D012) die aktuelle Rasterzeile mit. Ähnlich wie bei der Sprite-X-Position reichen 8 BIT aber nicht aus, so dass im Register 17 ($D011) das höchste BIT als 9. BIT für die aktuelle Rasterzeile verwendet wird. Damit man jetzt nicht laufend diese Register manuell kontrollieren muss (könnt ihr aber durchaus machen), gibt es die schöne Möglichkeit, automatisch einen Interrupt auszulösen, wenn eine bestimmte Rasterzeile beginnt. Dies ist dann der RASTERZEILENINTERRUPT.

 

Einen Rasterzeileninterrupt einrichten
Um den Raster-IRQ einzurichten sind eigentlich nur zwei Schritte notwendig.

  1. Dem VIC-II mitteilen, wann genau (also in welcher Zeile) er einen Rasterzeileninterrupt auslösen soll.
    Dazu können die Register $D011 & $D012 nicht nur gelesen, sondern auch beschrieben werden. Schreiben wir jetzt z. B. etwas nach $D012, dann wird sich der von uns geschriebene Wert in einem internen Register gemerkt. Dies ist notwendig, da der VIC-II $D012 ja verändert, um die aktuelle Rasterzeile mitzuzählen.
     
  2. Den Interrupt zu unserer Routine springen lassen.
    Da der Rasterzeileninterrupt nichts anderes als ein ganz normaler Interrupt ist, benutzt er ebenfalls den RAM-Vector an der Adresse $0314/15 , den wir bereits kennen. Wir müssen diesen also nur wieder auf unsere Interrupt-Routine umbiegen.

Auf ins Abenteuer…
Es ist an der Zeit auch mal einen ersten Rasterinterrupt in Aktion zu sehen. Man möge mir den nun folgenden Patriotismus verzeihen, aber die Deutschland-Flagge bietet sich hier einfach an. Das Beispiel soll unseren Bildschirm in den Farben schwarz, rot und gold erstrahlen lassen, während wir weiterhin mit dem BASIC arbeiten können.

Zu Beginn werden drei Konstanten festgelegt, in denen die Zeile steht, ab der die jeweilige Farbe verwendet wird. Später kann man durch Ändern der Werte die Auswirkung auf den Rasterzeileninterrup testen. Danach folgt wieder die ‘neue‚ BASIC-Startzeile 2013 SYS 49152:NEW da auch diesmal das Programm nach  $C000 gelegt wurde, damit es nicht mit einem evtl. BASIC-Programm kollidiert. Ab *=$C000 wird nun alles für den Raster-IRQ vorbereitet.
Als Erstes wieder die Interrupts sperren und dann in $0314/15 die Adresse der neuen Interruptroutine speichern. Das ist bekannt, es wurde schon in den beiden vorangegangenen Beiträgen erklärt. Jetzt muss der VIC-II ‚wissen‚ wann er für uns einen Rasterzeileninterrupt auslösen sollen. Die ‚Flagge‚ soll mit schwarz beginnen, also soll der IRQ in Zeile 0 ausgelöst werden, START_BLACK hat genau diesen Wert bekommen. Den nun einfach ins Register 18 / $D012 schreiben. Wie bereits erwähnt landet der Wert, den wir nach $D012 schreiben in einem internen Register. Da der IRQ in Zeile 0 und nicht versehentlich in der 256. Zeile ausgelöst werden soll, wird anschließend auch noch das ‚9. BIT der Rasterzeile‚ in $D011 gelöscht. Schließlich muss dem VIC-II noch mitgeteilt werden, dass er überhaupt den gewünschten Rasterzeileninterrupt auslösen soll. Dazu im Register 26  $D01A das 1. BIT setzen. Schon ist alles vorbereitet, jetzt die Interrupts wieder freigeben und zurück zum BASIC.

 

Jetzt noch die passende Interrupt-Routine:

Damit der VIC-II ‚weiß‚, dass der Interrupt behandelt wurde, muss man dies durch lesen und schreiben von Register 25  $D019 bestätigen. Ich wähle hier den ‚langen‚ Weg, da $D019 gleich noch geprüft werden soll, man könnte den IRQ auch mit asl $D019 bestätigen. Dies würde auch mit  lsr, inc und dec  klappen, da es sich dabei um sog. READ-MODIFY-WRITE-Befehle handelt. Diese Befehle lesen die aktuelle Speicherstelle, schreiben dann (eigentlich unnötigerweise, aber schon hierdurch wird der IRQ bestätig) den aktuellen Wert nochmal zurück, verändern ihn anschließend und speichern zum Schluß das Ergebnis. Dadurch ist die Anforderung des Bestätigens, des Rasterzeileninterrupts erfüllt. Mit einer SuperCPU klappt das übrigens nicht! Diese verzichtet auf das (unnötige) Zurückschreiben des unveränderten Wertes und somit wird der IRQ nicht bestätigt.

 

Beim Auftreten des Raster-Interrupts, wird dann $D012 in den Akku geladen. Dort steht ja die aktuelle Rasterzeile. Ist diese ungleich 0, geht es weiter zur Prüfung für rot beim Label dored:. Wenn sie aber null ist, dann schwarz in den Akku und Rahmen- und Hintergrundfarbe auf schwarz setzen. Nach schwarz folgt bekanntlich rot, also den Beginn für rot nach $D012 schreiben, damit dort der nächste Rasterinterrupt ausgelöst wird. Um die Interruptroutine zu verlassen, nach  rasterirq_exit: springen.

 

Bei rot läuft es nahezu identisch, wie bei schwarz. Es wird geprüft, ob die aktuelle Rasterzeile (die steht immer noch im Akku!), die von rot ist, falls nicht geht es bei dogold:  mit gold weiter. Sonst wird die Rahmen- und Hintergrundfarbe einfach auf rot gesetzt und der Beginn von gold für den nächsten IRQ nach $D012 geschrieben. Danach geht es beim EXIT weiter.

 

Da nur drei Rasterzeilen als Auslöser für den IRQ in Frage kommen, wird bei dogold: nicht mehr geprüft, ob es die Zeile von gold ist. Es wird (in Ermangelung der Farbe gold) jetzt gelb als Farbe verwendet und als nächste Zeile für den Rasterzeileninterrupt, die von schwarz nach $D012 geschrieben. Hier läuft das Programm nun automatisch zum EXIT weiter.

 

Das Ende sollte bekannt sein. Da die System-Routine ab $FF48 (es handelt sich, wie bereits mehrfach erwähnt, beim Rasterzeileninterrupt um einen ganz normalen Interrupt) die Register auf den Stack gelegt hat, werden diese zurück geholt und dann per rti der Interrupt verlassen.

 

Zur Übersicht nochmal das komplette Programm:

 

Ein Start offenbart dann aber, wie fehlerhaft es noch ist. Der Bildschirm hat überhaupt keine Ähnlichkeit mit einer Flagge und Eingaben kann man ebenfalls nicht vornehmen. 🙁

Der Rasterzeileninterrupt will nicht ganz.
Der Rasterzeileninterrupt will nicht ganz.

 

Was läuft da schief?
Bei genauer Betrachtung sind alle drei Farben zwar vertreten, aber eben nicht so wie gedacht. Wer die letzten beiden Beiträge über die Interrupts verfolgt hat, dem ist evtl. schon klar, was hier das Problem ist. Der Raster-IRQ stellt ja einen ganz normalen Interrupt dar, aber wie bei ‚Interrupts‚ beschrieben, findet auch 60 mal in der Sekunde der Interrupt für die Tastatur, Uhrzeit und das Cursor-Blinken statt. Auch dieser springt über den RAM-Vector an $0314/15 zur Interrupt-Routine und landet so bei rasterirq:. Dadurch kommt dann das ganze System aus dem Tritt. Da $D012 für schwarz und rot geprüft wird, läuft die Routine meistens in den Abschnitt für gold, also dominiert diese Farbe. Um festzustellen, ob der Interrupt vom VIC-II kam, kann $D019 geprüft werden. Ist das höchste BIT gesetzt, dann kam der Interrupt vom VIC-II und man kann mit den ‚unteren‚ BITs die genaue Quelle identifizieren.

Also einfach den Abschnitt mit den Befehlen lda $D019  und sta $D019 etwas umbauen (die Änderungen sind gelb hervorgehoben):

Da nur der Raster-IRQ verwendet wird, reicht es zu prüfen, ob $D019 negativ ist. Falls der Akku nun negativ ist, kam der Interrupt vom VIC-II und es geht bei dorasterirq: weiter, wenn er positiv ist gehts zum EXIT.

Schon besser, aber es gibt noch Probleme
Schon besser, aber es gibt immer noch Probleme

Das sieht doch schon besser aus, aber es bestehen noch zwei Probleme:

  1. Eine Eingabe ist immer noch nicht möglich
  2. Es kommt zu einem Versatz (s. Markierungen im Bild)

 

Um endlich wieder die Eingabe zu ermöglichen, muss die Routine nur zur bekannten System-Routine nach $EA31 springen, wenn es sich um keinen Raster-IRQ handelt.

Also den Beginn der Interrupt-Routine nochmal etwas umbauen:

Mit  lda $DC0D wird der Interrupt, der ja vom CIA1 kam, bestätigt, dann werden die Interrupts wieder erlaubt und zur Systemfunktion gesprungen. Ich empfehle übrigens sich mal anzusehen, was passiert, wenn man auf das lda oder den cli verzichtet.
Jetzt sollte sich auch automatisch das zweite Problem gelöst haben, der Versatz ist verschwunden.

Start im Java Emulator

BASIC-Programm läuft neben dem Rasterzeileninterrupt
Das BASIC-Programm läuft neben dem Raster-IRQ

 

Das war also die erste kurze Einführung zum Rasterzeilen-Interrupt.
Wer jetzt verstanden hat, was hier genau geschieht, hat den Schlüssel zur Effekt-Programmierung in der Hand. Mit den hier gezeigten Grundlagen, ist es z. B. möglich einen Splitt-Screen (oben High-Res-Grafik / unten Textbildschirm) oder mehr als acht Sprites gleichzeitig auf den Bildschirm zu zaubern. So, wie hier die Rahmen- und Hintergrundfarbe abhängig von der jeweiligen Rasterzeile geändert wurde, kann man auch alle anderen Register ändern. Laßt eurer Phantasie einfach mal freien Lauf. Auch ein ‚Öffnen‚ des Rahmens, läßt sich durch eine entsprechende Manipulation der zugehörigen Register zur richtigen Zeit erzielen.

Ich möchte jetzt bei Niemandem den Eindruck erwecken, mit diesen paar Infos die neuste Super-Demo schreiben zu können, aber für eigene Experimente ist man nun gewappnet. Um in die Superliga der Rasterprofis aufzusteigen, ist noch viel mehr an Wissen und Arbeit notwendig. Schlagworte, die einem auf dem Weg dahin begegnen sind z. B. Anzahl der Taktzyklen je Rasterzeile bei PAL / NTSC oder die sog. ‚Bad Lines‚.

Ich werde diesen Bereich nach und nach erweitern, außerdem plane ich einen DEMO-Bereich. Dort stelle ich dann kurz und knapp Spielereien wie oben erwähnt (mehr als acht Sprites, HiRes & Text gemischt usw.) vor.

 


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

Loading...


 

<<< zurück | weiter >>>

 

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

8 Gedanken zu „Der Rasterzeileninterrupt“

  1. Hallo Jörn,

    meine eigenen Rasterzeilen-Interrupt-Routinen trage ich ja in $0314 / $0315 ein, damit sie aufgerufen werden. Was muss ich eigentlich tun, um sie wieder auszutragen? (also um beim Beispiel zu bleiben: Wenn ich die deutsche Fahne nur während der Anzeige einer HIghscoreliste sehen will, aber nicht im Spiel selbst, wie kann ich das entsprechend steuern?)

    Vielen Dank & Grüße!

    Carsten

    1. Hallo Carsten,
      leg doch einfach mehrere Raster-IRQ-Routinen an und trage dann immer, die gerade benötigte, bei $0314 / $0315 ein. So kannst du eine fürs Spiel und eine für die Highscoreliste verwenden.

      PS: Falls du die System-Routine wieder verwenden möchtest, dann musst du dir zu Beginn die Werte aus $0314 / $0315 merken und diese bei Bedarf wieder zurückschreiben. Dies kommt dem von dir gewünschten Austragen wohl am nächsten.

      1. danke! Klappt 🙂

        bzw., ich muss bei mir noch ein
        lda $D01A
        and #%11111110
        sta $D01A
        machen (also die IRQs vom VIC wieder deaktivieren), sonst kann ich keine Tastatureingaben abfragen

        1. Richtig!
          Wenn du wieder die System-Routine verwenden möchtest, dann muss auch noch der Raster-IRQ deaktiviert werden. Den nutzt das System nämlich nicht, dort wird nur durch den Timer über $0314 / $0315 gesprungen.
    1. Hallo,
      dort ist aber nicht vom Rasterzeileninterrupt die Rede:

      findet auch 60 mal in der Sekunde der Interrupt für die Tastatur, Uhrzeit und das Cursor-Blinken statt.

      Dieser IRQ kommt vom CIA und läuft auch auf einem PAL-System 60 mal in der Sekunde.

      Daher meine Frage, wo steht etwas von den erwähnten 60 Bildern in der Sekunde bei PAL?

Schreibe einen Kommentar

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

Protected by WP Anti Spam