Rasterzeile

Aus C64-Wiki
Zur Navigation springenZur Suche springen
Spielszene aus dem Spiel "Nippon"; Rasterzeile 104 wurde hervorgehoben. Rechts oben ist ein Ausschnitt vergrößert dargestellt; hier sieht man gut, dass die Rasterzeile genau ein Pixel breit ist.

Eine Rasterzeile ist eine Zeile auf dem Bildschirm. Diese besteht aus mehreren Pixeln, beim C64 sind das je nach verwendetem Video-Chip 504 (PAL) oder 512 Pixel (NTSC). Auch die Anzahl der Rasterzeilen auf dem Bildschirm hängt vom Video-Chip ab: 312 (PAL) oder 262/263 (NTSC).


Aufbau einer Rasterzeile durch den Grafikchip[Bearbeiten | Quelltext bearbeiten]

Bei Computern, die einen Kathodenstrahlröhrenmonitor benutzen, wird der Bildschirminhalt meist zeilenweise aufgebaut, wobei unterschiedliche Grafikelemente vom Video-Chip kombiniert werden: Beispielsweise Sprites, Text und der Bildrahmen. Der Rasterstrahl, ein Elektronenstrahl im Monitor, zeichnet die so berechnete Zeile auf den Bildschirm.

Nutzungsmöglichkeiten[Bearbeiten | Quelltext bearbeiten]

Ob und wie Rasterzeilen in Programmen genutzt werden können, hängt stark davon ab, welche Informationen der Video-Chip preis gibt, und auf welche Art. Wenn genügend schnell auf das Erreichen einer Rasterzeile reagiert werden kann, können überraschende Effekte erreicht werden, beispielsweise Rasterbars, Sprite-Multiplexer, Split-Screens, Flexible Line Interpretation und vieles mehr.

Beim BASIC 7.0 am C128 wird der Rasterzeilen-Interrupt als Basis für zeitliche Vorgänge für diversen Befehlen (PLAY, MOVSPR) herangezogen. Dabei dient Bildwiederholfrequenz als Grundlage, also jene Zeit, die benötigt wird, um eine bestimmte Rasterzeile erneut zu erreichen. Dies ist abhängig vom jeweiligen TV-Standard (50 Hz bei PAL und 60 Hz bei NTSC).

VIC[Bearbeiten | Quelltext bearbeiten]

Der VIC kennt drei Möglichkeiten um Rasterzeilen abzufragen:

  1. Die aktuelle Nummer kann durch die Register des VIC abgefragt werden.
  2. Der VIC kann so programmiert werden, dass er beim Erreichen einer zuvor festgelegten Rasterzeile einen Interrupt auslöst.
  3. Mit einem Lightpen als Eingabegerät wird die Rasterzeile (und -spalte!), in der sich der Lightpen befindet, in Registern des VIC abgelegt.

Abfrage durch Register[Bearbeiten | Quelltext bearbeiten]

Da es bis zu 312 Rasterzeilen gibt, werden 9 Bit zum Speichern dieser Information benötigt. Die acht niederwertigen Bits befinden sich in Register 18 ($D012) des VIC und das verbleibende Bit ist das Bit 7 in Register 17 ($D011). Theoretisch könnte man mit folgender BASIC-Formel die Rasterzeile bestimmen:

PEEK(53266) + 2 * (PEEK(53265) AND 128)

Allerdings läuft die Bildschirmausgabe während der Ausführung dieser Formel weiter, sodass die Ergebnisse der beiden PEEKs sich auf unterschiedliche Rasterzeilen beziehen und das Ergebnis somit weitgehend nutzlos ist. Selbst in Maschinensprache kann es sein, dass zwischen den beiden Abfragen die Rasterzeile wechselt. In ungünstigen Fällen kann dies auch hier zu fehlerhaften Ergebnissen führen.

In den meisten Fällen möchte man allerdings gar nicht die aktuelle Rasterzeile wissen, sondern nur abwarten, bis eine bestimmte Rasterzeile erreicht wurde. Bei den Rasterzeilen 56 bis 255 (bei PAL, bei NTSC sind es noch ein paar mehr und die Formeln unten ändern sich etwas) kann man einfach auf die Abfrage von Register 17 verzichten, da dieses in diesem Fall immer eine 0 in Bit 7 enthält. Damit deckt man auch schon einen guten Teil des Bildschirms außerhalb des Rahmens ab.

In den anderen Fällen muss man in mehreren Schritten vorgehen (wobei hier nur jeweils eine von mehreren möglichen Vorgehensweisen aufgeistet ist):

  • Für die Rasterzeilen n>255 wartet man zuerst auf Rasterzeile 255, wie oben, und danach darauf, dass sich die Zahl n-256 in Register 18 befindet.
  • Bei den Rasterzeilen n<55 benötigt man sogar drei Schritte: Auf Zeile 255 warten, dann auf die Zahl 55 in Register 18 warten (das ist dann die unterste Rasterzeile) und dann auf n warten.
  • Rasterzeile 55 ist ein Sonderfall, den man so erreicht: Auf 1 in Register 17 warten, dann auf 0 in Register 17 warten und dann auf 55 in Register 18 warten.

Oftmals kann man auch schon abschätzen, dass sich die aktuelle Rasterzeile in einem bestimmten Bereich des Bildschirms befindet. Das kann die Abfragen deutlich vereinfachen.

Ausgabe des nebenstehenden Programms; der Cursor wurde oberhalb von Rasterzeile 108 positioniert, um die Zeile besser sehen zu können.

Das nachfolgende Beispielprogramm kann man dazu nutzen, herauszufinden, welche Zeile zu welcher Nummer gehört: Es markiert Zeile 108, indem die Hintergrundfarbe in dieser Zeile geändert wird und in der darauffolgenden wieder zurück. Das leichte Flackern am linken Ende der Zeile kommt daher, dass zwischen dem Abfragen der Zeile und der Reaktion etwas Zeit vergeht. Für ein flackerfreies Programm muss mehr Aufwand betrieben werden.

      sei               ; Interrupt wegen besserem Timing abschalten

loop1 lda $d018         ; Auf Zeile 108 warten
      cmp #108
      bne loop1
      inc $d021         ; Hintergrund +1

loop2 lda $d018         ; Auf Zeile 109 warten
      cmp #109
      bne loop2
      dec $d021         ; Hintergrund -1

      jmp loop1         ; Zurück zum Anfang

Interrupt auslösen[Bearbeiten | Quelltext bearbeiten]

Ausgabe des nebenstehenden Programms.

Der VIC kann so programmiert werden, dass beim Erreichen einer bestimmten Rasterzeile ein Interrupt ausgelöst wird. Dazu schreibt man in die Register 17 ($D011) und 18 ($D012) die gewünschte Zeile und setzt dann Bit 0 in Register 26 ($D01A). Zudem muss man natürlich noch die Interrupt-Routine verbiegen und schreiben.

         sei              ; Interrupt ausschalten, während wie ihn ändern

         lda #108         ; Rasterzeile einstellen
         sta $d012
         lda $d011        ; 8. Bit auf 0 setzen
         and #$7f
         sta $d011

         lda $d01a        ; Rasterzeilen-Interrupt anschalten
         ora #$01
         sta $d01a

         lda #<irq        ; Interrupt-Routine auf eigene Routine umbiegen
         sta $0314
         lda #>irq
         sta $0315

         cli              ; fertig
         rts

irq      lda $d019        ; Bit 7 aus Register 25 ($D019) ist gesetzt,
         bmi vic_irq      ; wenn der VIC die Ursache der Interrupts war

         lda $dc0d        ; wenn nicht war es die CIA. Interrupt dort bestätigen,
                          ; damit nicht sofort ein neuer Interrupt ausgelöst wird,
                          ; sobald das Interrupt-Flag gelöscht wird
         cli              ; Interrupts zulassen, damit der Rasterzeilen-Interrupt
                          ; die Abarbeitung des System-Interrupts unterbrechen kann
         jmp $ea31        ; Zum System-Interrupt

vic_irq  inc $d021        ; Hintergrund +1
         dec $d021        ; Hintergrund -1

         sta $d019        ; VIC-Interrupt bestätigen, damit nicht sofort ein neuer
                          ; Interrupt ausgelöst wird.

         pla              ; Interrupt geordnet beenden
         tay
         pla
         tax
         pla
         rti

Es mag ziemlich überraschen, dass die auf dem Bildschirm markierte Stelle sich am rechten Ende der Zeile 108 befindet. Zwischen Aufruf der Interrupt-Routine und dem Ändern der Hintergrundfarbe sind ja gerade mal 13 Taktzyklen vergangen (LDA benötigt 4, BMI beim Springen 3 und INC 6 Taktzyklen) und der Aufbau einer Zeile dauert 63 Taktzyklen.

Der Grund ist, dass die Interrupt-Routine zuerst eine ROM-Routine anspringt, die die Register speichert und dann überprüft, ob der Interrupt durch einen BRK-Befehl ausgelöst wurde. Insgesamt dauert dies weitere 29 Taktzyklen. Hinzu kommt, dass zwischen Anforderung eines Interrupts und tatsächlichem Aufruf der Interrupt-Routine ebenfalls ein paar Taktzyklen vergehen können, wobei es vom Zustand des Prozessors zum Zeitpunkt der Anforderung abhängt, wie viele das genau sind. Dadurch entsteht auch das Flackern am linken Ende des Strichs.

Für korrektes Timing ist es also empfehlenswert, den Interrupt auf eine Rasterzeile früher zu setzen, und dann ein paar Taktzyklen zu warten. Alternativ dazu kann man auch das Kernal-ROM ausblenden und den Interrupt-Zeiger an Stelle $FFFE auf die eigene Routine umbiegen. Dann vergehen zwischen Auslösung des Interrupts und Reaktion deutlich weniger Taktzyklen.

Position des Lightpen abfragen[Bearbeiten | Quelltext bearbeiten]

Bei einem Lightpen als Eingabegerät im Zusammenspiel mit einem auf Elektronenstrahlen basierenden Bildschirmgerät (Monitor, TV-Gerät) wird die Rasterzeile in Register 20 ($D014) ausgegeben (und die Position innerhalb der Zeile in Register 19 ($D013), allerdings nur mit halber Auflösung).

Bad Lines und Sprites[Bearbeiten | Quelltext bearbeiten]

Wenn man in den beiden Programmen oben Zeile 108 durch Zeile 107 ersetzt, scheint das Timing komplett aus dem Tritt zu kommen. Das liegt daran, dass es sich bei Zeile 107 um eine sogenannte Badline handelt.

Für eine Rasterzeile stehen dem VIC nämlich genau 63 Taktzyklen zur Verfügung und demnach kann er auch maximal 63 mal in der Zeit auf den Speicher des Rechners zugreifen. Das reicht nicht ganz aus, um die Bitmap-Daten aus dem Character-ROM und zusätzlich einmal für eine Character-Zeile auf die Zeichen samt Farbinformationen zuzugreifen. Deswegen wird in jeder achten Zeile kurzerhand der Prozessor angehalten, damit der VIC zweimal pro Taktzyklus auf den Speicher zugreifen kann (normalerweise nutzt der Prozessor die zweite Hälfte eines Taktzyklus für einen Speicherzugriff). Dadurch entgehen dem Prozessor zwischen 40 und 43 Taktzyklen.

Ein ähnliches Problem verursachen Sprites: In Zeilen, in denen Sprites angezeigt werden, benötigt der VIC ebenfalls Extrazugriffe auf den Speicher, die durch Anhalten des Prozessors gewonnen werden. Pro Sprite sind dies 2 zusätzliche Zugriffe, die zum Teil allerdings schon in der vorigen Zeile liegen. Weiterhin fehlen noch ein paar weitere Taktzyklen, die durch das Ab- und Anschalten des Prozessors zu Stande kommen.

VDC[Bearbeiten | Quelltext bearbeiten]

Der VDC im C128 stellt dem Prozessor nur die Information zur Verfügung, ob gerade der obere/untere Rand oder das Anzeigefenster dargestellt wird.

Weblinks[Bearbeiten | Quelltext bearbeiten]

WP-W11.png Wikipedia: Scan line Sprache:english