5. Grafik
Bis jetzt können Sie nur Texte mit PRINT auf dem
Bildschirm ausgeben, oder aber auch Zeichen mit POKE in den
Bildschirmspeicher schreiben. Klar: PRINT ist sehr
flexibel, aber was es eben von Haus aus nicht kann, das ist, eine andere
Zeichentabelle zu benutzen, als die, die im Character-ROM steht.
Das Character-ROM beinhaltet die Muster der einzelnen Zeichen
als Bits, und diese Bits werden natürlich auch im Speicher abgelegt, damit der Grafikprozessor
(oft einfach als VIC abgekürzt) diese Bits benutzen kann, um
die Zeichen anzuzeigen. Ein Bit ist erst einmal nichts Wildes, es ist
im Endeffekt nur eine Speichereinheit, die eine einzige Ziffer aufnehmen kann.
Allerdings darf diese Ziffer nur 0 oder 1 sein, schon für die Zahl 2 benötigt
man eine neue Stelle. Man nennt diese Darstellung binäre Darstellung,
weil es eben nur die Ziffern 0 und 1 gibt.
5.1 selbst definierte Zeichen
Für die Darstellung der Zeichen werden immer 8 Bits zu
einer Zeile zusammengefasst, 8 Bits nennt man ein Byte.
In diesem Byte entspricht im Standardmodus ein 0-Bit einem Pixel mit
der Hintergrundfarbe, und ein gesetztes Bit (1-Bit) einem Pixel mit der gerade
aktiven Zeichenfarbe (wo sich die Farben befinden wissen Sie bereits: In den
Adressen 55296-56295). Da ein Zeichen 8 Bildschirmzeilen hoch ist, belegt
dieses 8 Bytes Speicher. Für 256 Zeichen (PET-Code 0-255) und die 2
Zeichentabellen, die es nun mal gibt, werden vom VIC 4096 Bytes in den Adressen
53248-57343 benutzt. Das Zeichen für den Cursor (Quadrat) hat hier den
Zeichenwert 160, beginnt also an der Adresse 53248+(160*8)=54528
und geht bis zur Adresse 54535. Für das Cursor-Quadrat wird 8-mal der Wert 255
in den Adressen 54528-54535 gespeichert. Die Zahl 255 kommt hier wie folgt
zustande:
|
Bit 7 |
Bit 6 |
Bit 5 |
Bit 4 |
Bit 3 |
Bit 2 |
Bit 1 |
Bit 0 |
|
gesetzt |
gesetzt |
gesetzt |
gesetzt |
gesetzt |
gesetzt |
gesetzt |
gesetzt |
Summe=128+64+32+16+8+4+2+1=255 (8 Zeilen mit gesetzten Bits=8*Wert 255)
Natürlich können auch andere Bitmuster auftreten, z.B. für das große A.
Wenn Sie nun eine Zeit lang programmieren, dann werden Ihnen die
Standardzeichen irgendwann nicht mehr ausreichen. Vielleicht wollen Sie nun
eigene Zeichen entwerfen. An dieser Stelle stehen Sie aber vor einem Problem,
denn die Zeichen stehen im ROM, dazu noch an Adressen, die sich z.B. mit
dem Joystickport, aber auch mit den
VIC-Registern überschneiden, und die deswegen auch ausgeblendet sind. Was
Sie nun tun müssen, ist folgendes:
· Deaktivieren der Geräte-Interrupts (die Adressen der Geräte-Register z.B.
des VIC oder der Tastatur überschneiden sich teilweise mit den Adressen des
Character-ROMs)
· Einblenden des Character-ROMs in das RAM, damit Sie darauf zugreifen können
· Kopieren des Character-ROMs in einen RAM-Bereich, den Sie bearbeiten können
· Ausblenden des Character-ROMs (damit Ihre Geräte wieder korrekt
funktionieren)
· Reaktivieren der Geräte-Interrupts (sonst können Sie BASIC nicht mehr
verwenden)
· Umbiegen des Zeigers für die Zeichentabelle in den RAM-Bereich, den Sie nun
umschreiben wollen
All dies ist nicht trivial und erfordert einiges an Spezialwissen. Das
Erste, was Sie an dieser Stelle erfahren sollten, ist, dass auch der VIC
seine Register in den Speicher einblendet, nämlich an die Adresse 53248.
Diese Konfigurationsregister enthalten Steuerinformationen in Form
von Bits und Bytes, die der VIC dazu benutzt, den Inhalt des Bildschirms
darzustellen. Was Sie nun davon haben, ist Folgendes: Sie können mit PEEK und POKE das
Verhalten des Grafikchips steuern, und eine andere Startdresse für die
Zeichentabelle auswählen. Natürlich müssen Sie zuvor das Character-ROM an die
entsprechende Stelle kopieren. Das folgende BASIC-Programm tut genau dies:
09-CHARCOPY 1
10 POKE 56334,0
20 POKE 1,51
30 FOR I=0 TO 2048: POKE
10240+I,PEEK(53248+I): NEXT I
40 POKE 1,55
50 POKE 56334,1
60 POKE 53272,26
In Zeile 10 wird auf den Tastaturcontroller über den
Baustein CIA1 zugegriffen (was eine Abkürzung für „Complex Interface Adapter“
ist und mit dem amerikanischen Geheimdienst nichts zu tun hat). Auf diese Weise
wird die Tastatur ausgeschaltet. In Zeile 20 muss danach auch
die Interrupt-Bedienung des 6510-Prozessors abgeschaltet werden, denn auch hier
überschneiden sich die Geräteadressen mit denen des Character-ROMs. Ferner wird
in Zeile 20 auch das Character-ROM eingeblendet, denn
in Adresse 1 stehen auch einige Bits zur Speichersteuerung des C64
(hier werden Sie besonders im Assembler-Kurs noch viel mehr zu erfahren).
In Zeile 30 wird dann das Character-ROM an die Adressen
10240-12287 kopiert (also nur die Tabelle mit den Grafikzeichen), und in
den Zeilen 40-50 werden die Geräte wieder in den normalen
Zustand versetzt, und die normalen VIC-Register wieder eingeblendet. In Zeile
60 wird die Kopie der Zeichentabelle aus dem ROM verwendet, die nun im
RAM steht.
Wenn Sie nun RUN eingeben, dann geschieht erst einmal eine Zeit lang
nichts. Auch bei Erscheinen von READY sehen Sie noch nichts.
Dies liegt einfach daran, dass Sie noch nichts verändert haben - Sie benutzen
ja eine exakte Kopie des ROM-Zeichensatzes! Geben Sie nun die folgende Zeile
ein:
POKE 10240,255
Nun hat sich doch etwas verändert, nämlich der Klammeraffe: Dieser wird nun
mit einem Strich überschrieben. Noch nicht sehr begeistert? Ist ja auch noch
nicht so viel, was hier passiert. Erstens ist Ihr Programm sehr langsam (Sie
müssen lange warten, bis das Character-ROM kopiert worden ist) und zweitens
haben Sie, wenn Sie Ihre Zeichentabelle an die Adresse 10240 legen, nur noch
8192 Bytes für Ihr BASIC-Programm zur Verfügung (das sind maximal 32 Blocks auf
der Diskette). Die Lösung ist hier das nächste Listing, das die
RAM-Zeichentabelle an die Adresse 14336 legt, und außerdem die langsame
BASIC-Kopierschleife als Maschinenprogramm realisiert:
10-CHARCOPY 2
10 FOR I=49152 TO 49234:
READ A:POKE I,A:NEXT I
20 SYS 49152:REM ZEICHEN
NACH ADRESSE 14336 KOPIEREN
30 FOR I=0 TO 7:READ A:POKE 14336+I,A:NEXT I: REM SMILY
40 POKE 53272,30:REM ZEICHEN AB 14336
50
PRINT"[SHIFT+CLR/HOME]@ @ @ ALWAYS KEEP
SMILING @ @ @"
60 END
10000 REM ***
ROM-KOPIERROUTINE ***
10010 DATA
173,14,220,41,254,141,14,220,165,1,41,251,133,1,169,0,133,248
10020 DATA
169,208,133,249,169,0,133,250,169,56,133,251,160,0,177,248,145
10030 DATA
250,24,165,248,105,1,133,248,165,249,105,0,133,249,24,165,250
10040 DATA
105,1,133,250,165,251,105,0,133
10050 DATA
251,165,249,201,216,208,220,165
10060 DATA
1,9,4,133,1,173,14,220,9,1,141,14,220,96
10070 REM *** SMILY ***
10080 DATA 60,66,165,129,165,153,66,60
5.1.2 Hilfsmittel für die Zeichenerstellung
Sie können nun Zeichen, die Sie z.B. für Ihre Spiele brauchen, per Hand
erstellen, und die folgende Tabelle hierfür benutzen:
|
128 |
64 |
32 |
16 |
8 |
4 |
2 |
1 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Setzen Sie nun in jedes Kästchen, in dem ein Pixel erscheinen soll, ein Kreuz
(oder was immer Sie wollen) und addieren zeilenweise jeweils die Zahlen für die
entsprechenden Bit-Werte, in dem ein Kreuz steht. Die auf diese Weise
berechneten Werte können Sie später in DATA-Zeilen übernehmen. Sie
müssen also nur die Werte für die einzelnen Bits spaltenweise addieren, und
erhalten auf diese Weise 8 Zeilen mit 8 Zahlen, die die Werte darstellen, die
Sie später in den Speicher schreiben müssen. Wenn Sie mehrere Zeichen umdefinierten
wollen, dann müssen Sie mehrere Tabellen erstellen - für jedes Zeichen eine.
Die Daten tragen Sie anschließend in DATA-Zeilen ein, und lesen diese mit der
folgenden BASIC-Zeilen aus:
10 FOR I=14336+(8*S) TO
14336+(8*E): REM S=STARTZEICHENCODE, E=ENDZEICHENCODE
20 READ A: POKE I,A
30 NEXT I
Vielleicht verwenden Sie für die Zeichentabellen auch lieber Excel, dort
können Sie die Summe von Werten in Zeilen oder Spalten in einfacher Weise
berechnen, wenn Sie statt des Kreuzes eine 1 in die Tabelle eintragen, und für
nicht gesetzte Pixel eine 0. Der Königsweg ist sicherlich die Verwendung eines
Zeicheneditors, der sich zum Beispiel auch auf dem Disk-Image BASIC-Kurs.d64 befindet.
Ich bin zugegebenermaßen sehr stolz auf diesen Editor (den ich übrigens selbst
programmiert habe), denn Sie können hier sogar (falls Ihr C64 diese korrekt
erkennt) mit der 1351-Maus zeichnen (wenn Sie einen Emulator wie VICE benutzen,
dann müssen Sie die Maus vorher über das Einstellungen-Menü
aktivieren). Aber auch mit den Cursortasten können Sie arbeiten, falls dann
keine Maus vorhanden ist, und Sie können mit dem Editor auch mehrfarbige
Zeichen erstellen. Durch Verwendung von Maschinensprache-Routinen ist der
Zeicheneditor auch sehr schnell und sehr flüssig zu bedienen.
Auf welche Weise Sie die einzelnen Zeichen jedoch erstellen: Inzwischen
sind Sie sicherlich an die Grenzen Ihres Wissens gestoßen. Wie komme ich z.B.
darauf, dass mit POKE 53272,30 die RAM-Zeichentabelle an der
Adresse 14336 ausgewählt wird?
5.1.2 Die Lage der Zeichentabelle im Speicher festlegen
Die Antwort liegt hier in der Adresse 53272, was dem VIC-Register
Nr. 24 entspricht. Dieses Steuerregister steuert neben anderen Dingen
wie dem aktuellen Grafikmodus auch die Lage der Zeichentabelle durch Bit 4-1.
Normalerweise bilden diese Bits die Zahl 2 ab (in Adresse 53272 steht
normalerweise der Wert 21), was bedeutet, dass sich die Zeichentabelle an der
Adresse 2*2048 befindet. Im Endeffekt müsste man sagen „befinden müsste“, denn
alle Adressen zwischen 4096 und 8191 werden hier auf das Character-ROM abgebildet
- man spricht hier auch von „mapping“. Deshalb wählt
der „normale“ Wert von 2 in Bit 4-1 die erste Bank des Character-ROMs aus, und
dies sind die Großbuchstaben und die Grafikzeichen. Deshalb wählt dann
auch POKE 53272,22 die Zeichentabelle in der zweiten Bank des
Character-ROMs aus, und dies ist die Groß/Kleinschrift (beachten Sie bitte,
dass Bit 0 in der Adresse 53272 stets auf 1 gesetzt bleibt). POKE
53272,16 wählt also welche Zeichentabelle an welcher Adresse aus?
Richtig: Die Adressen 0-2048 im RAM! Probieren Sie es ruhig aus und geben POKE
53272,16 ein. Wenn Sie alles richtig gemacht haben, dann erscheinen
sogar einige Zeichen, die sich dauernd ändern. Das ist aber kein Wunder, denn
einige Adressen im Bereich 0-255 werden von Kernal benutzt,
und ändern sich eben dauernd.
Nun haben Sie alle Informationen zusammen, um die Adresse der
Zeichentabelle wie folgt einzustellen (und auch zu verstehen, warum dies so
ist):
· POKE 53272,16 (identisch mit POKE 53272,17) wählt als Quelle
Adresse 0-2047 aus
· POKE 53272,18 (identisch mit POKE 53272,19) wählt als Quelle
Adresse 2048-4095 aus
· POKE 53272,20 (identisch mit POKE 53272,21) wählt als Quelle
die ROM-Bank Nr. 0 aus
· POKE 53272,22 (identisch mit POKE 53272,23) wählt als Quelle
die ROM-Bank Nr. 1 aus
· POKE 53272,24 (identisch mit POKE 53272,25) wählt als Quelle
Adresse 8192-10239 aus
· POKE 53272,26 (identisch mit POKE 53272,27) wählt als Quelle
Adresse 10240-12287 aus
· POKE 53272,28 (identisch mit POKE 53272,29) wählt als Quelle
Adresse 12288-14335 aus
· POKE 53272,30 (identisch mit POKE 53272,31) wählt als Quelle
Adresse 14336-16383 aus
·
POKE 53272,21 stellt die
Standard-Verhältnisse wieder her
5.1.3 mehrfarbige Zeichen
Im Endeffekt sehen Ihre selbst definierten Zeichen noch sehr blass aus. Sie
haben ja nur eine einzige Farbe benutzt! Wenn Sie aber die schon sehr bunten
Spiele von früher kennen, dann werden Sie nach kurzer Zeit keinen Spaß mehr an
solchen einfachen Zeichen haben. Zum Glück gibt es aber die Möglichkeit, bei
der Definition Ihrer Zeichen immer zwei Pixel zu einem einzigen (leider doppelt
so großen) Pixel zusammenzufassen, und mit dieser Zahl (im Bereich 0-3) Zeichen
mit vier Farben zu benutzen. Allerdings wird die Zahl 0 für die
Hintergrundfarbe reserviert. Bleiben noch drei Farben übrig, aber das ist ja
schon mal was! In der Tat können Farbe 1 und 2 beliebig mit POKE-Befehlen
geändert werden:
POKE 53282,[Farbwert]
setzt Farbe 1
POKE 53283,[Farbwert]
setzt Farbe 2
Das heißt, dass mit den VIC-Registern Nr. 34 und 35 der Zeichenfarbe
1 und 2 ein neuer Wert zugewiesen werden kann, der dann für sämtliche
Zeichen gültig ist. Allerdings kann Farbe 3 nicht mit
POKE 53284,[Farbwert]
geändert werden, denn Farbe 3 entspricht der Farbe, die Sie mit dem PRINT-Befehl
durch die entsprechenden Farb-Steuerzeichen einstellen müssen. Diese
Einschränkung ist nicht so schlimm, wie sie aussieht, denn mit PRINT (oder
auch einem POKE in den Farbspeicher) können Sie für jedes
Zeichen einen separaten Farbwert für Farbe Nr. 3 festlegen. Allerdings können
Sie das Multicolor-Flag nicht mit
der Speicheradresse 53272 festlegen, sondern müssen hierzu Bit Nr. 3 in
Adresse 53270 setzen:
POKE 53270,PEEK(53270)
OR 16
OR bewirkt hier (weil es als Bitmaske benutzt wird), dass der ursprüngliche Inhalt
der Adresse 53270 (VIC-Register Nr. 22) kopiert wird, außer dass
bei der Kopie zusätzlich Bit 3 gesetzt ist. Erschrecken Sie nicht, wenn Sie in
den Multicolor-Modus wechseln, und nun sämtliche Zeichen leicht verändert
dargestellt werden. Das ist ja auch klar: Die Bits in den einzelnen Spalten der
8 Zeilen werden nun anders interpretiert. Wenn Sie den Multicolor-Modus wieder
„loswerden“ möchten, dann können Sie die folgende Zeile benutzen:
POKE 53270,PEEK(53270)
AND (NOT 16)
Bei den Bitmasken gilt übrigens folgende Regel:
Bei Verknüpfung einer Zahl mit einer OR-Maske werden bei dieser Zahl
sämtliche Bits gesetzt, die auch in der Maske gesetzt (=1) sind, bei
Verknüpfung einer Zahl mit einer AND-Maske werden bei dieser Zahl sämtliche
Bits gelöscht, die auch in der Maske gelöscht (=0) sind.
Bitmasken werden ab nun noch häufiger benutzt, vor Allem bei der Steuerung
des Verhaltens der Grafikausgabe. Auch bei der Tonerzeugung mit dem SID werden
Ihnen später noch zahlreiche Bitmasken begegnen.
5.2 Sprites
Der VIC kann über das eigentliche Bild sogenannte MIBs (Moveable Image Blocks)
drüberlegen. MIBs sind 24*21 Pixel große Bildchen, die sogar transparente
Farbinformationen enthalten können (das heißt, dass Farbe 0 transparent ist).
Und nicht nur das: Von wo im Speicher diese kleinen Bildchen geholt werden,
kann hier durch spezielle Zeiger in den Adressen 2040-2047 festgelegt
werden. Da die MIBs nicht direkt im Bildschirmspeicher stehen, sondern irgendwo
wie Kobolde im RAM „herumspuken“, hat sich für den Begriff MIB später
das Wort Sprite eingebürgert.
5.2.1 Erstellen von Sprites
Ein Sprite wird fast
wie ein selbst definiertes Zeichen erstellt, das heißt mittels der Definition
von Bytes, die die einzelnen Pixel-Bits enthalten. Allerdings müssen für
Sprites 3 Bytes für eine Zeile und 21 Zeilen (das sind 63
Bytes pro Sprite) benutzt werden. Allerdings werden die Farben anders
definiert, und Farbe 2 ist die Hauptfarbe, die auch beliebig geändert werden
kann. Farbe 1 und 3 sind die globalen Farben, die in den VIC-Registern
Nr. 37 und 38 stehen. Für Sprites empfehle ich, einen guten Sprite-Editor
zu verwenden, mit dem Sie auch mehrere Animations-Frames in einfacher Weise
erstellen können. Ein sehr guter Sprite-Editor war damals in den
80-ern Super Spriter- ich weiß aber nicht,
ob Sie diesen noch im Internet als D64-Datei finden können. Sie können
natürlich Sprites auch von Hand oder mit Excel erstellen, aber dies ist sehr
viel Arbeit. Einen eigenen Sprite-Editor habe ich natürlich auch in das
Image BASIC-Kurs.d64 eingebunden, und auf diesen Editor bin
ich mal wieder sehr stolz, weil man auch hier mit der Maus zeichnen kann. Aber
nicht nur das. Sie können die einzelnen Frames horizontal und vertikal drehen,
sowie auch kopieren und löschen. Ferner können Sie die Sprite-Frames als DATA-Zeilen
in eine BASIC-Datei schreiben lassen.
Nachdem Sie einen Spriteblock definiert
und mittels READ und DATA in den Speicher
geschrieben haben (z.B. an die Adressen 12288-13248), sehen Sie leider noch
nichts, denn Sie müssen ein Sprite einschalten
und die Koordinaten festlegen. Ferner muss natürlich bekannt sein, von welcher
Adresse der Sprinte-Block geholt werden soll. Und schon wieder müssen Sie sich
Speicheradressen merken! Um die Sache etwas zu vereinfachen, kann man sich aber
einfach nur die Registernummern des VIC merken, die die
Sprites steuern, und die Basisadresse einfach mit
V=53248
festlegen. So ist es viel einfacher, ein bestimmtes
Sprite einzuschalten. Hierzu muss nur das entsprechende Bit
im VIC-Register Nr. 21 auf 1 gesetzt werden. Bit 0
bestimmt hier die Sichtbarkeit von Sprite Nr. 0 (gesetzt=sichtbar), und Bit
Nr. 7 die Sichtbarkeit von Sprite Nr. 7. Das heißt, dass z.B.
POKE V+21,1
Sprite Nr. 0 einschaltet. Die Hauptfarbe wird nun mit
POKE V+39,[Farbe] festgelegt,
also z.B.
mit POKE V+39,1
auf Weiß gesetzt. Die Adresse, von der der Bildblock für ein Sprite geholt
wird, wird in den Adressen 2040 für Sprite Nr. 0 bis 2047 für
Sprite Nr. 7 festgelegt. Allerdings haben die Zeiger für die Bildblöcke
nur 8 Bits zur Verfügung, deshalb wird der Wert, der in der Adresse 2040 steht
(für Sprite Nr. 0) noch mal mit 64 multipliziert, um die tatsächliche Adresse
für den Zeiger zu erhalten. Wenn Sie also Ihre Sprite-Daten an Adresse
12288-12348 ablegen, dann adressieren Sie diese mit
POKE 2040,192 (192*64=12288)
Die Koordinaten für ein Sprite legen Sie mit den VIC-Registern
Nr. 0-15 fest, und zwar in Paaren zu je zwei Bytes. Das heißt, die
X-Koordinate für Sprite Nr. 0 steht in VIC-Register 0 und die Y-Koordinate in
VIC-Register 1. Sichtbar wird also z.B. Sprite Nr. 0 erst durch
POKE V,100: POKE V+1,100
Nun sehen Sie endlich ihr Sprite, aber so
dolle ist die Sache dann auch wieder nicht, denn Sie bekommen Ihr Sprite ums
Verrecken nicht an den rechten Bildschirmrand, egal welche Werte Sie in die
Adresse 53248 schreiben. Dies ist kein Wunder, denn Ihr Bildschirm fasst 368
Pixel, von denen einige durch den Bildschirmrahmen verdeckt werden (nämlich
links und rechts 24 und oben und unten jeweils 50). Bleiben immer noch 320
Pixel für den sichtbaren Teil in X-Richtung, und auch diese Zahl kann natürlich
nicht durch ein einzelnes Byte dargestellt werden. Es hilft alles nichts, Sie
benötigen für jede X-Koordinate jedes Sprites ein 9. Bit, und diese Bits stehen
in VIC-Register Nr. 16. Und ja: Für jedes Sprite gibt es hier
wieder (wie auch schon in Register Nr. 21) ein 9. Bit. Aber wie setzen Sie nun
Ihr Sprite Nr. 0 z.B. an die Position 280? Auf folgende Weise:
X=280: POKE V,X AND
255: POKE V+16,INT(X/256)
Der Trick hier: Durch die AND-Maske werden sämtliche Bits von X
ab Bit Nr. 9 zu 0 gesetzt.
5.2.2 Multicolor-Sprites
Multicolor-Sprites funktionieren genauso, wie
Multicolor-Zeichen: Es werden in einer Spalte stets 2 Pixel zu einem (leider
auch hier doppelt so großen) Pixel zusammengefasst. Jeder gute Sprite-Editor
(auch mein eigener) lässt sich zum Glück immer auf Multicolor-Sprites umschalten.
Wenn Sie dies per Hand machen wollen, so müssen Sie in VIC-Register 28 das
entsprechende Bit auf 1 setzen. Beachten Sie, dass bei Sprites Farbe
Nr. 2 die Hauptfarbe ist (nicht Farbe 3!) und 0 die transparente
Hintergrundfarbe darstellt. Im Gegensatz zu Zeichen kann bei Sprites auch festgestellt werden, ob ein bestimmtes
Sprite mit den Hintergrundzeichen oder einem anderen Sprite kollidiert ist (zu
der Kollision gibt es am Ende von Kapitel 5 noch ein Beispiel in Form eines
kleinen Autorennens). Ferner kann festgelegt werden, ob ein
Sprite im Hintergrund erscheinen soll (in VIC-Register Nr. 27
durch ein gesetztes Bit für jedes Sprite). Dies können Sie mit Zeichen
nicht machen. Allerdings können Sie eine Sache nicht ändern, nämlich die
Anzeigepriorität: Sprite 7 wird stets zuerst gezeichnet, und Sprite 7 zuletzt,
sodass Sprite 0 stets vor allen anderen Sprites und
Sprite 7 hinter allen anderen Sprites platziert wird. Wenn Sie es nun
besonders „dolle treiben“ wollen, dann können Sie Ihre
Sprites noch in X-Richtung (Bits in VIC-Register Nr.
29) und Y-Richtung (VIC-Register Nr. 23) vergrößern,
z.B. für wirklich riesige, feindliche Raumschiffe. Ihre
Sprites erscheinen dann doppelt so groß. Allerdings wirken sogenannte expandierte Sprites recht pixelig.
5.3 Bitmap-Grafiken
Bitmaps werden nicht oft benutzt, außer in Zeichen- oder
Mathematikprogrammen oder für Vorspänne bei Spielen. Der Grund: Die Verwaltung
ist aufwendig, überfordert beim Scrolling den Prozessor, und die Grafiken
benötigen zumindest unkomprimiert sehr viel Speicher (ein
Bitmap benötigt auf der Diskette ganze 33 Blocks). Trotzdem können
Bitmaps eine Option sein, z.B. beim Zeichnen von mathematischen Funktionen.
Im Bitmap-Modus des VIC werden standardmäßig sämtliche
Bytes in den Adressen 8192-16383 als Grafik
dargestellt, das heißt als Pixel. Es gibt also hier keine Zeichentabelle,
sondern die Bytes werden einzeln ausgewertet. Leider geschieht dies nicht Zeile
für Zeile (z.B. indem einfach 40 Bytes mit 320 Pixeln in der ersten Reihe, und
weitere 40 Bytes in der nächsten Reihe angezeigt werden, usw.). Die Pixel
befinden sich vielmehr in zeichenähnlichen, 8 Byte großen Blöcken. Das bedeutet
nun folgendes: Die ersten 320 Bytes beschreiben die Pixel in der
Bildschirmzeile 0-7, die nächsten 320 Bytes die Pixel in der Bildschirmzeile
8-15, und die einzelnen Pixel entsprechen bestimmten gesetzten Bits in
bestimmten Adressen in 8 Zeilen hohen Blöcken (das bedeutet im Endeffekt, dass
hier einfach die Character-Engine für den Bitmap-Modus „missbraucht“ wird).
Erschwerend kommt noch hinzu, dass es bei Bitmaps einen Multicolor-Modus gibt,
was die Verwaltung noch weiter erschwert. Der C64 besitzt also keinen linearen Framebuffer, also einen Speicherbereich, in den Sie die
Pixel Byte für Byte hintereinander ablegen können. Wenn Sie z.B. die
Zeichendaten für den Klammeraffen und das A in die Adressen 8192-8207
schreiben, so stehen die Bitmap-Blöcke auch direkt nebeneinander, so, als
hätten Sie POKE 1024,0: POKE 1025,1 eingegeben. Sie können also einzelne
Pixel nur durch einen Algorithmus setzen. Dieser Algorithmus funktioniert wie
folgt (einfarbige Bitmaps):
· Festlegen der Koordinaten (X,Y) des nächsten
Pixels
· Die Adresse AD der Bildschirmzeile, in der das Pixel angezeigt
wird, ist (Y/8) ohne Komma-Anteil, multipliziert mit 320. Zu diesem
Wert wird dann noch der Rest addiert (Y-Offset), der sich ergibt, wenn man
von Y (Y/8) ohne Komma-Anteil subtrahiert.
· Der Wert, mit dem man den Inhalt
von AD durch OR verknüpfen muss, muss identisch sein mit
der Bitnummer, die man erhält, wenn man von X (X/8) ohne
Komma-Anteil subtrahiert (X-Offset).
Als BASIC-Listing sieht das Setzen eines Pixels nun so aus:
11-SETPIXEL
10 X=100:Y=100:BA=8192
20 GOSUB 10000
30 END
10000 REM *** SETPIXEL ***
10010 Z=INT(Y/8):REM ZUM
ZEICHENBILDSCHIRM KOMPATIBLE ZEILENNUMMER
10020 C=INT(X/8):REM ZUM
ZEICHENBLIDSCHIRM KOMPATIBLE ZEICHENPOSITION
10030 YO=Y AND 7:REM Y-OFFSET
10040 XO=7-(X AND 7):REM X-OFFSET
10050 AD=BA+Z*320+C*8+YO: REM SPEICHERADRESSE FUER
PIXEL-BYTE
10060 POKE AD,PEEK(AD) OR (2^XO): REM PIXEL-BIT SETZEN (MIT OR)
10070 RETURN
Nun haben Sie noch ein Problem: Beim Einschalten des C64 wird der
Bildschirmspeicher für Bitmap-Bilder nicht initialisiert. Deshalb befinden sich
zufällig aussehende Muster in den Adressen 8192-16383, in denen Bitmaps
normalerweise abgelegt werden. Deswegen muss, falls Sie vorhaben, Bitmaps zu
erstellen, am Anfang Ihres Programmes immer folgende Schleife ausgeführt
werden:
FOR I=8192 TO 16383:POKE I,0:NEXT I
5.3.1 Eine Sinuskurve zeichnen
Das Löschen des Bitmap-Speichers benötigt in etwa eine Minute, und das
Setzen eines Pixels durch ein Unterprogramm benötigt in BASIC etwa 0,05
Sekunden. Das heißt, dass Sie mit BASIC nur 20 Pixel in der Sekunde setzen
können. Obwohl also die ganze Sache ziemlich langsam ist, möchte ich dennoch
einige Listings anführen, die ein paar mathematische Funktionen und
geometrische Körper auf den Bildschirm zeichnen. Das nächste Listing zeichnet
eine Sinuskurve.
12-SINUS
10
PRINT"[SHIFT+CLR/HOME]BITTE WARTEN..."
20 FOR I=8192 TO 16383:POKE I,0:NEXT I
30 PRINT"[SHIFT+CLR/HOME]":REM BILDSCHIRM
WIEDER LOESCHEN
40 POKE 53265,PEEK(53265)
OR 32: REM BITMAP-FLAG AUF 1 SETZEN
50 POKE 53272,PEEK(53272)
OR 8: REM BIPMAP-BANK WIRD BEI 8192 EINGEBLENDET
60 FOR I=1024 TO 2023:POKE I,1:NEXT I
70 BA=8192
80 FOR X=0 TO 319 STEP
0.5
90 Y=INT(90+80*SIN(X/10))
100 GOSUB 10000
110 NEXT X
120 END
10000 REM *** SETPIXEL ***
10010 Z=INT(Y/8):REM ZUM
ZEICHENBILDSCHIRM KOMPATIBLE ZEILENNUMMER
10020 C=INT(X/8):REM ZUM
ZEICHENBLIDSCHIRM KOMPATIBLE ZEICHENPOSITION
10030 YO=Y AND 7:REM Y-OFFSET
10040 XO=7-(X AND 7):REM X-OFFSET
10050 AD=BA+Z*320+C*8+YO: REM SPEICHERADRESSE FUER
PIXEL-BYTE
10060 POKE AD,PEEK(AD) OR (2^XO): REM PIXEL-BIT SETZEN (MIT OR)
10070 RETURN
Am Anfang muss zunächst der Bildschirmspeicher gelöscht werden, und dies dauert
etwas - deshalb die Meldung „BITTE WARTEN“. Um den C64 anschließend in den
Bitmap-Modus zu schalten, muss in Zeile 40 das Bitmap-Flag durch Setzen von Bit 5 in
Adresse 53265 aktiviert werden. Allerdings verbleibt der
Bildschirmspeicher durch diese Maßnahme an der Adresse 0 (dies ist die
Standardeinstellung im Bitmap-Modus), deshalb muss anschließend in Zeile
50 noch die Speicherbank für den Bildschirm an die Adresse 8192 verschoben
werden. Dies erreichen Sie durch Setzen von Bit 3 in
Adresse 53272 auf 1. Nun haben Sie einen Bitmap-Modus
gewählt, der kein leeres Bild enthält, deshalb muss vorher in den Zeilen
10-30 eine Schleife ausgeführt werden, die Null-Bytes in die Adressen
8192-16383 schreibt. Da diese Prozedur etwa eine Minute dauert, wird eine
entsprechende Meldung angezeigt, dass der Benutzer des Programms warten soll.
Leider hat der Bitmap-Modus noch eine Eigenschaft, die man beachten muss, wenn
man wirklich eine leere Zeichenfläche erzeugen möchte: Der Zeichenspeicher an
den Adressen 1024-2023 wird zusätzlich benutzt, um
die Hintergrund- und die Pixelfarbe der einzelnen Bitmap-Blöcke zu
bestimmen. Hierbei bestimmt das hohe Nibble (Bit 7-4) die Pixelfarbe,
und das niedrige Nibble (Bit 3-0) die Hintergrundfarbe. Um also
schwarze Pixel auf weißem Grund anzuzeigen, muss in Zeile 60 noch
der Wert 1 in die Adressen 1024-2023 geschrieben werden.
Die Basisadresse BA des Bitmap-Bildschirms wird anschließend in Zeile
70 auf 8192 festgelegt.
Das Zeichnen der Sinuskurve ist nun nicht mehr so schwer und kann in
den Zeilen 80-110 durch eine einfache FOR-Schleife
erledigt werden, die zu den x-Werten zwischen 0 und 319 die Funktion
f(x)=sin(x) berechnet. Der Abstand zwischen den Werten auf der x-Achse ist 0,5.
Allerdings könnten Sie die Originalwerte der Funktion f(x)=sin(x) nicht sehr
gut auf dem Bildschirm sehen, denn die y-Werte für f(x)=sin(x) liegen im
Intervall [-1,1]. Deshalb wird in Zeile 90 die Sinusfunktion
so skaliert, dass man auch etwas sieht. Hierzu wird die Funktion f(x) um 90
Pixel nach unten verschoben, und anschließend um den Faktor 10 gestreckt. Erst
nach der Skalierung wird dann das Unterprogramm zum Zeichen eines Pixels
mit GOSUB 10000 aufgerufen.
Das Zeichnen eines Pixels ist wie gesagt etwas tricky,
und es gibt hier unzählige Varianten im Internet. Ich habe von diesen Varianten
einfach die schnellste verwendet, die es in BASIC gibt. Diese schnelle Variante
arbeitet wie folgt: Zunächst wird in Zeile 10010 erst einmal Y
durch 8 geteilt, und der Komma-Anteil entfernt. Dadurch erhalte ich eine
Zeilennummer Z, die auch kompatibel zu den Zeilen des normalen
Zeichenbildschirm ist (0<=Z<=24). Die Spalte C, die
kompatibel zum Zeichenbildschirm ist, erhalte ich in Zeile 10020 auf
dieselbe Weise (C ist hier die Abkürzung von „character“).
Leider bilden X und Y ein Vielfaches von 8 Pixeln
ab, deswegen ist dann ein Pixel an der Position (45,45) nicht mehr
adressierbar. Um dieses Problem zu lösen, enthält XO und YO einen Offset,
der jeweils den Modulo von X/8 und Y/8
enthält (der Modulo ist der Rest einer Division). Leider kann BASIC
(und auch der 6510) den Modulo nicht direkt
berechnen, und Sie müssen einen Trick anwenden: Sie verknüpfen X und Y in Zeile
10030 und 10040 mit einer AND-Maske, die den
Wert 7 enthält. Auf diese Weise erhalten Sie auf eine sehr effiziente Weise
eine Division Modulo 8.
Die Bestimmung der Adresse, in die Sie das Pixel eintragen müssen, ist nun
nicht mehr weiter schwierig: Für eine Zeile von 8 Pixeln Höhe müssen Sie
40*8=320 Bytes reservieren, für einen Bitmap-Block 8 Bytes. Da Sie auch YO noch
in Ihre Berechnungen integrieren müssen, erhalten Sie anschließend die Formel
in Zeile 10050. Nun muss zum Schluss nur noch das Pixel gesetzt
werden. Die geschieht durch eine OR-Verknüpfung mit dem Inhalt der
Adresse AD=BA+Z*320+C*8+YO: XO muss durch den
Dezimalwert des Bits Nr. XO ersetzt werden. Wenn XO also
1 ist, wird XO zu 1, wenn XO 7 ist,
wird XO zu 128. Dies leistet dann die Formel XO=2^XO,
die in Zeile 10060 noch zusätzlich mit dem aktuellen Inhalt
der Speicheradresse AD verknüpft wird. Zugegeben: Das Setzen
von Pixeln ist in BASIC immer noch sehr langsam, selbst, wenn Sie die schnellst
mögliche Variante verwenden. Vielleicht denken Sie an dieser Stelle wieder
darüber nach, die BASIC-Variante später durch eine Maschinensprache-Routine zu
ersetzen. In der Tat beschleunigt diese Variante das Setzen von Pixeln
erheblich, aber im Endeffekt können Sie hochwertige Grafiken dann doch nur
dadurch erstellen, dass Sie ein professionelles Malprogramm benutzen. Leider
ist auch dies nicht trivial, denn es gibt auf dem C64 noch keinen Standard für
Bitmap-Grafiken. Wenn Sie also Bilder von der Diskette nachladen wollen, dann
müssen Sie das entsprechende Dateiformat sehr gut kennen.
5.3.2 Einen Kreis zeichnen
Kommen wir nun zum nächsten Beispiel-Listing. In diesem Beispiel wird ein
Kreis gezeichnet, der den Mittelpunkt (60,60) und einen Radius von 50 Pixeln
hat.
13-KREIS
10
PRINT"[SHIFT+CLR/HOME]BITTE WARTEN..."
20 FOR I=8192 TO 16383:POKE I,0:NEXT I
30 PRINT"[SHIFT+CLR/HOME]":REM BILDSCHIRM
WIEDER LOESCHEN
40 POKE 53265,PEEK(53265)
OR 32:REM BITMAP-FLAG AUF 1 SETZEN
50 POKE 53272,PEEK(53272)
OR 8:REM BITMAP-BANK WIRD BEI 8192 EINGEBLENDET
60 FOR I=1024 TO 2023:POKE I,1:NEXT I
70 BA=8192
80 FOR W=-[PI] TO [PI]
STEP 0.02
90 X=INT(60+50*SIN(W)):
Y=INT(60+50*COS(W))
100 GOSUB 10000
110 NEXT W
120 GET A$:IF A$="" THEN 120
130 POKE 53272,21:POKE 53265,27: PRINT"[SHIFT+CLR/HOME] DAS
PROGRAMM WURDE BEENDET"
140 END
10000 REM *** SETPIXEL ***
10010 Z=INT(Y/8):REM ZUM
ZEICHENBILDSCHIRM KOMPATIBLE ZEILENNUMMER
10020 C=INT(X/8):REM ZUM
ZEICHENBLIDSCHIRM KOMPATIBLE ZEICHENPOSITION
10030 YO=Y AND 7:REM Y-OFFSET
10040 XO=7-(X AND 7):REM X-OFFSET (BIT)
10050 AD=BA+Z*320+C*8+YO:REM SPEICHERADRESSE
FUER PIXEL-BYTE
10060 POKE AD,PEEK(AD) OR (2^XO): REM PIXEL-BIT SETZEN (MIT OR)
10070 RETURN
Das Unterprogramm für das Zeichnen von Pixeln (Zeile 10000-10070)
ist dasselbe Unterprogramm, wie im letzten Beispiel. Auch die Routine zum
Initialisieren des Bitmap-Modus (Zeile 10-70) hat sich nicht verändert.
Allerdings ist der Algorithmus zum Zeichnen eines Kreises (Zeile 80-110)
nicht ganz trivial, weil Sie einen zweidimensionalen geometrischen Körper
erstellen müssen. In diesem Fall ist dies ein Kreis, für den gilt:
x2+y2=r2.
Nach dem Satz des Pythagoras ist der x-Anteil der Koordinate für einen
Punkt, der auf dem Kreis mit dem Radius r liegt, die Wurzel aus der Differenz
von r2 und y2, und der y-Anteil der Koordinate für
einen Punkt, der auf diesem Kreis liegt, ist die Wurzel aus der Differenz von r2 und
x2 (r ist also hier die Hypotenuse in einem rechtwinkligen
Dreieck). Leider führt diese Erkenntnis nicht direkt zum Ziel, denn es soll ja
die Menge aller Punkte gezeichnet werden, die vom Kreismittelpunkt den Abstand
r haben. Nach Euler gilt nun:
Sei M der Mittelpunkt eines Kreises, und die Strecke MR führe zu dem Punkt
R, der die Koordinaten (x,y)
besitzt. Der Abstand zwischen M und R sei der Kreisradius r, und der Winkel in
R sei w. Dann gilt:
x=r*sin(w)
y=r*cos(w)
In Zeile 90 wird nun genau diese Formel angewendet, um für
einen variablen Winkel (angegeben im Bogenmaß) und einen Kreis mit einem Radius
von 50 Pixeln die Koordinaten aller Punkte zu berechnen, die auf dem Kreis
liegen. Der Mittelpunkt des Kreises liegt bei M=(60,60).
Das Intervall, in dem sich der Winkel ändert, liegt zwischen -PI und PI
(PI ist hier die Zahl 3,14), und ändert sich in Schritten von 0,02.
Um Zeile 80 korrekt auszuführen, gibt es mehrere
Möglichkeiten. Bei BASIC-Versionen über 3.0 können einige Konstanten, wie z.B.
die Kreiszahl PI, in eckigen Klammern angegeben werden. BASIC setzt dann
an die Stelle des Ausdrucks [PI] die Kreiszahl auf 8 Stellen genau ein.
Bei den BASIC-Versionen 2.0 und 3.0 gibt es allerdings das Zeichen π auf
der Tastatur, und das Zeichen π erscheint dann auch korrekt im
Programm-Listing. Beim Standard-C64 ist dies dann auch so, allerdings nicht bei
dem beliebten C64-Nachfolger Plus 4. Dort müssen Sie die Zahl π nicht oft
benutzen, weil der Plus 4 Funktionen besitzt, um Kreise direkt zu zeichnen.
Hinweis: Bildschirmkoordinaten vs. kartesische Koordinaten
In dem in der Mathematik oft benutzten kartesischen
Koordinatensystem befindet sich der Nullpunkt oft in der Mitte des
Zeichenblattes, in dem Koordinatensystem, das der C64 für die Pixel benutzt,
liegt allerdings der Punkt (0,0) in der linken oberen Ecke, und
die Y-Koordinaten erhöhen sich von oben nach unten. Sie müssen also im
Bedarfsfall das kartesische Koordinatensystem in das Koordinatensystem des
C64-Bildschirms durch folgende Formel umrechnen:
Sei M der Mittelpunkt des Koordinatensystems, der auf die
Bildschirmkoordinaten (X0,Y0) abgebildet wird, und
(X,Y) ein Punkt im kartesischen Koordinatensystem. Dann gilt:
X‘=X+X0 und
Y‘=Y-Y0,
wobei (X‘,Y‘) der zu dem Punkt (X,Y) im
kartesischen Koordinatensystem gehörige Bildschirmpunkt ist.
5.3.3 Multicolor-Bitmaps
Wie bei den Zeichen auch, können Sie im Bitmap-Modus das Multicolor-Bit in
Adresse 53270 auf 1 setzen. Dies führt dann im Bitmap-Modus
dazu, dass immer zwei Pixel zu einem Pixelpaar zusammengefasst werden, mit dem
Sie eine Farbnummer zwischen 0 und 3 auswählen können. Auch hier ist die
Farbnummer für die Hintergrundfarbe 0, wogegen die Farbnummern 1 und 2 die
Farben benutzen, die in den Adressen 53282 und 53283 angegeben
wurden. Die Hauptfarbe (Farbnummer 3) wird dagegen so ermittelt, wie im
Single-Color-Modus, nämlich durch das entsprechende Byte in den Adressen
1024-2023, das zu dem gerade verwendeten Bitmap-Block passt. Dies ist
vielleicht auf den ersten Blick etwas verwirrend, aber nur so kann der C64
genug Speicher einsparen, um auch ganze Bilder vollständig anzeigen zu können.
Der Multicolor-Bitmap-Modus wird auch oft als hires 3 (hi resolution 3 color mode) bezeichnet, was allerdings etwas verwirrend ist. Denn
im Endeffekt reduziert sich bei hires 3 die
horizontale Auflösung auf die Hälfte, also auf 160x200 Pixel. Ich persönlich
würde dann sogar von „lores 3“ sprechen, vor
allem deshalb, weil mit Tricks quasi wirklich drei Farben mit einer Auflösung
von 320x200 Pixeln angezeigt werden können (allerdings eventuell mit Artefakten
im Bild). Im Internet gehen die Begriffe „hires 3“
für den Multicolor-Bitmap-Modus und den hochauflösenden „tricky hires 3 mode“ mit der
Inkaufnahme von Artefakten durcheinander, was die Sache dann noch schwieriger
macht.
5.3.3.1 Zeichnen einer Ellipse im Multicolor-Modus
Im nächsten Beispiel wird nun eine Ellipse mit drei verschiedenen Farben
gezeichnet, die je nach Wert des Winkels W ausgewählt werden (siehe
nächste Abbildung). Um den Multicolor-Modus zu nutzen, wird nun die Routine zum
Setzen eines bestimmten Bits in einer Bildschirmspeicheradresse zweimal
hintereinander gehängt, einmal wird jedoch ein Bit gesetzt, und einmal wird das
entsprechende Bit gelöscht. Die Zeichenroutine zum Setzen eines
Multicolor-Pixels ruft dann je nach übergebener Farbe die Routinen zum Setzen
und Löschen von Bits entsprechend auf.
14-ELLIPSE
10
PRINT"[SHIFT+CLR/HOME]BITTE WARTEN..."
20 FOR I=8192 TO 16383:POKE I,0:NEXT I
30 PRINT"[SHIFT+CLR/HOME]":REM BILDSCLIRM
WIEDER LOESCHEN
40 POKE 53265,PEEK(53265) OR 32: POKE 53270,PEEK(53270) OR 16
50 POKE 53272,PEEK(53272)
OR 8:REM BITMAP-BANK WIRD BEI 8192 EINGEBLENDET
60 FOR I=0 TO 1000:POKE 1024+I,71: POKE 55296+I,0:NEXT I
70 BA=8192
80 FOR W=-[PI] TO [PI]
STEP 0.02
90 CL=3
100 IF (W>-[PI]) AND
(W<0) THEN CL=1
110 IF (W>0) AND
(W<([PI]/2)) THEN CL=2
120 X=INT(60+50*SIN(W)):Y=INT(60+50*COS(W))
130 GOSUB 10000
140 NEXT W
150 GET A$:IF A$="" THEN 150
160 POKE 53272,21:POKE 53265,27: POKE 53270,200
170
PRINT"[SHIFT+CLR/HOME]DAS PROGRAMM WURDE
BEENDET"
180 END
10000 REM *** SET PIXEL (CL=FARBE) ***
10010 T=X:X=(2*X)+1
10020 IF (CL AND 1)=1 THEN GOSUB 20000:GOTO 10030
10025 GOSUB 30000
10030 X=X-1:CL=CL/2:IF (CL AND 1)=1 THEN GOSUB 20000:GOTO 10040
10035 GOSUB 30000
10040 X=T
10050 RETURN
20000 REM *** SETBIT ***
20010 Z=INT(Y/8):REM ZUM
ZEICHENPEEKILST
R$SCHIRM KOMPATIBLE RETURNEILENNUMMER
20020 C=INT(X/8):REM ZUM
ZEICHENBLIDSCHI
RM KOMPATIBLE
ZEICHENPOSITION
20030 YO=Y AND 7:REM Y-OFFSET
20040 XO=7-(X AND 7):REM X-OFFSET (BIT)
20050 AD=BA+Z*320+C*8+YO:REM SPEICHERADRESSE
FUER PIXEL-BYTE
20060 POKE AD,PEEK(AD) OR (2^XO):REM PIXEL-BIT SETZEN (MIT OR)
20070 RETURN
30000 REM *** CLEARBIT ***
30010 Z=INT(Y/8):REM ZUM
ZEICHENBILDSCHIRM KOMPATIBLE RETURNEILENNUMMER
30020 C=INT(X/8):REM ZUM
ZEICHENBLIDSCHIRM KOMPATIBLE ZEICHENPOSITION
30030 YO=Y AND 7:REM Y-OFFSET
30040 XO=7-(X AND 7):REM X-OFFSET (BIT)
30050 AD=BA+Z*320+C*8+YO:REM SPEICHERADRESSE
FUER PIXEL-BYTE
30060 POKE AD,PEEK(AD) AND (NOT(2^XO)):REM PIXEL-BIT LOESCHEN (MIT AND)
30070 RETURN
Die Ellipse selbst wird in den Zeilen 80-130 gezeichnet.
Der Winkel W wählt hierbei die Farbe CL aus,
und übergibt diese Farbe in Zeile 130 an das PSet-Unterprogramm zum Setzen eines Pixels. Die PSet-Routine beginnt nach wie vor in Zeile 10000,
jedoch entspricht hier eine Einheit auf der x-Achse zwei Subpixeln auf dem
Bildschirm, die zusammen eine einzige Farbe darstellen. Beim Setzen eines
Farbpixels wird immer das rechte Subpixel (also
das mit dem niedrigsten Bit-Wert) zuerst gezeichnet, und dann das linke. Wenn
ein Subpixel einem 0-Bit entspricht, dann
wird das entsprechende Pixel auf dem Bildschirm gelöscht (GOSUB 20000),
wenn ein Subpixel einem 1-Bit entspricht,
dann wird das entsprechende Pixel auf dem
Bildschirm gesetzt (GOSUB 30000). Dieses Vorgehen ist inzwischen so
langsam, dass Sie hier gewissermaßen an die Grenze des mit BASIC Machbaren
stoßen.
5.4 Verschiedene Schreibweisen für Bitfolgen
Sie wissen ja: Im sogenannten binären Zahlensystem, das
Computer (besser: die in ihnen enthaltenen Prozessoren) intern benutzen, gibt
es nur die Ziffern 0 und 1, und aus diesen Ziffern werden in Form von relativ
langen Symbolketten Zahlen aufgebaut. Natürlich können Sie dann die Zahlen auch
so darstellen, z.B. als
1000001
für die Zahl 65. Die Zahl 65 im Dezimalsystem kommt hier - wie schon bei den Sprites und Zeichen - dadurch zustande, dass
Sie die Stellenwerte der einzelnen Bits addieren:
1*26+0*25+0*24+0*23+0*22+0*21+1*20=64+0+0+0+0+0+1=65
Der Zahl 65 sieht man aber leider nicht direkt an, wie die entsprechenden
Bits verteilt sind. Hinzu kommt noch, dass für den 6510-Prozessor diese
Bitfolge in der Tat eine 8-Bit-Zahl ist, für den VIC dagegen sind dies einfach
nur Pixel. Eine Bit-Folge muss also stets interpretiert werden, denn
sonst hat sie keinen Sinn. Deshalb haben sich auch verschiedene Schreibweisen
eingebürgert. Die Schreibweise aus dem Alltag ist die Dezimalschreibweise,
die man auch durch eine kleine 10 am Ende der Zahl angibt (man schreibt also 6510).
Die entsprechende Binärschreibweise ist 10000012.
Allerdings wird diese Schreibweise schnell unübersichtlich, deshalb verwendet
man oft Viererblöcke, die Nibbles, die nur halbe Bytes enthalten.
Für die Zahl 65 sieht dies dann so aus:
6510=0100 00012
Wenn man nun hergeht, und die Werte in den
einzelnen Nibbles notiert, dann erhält man die beim C64 sehr oft
verwendete Hexadezimalschreibweise:
6510=0100 00012=4016
Für 4016 wird beim C64 (vor Allem in Assemblerprogrammen) auch
die Schreibweise $40 benutzt, und für Dezimalzahlen die Schreibweise #65. Dies
liegt einfach daran, dass mit dem Standard-Zeichensatz keine Indizes
dargestellt werden können. Aber halt: Was macht man, wenn in einem Nibble die
durchaus gültige Zahlenfolge 10102 für den Wert 10 steht? Ganz
einfach: Den Zahlen 10,11,12,13,14 und 15 werden zusätzlich die Buchstaben A-F
zugeordnet. So ist dann auch der höchste Hexadezimalwert für die Zahl #255 der
Wert $FF.
5.5 Scrolling
Vielleicht haben Sie sich schon gefragt, wie Spiele es schaffen, den
Bildschirm in alle Richtungen zu bewegen, statt nur nach oben. Sie haben dann
vielleicht auch schon folgende BASIC-Zeile ausprobiert:
PRINT “[CLR/HOME][25*CURSOR UNTEN]”
Auf diese Weise scrollt der Bildschirm aber nach oben, während viele
einfache Autorennen zeichenweise nach unten scrollen. Vielleicht haben sie nun
die folgende Schleife ersonnen, um das Problem zu lösen:
FOR I=1983 TO 1024 STEP
-1:POKE I,PEEK(I-40):NEXT I
Probieren Sie es ruhig aus, und Sie werden sehen, dass der Bildschirm
tatsächlich nach unten wandert, und die obere Zeile durch Klammeraffen ersetzt
wird. Aber die BASIC-Scrolling-Routine ist trotzdem noch viel zu langsam.
Stellen Sie sich nun vor, die BASIC-Routine wird durch ein
Maschinensprache-Pendant ersetzt, das Sie mit SYS 49152 aufrufen können.
In diesem Fall erhalten Sie dann das folgende Listing:
15-DOWNSCROLL
10 FOR I=49152 TO 49198:READ A:POKE I,A:NEXT I
20 SYS 49152:REM SCROLLING TESTEN
30 END
10000 REM ***
SCROLLING-ROUTINE (UNTEN) ***
10010 DATA
169,191,133,250,169,7,133,251,160,0,177,250,160,40,145,250
10020 DATA
56,165,250,233,1,133,250,165,251,233,0,133,251,24,165,251,201
10030 DATA
3,208,228,169,32,162,39,157,0,4,202,16,250,96
Auf diese Weise können Sie nun Maschinenprogramme für alle vier Richtungen
erstellen, was viele Spiele auch tun. Im Endeffekt erhalten Sie auf diese Weise
einen BASIC-Maschinensprache-Hybriden, der genau dort Maschinensprache-Routinen
benutzt, wo BASIC zu langsam ist.
5.5.1 Smooth Scrolling
Zusätzlich zu der Möglichkeit, Maschinensprache-Routinen für das
zeichenweise, ruckelige Scrolling zu benutzen, bietet der VIC auch noch die
Möglichkeit, den Inhalt des Bildschirms selbst zu verschieben. In vertikaler
y-Richtung geht dies über die Adresse 53265 (VIC- Register
Nr. 17). Normalerweise steht hier der Wert 27 (Hexadezimal $1B). Wenn
nun Bit Nr. 3 gelöscht wird, dann wird der Bildschirm
in y-Richtung eng gestellt. Dies geht z.B. durch POKE
53265,19 oder auch durch
POKE 53265,PEEK(53265)
AND (NOT 8)
Der eng gestellte Modus hat also nur 24 Bildschirmzeilen, und normalerweise
wird in diesem Modus die obere und untere Zeile nur halb angezeigt. Sie können
nun zusätzlich noch den y-Offset verstellen, ab dem das Bild
angezeigt wird. Dies geht mit den untersten 3 Bits in Adresse 53265 (VIC-Register
Nr. 17). Normalerweise steht hier der Wert 3, was bedeutet, dass eben die
halbe erste Zeile noch sichtbar ist. Wenn Sie allerdings hier den Wert 0
eintragen (z.B. mit POKE 53265,16), dann ist die oberste Zeile nicht
sichtbar, und die unterste Zeile ganz sichtbar. Im Endeffekt können Sie also
pixelweise nach unten scrollen, z.B. wenn Sie die folgende Schleife benutzen:
10 FOR I=16 TO 23:POKE 53265,I:NEXT I
20 PRINT”[CLR/HOME][Was immer
Sie hier anzeigen wollen, z.B. eine Rennstrecke]“
30 POKE 53265,16:SYS 49152: REM HIER SEI DAS SCROLLING
NACH UNTEN ABGELEGT
40 GOTO 10
Auch nach links können Sie pixelweise scrollen, indem Sie Adresse 53270 (VIC-Register
Nr. 22) benutzen. Normalerweise steht hier der Wert 200 ($B8). Wenn Sie
nun Bit Nr. 3 in Adresse 53270 löschen, dann wird
der Bildschirm in x-Richtung eng gestellt. Normalerweise steht auch in Adresse
53270 in den untersten 3 Bits ein Verschiebungswert, hier ist es
der x-Offset. Hier ist der Offset-Wert normalerweise 0, was bedeutet, dass
nur 38 Spalten noch sichtbar sind. Wenn Sie allerdings hier den Wert 7
eintragen (z.B. mit POKE 53265,199), dann ist die rechte Spalte Nr. 40 nicht,
und die rechte Spalte ganz sichtbar. Im Endeffekt können Sie also pixelweise
nach links scrollen, z.B. wenn Se die folgende Schleife benutzen:
10 FOR I=199 TO 192 STEP
-1:POKE 53270,I:NEXT I
20 PRINT”[CLR/HOME][Was immer
Sie hier anzeigen wollen, z.B. einen Level]“
30 POKE 53270,199:SYS 49152: REM HIER SEI DAS
SCROLLING NACH LINKS ABGELEGT
40 GOTO 10
ABER ES GEHT ALLES NICHT WIRKLICH, WEIL DER BILDSCHIRM
FLIMMERT
Nun haben Sie wirklich ein Problem, und dies ist nur noch mit sehr guten
Assemblerkenntnissen lösbar. Mehr noch: Scrolling (vor Allem das weiche,
flimmerfreie Scrolling) ist in der Tat eines der vertracktesten Probleme beim
C64. Und dies liegt an der Art und Weise, wie der VIC seine Bilder aufbaut,
nämlich mit Hilfe des Rasterstrahls. Der Rasterstrahl zeichnet das
Bild streng von oben nach unten, und fängt auch bei Zeile 0 an.
Wenn dann der Strahl außerhalb des sichtbaren Bildes liegt, wird der Rahmen
angezeigt. Dies gilt sowohl in horizontale, als auch in vertikale Richtung. Wo
Rahmen und Bild anfangen sollen, kann man in gewissen Grenzen verstellen, z.B.
durch die Adressen 53265 und 53270. Alles andere ist aber fest vorgegeben.
Dies hat nun die folgenden Konsequenzen: Wenn Sie eines der VIC-Register
ändern, während der Rasterstrahl noch im sichtbaren Bereich ist (also nicht auf
dem Rahmen), dann zeichnen Sie quasi eine Zeit lang nur halbe Bilder, und
natürlich flimmert dann das Scrolling - zumindest, wenn die Verschiebung in
Pixeln gerechnet nicht ein Vielfaches von 8 ist. Sie müssen also das weiche
Scrolling so realisieren, dass Sie die Adressen 53265 und 53270 nur dann
ändern, wenn der Rasterstrahl die Position 0 hat (oberer Rahmen) oder 255
(unterer Rahmen). Leider hilft dies auch noch nicht so viel, denn beim
Scrolling nach unten frischen Sie das Bild von unten nach oben auf. Das heißt
dann, dass Ihr Bildaufbau und der Rasterstrahl gegenläufig arbeiten. Auch dies
flimmert beim weichen Scrolling, denn zu allem Überfluss ist der Prozessor des
C64 nicht fähig, dem Rasterstrahl quasi „vorauszueilen“ und ein vollständiges
Bild aufzubauen, während nur der Rahmen gezeichnet wird. Es gibt nun folgende
Lösungen, die aber nur in Assembler realisiert werden können:
·
Verwenden von effizienten
Byte-Vertauschungsalgorithmen, die das Bild von oben nach unten auffrischen und
ggf. die untere Bildhälfte zuerst aufbauen, und danach erst die obere
·
Verwenden von illegalen OP-Codes, die mehrere
Schritte gleichzeitig ausführen können, um dadurch den Kopiervorgang zu
beschleunigen (es geht hier im Endeffekt um das Einsparen von
Prozessor-Taktzyklen durch OP-Codes, die eigentlich nicht so vorgesehen sind)
·
Verwenden des Raster-Interrupts für
eigene Bildauffrischungsroutinen, ggf. in Kombination mit illegalen OP-Codes
und/oder Abschalten des BASIC-ROMs und/oder überflüssiger Interrupt-Quellen
(auch dies spart Zeit ein)
·
Verwenden eines Offscreen-Buffers:
Hier wird das eigentliche (veränderte) Bild in einem nicht sichtbaren
Speicherbereich abgelegt und anschließend durch eine schnelle Kopierschleife in
den sichtbaren Bereich verschoben. Ein Offscreen-Buffer
ist quasi der flimmerfreie Königsweg, deshalb bekommt die Methode, die
einen Offscreen-Buffer benutzt auch einen extra
Namen: Double Buffering. Der große Nachteil von Double Buffering
ist der zusätzliche Speicherverbrauch, und auch die Tatsache, dass der
Farbspeicher leider nicht verschoben werden kann.