Reise durch die Wunderwelt der Grafik (Teil 4)
Nachdem in den ersten drei Folgen unseres Grafikkurses alle Grundlagen geschaffen wurden, nähern wir uns endlich mit Riesenschritten unserem Ziel: Die hochauflösende Grafik des Commodore 64, die von uns endlich aus ihrem Dornröschenschlaf geweckt wurde, gezielt in unseren Basic-Programmen einzusetzen. Die nötigen Hilfsmittel, wie zum Beispiel eine kleine Bibliothek von Grafik-Unterprogrammen, werden in dieser Folge vorgestellt.
Sie können bisher Zeichen im Mehrfarbenmodus und (aber nicht gleichzeitig) mit veränderten Hintergrundfarben darstellen. Das Prinzip der Bit-Map ist Ihnen vertraut und Sie wissen, wie man dem Computer sagt, daß er nun seine Bildschirminformationen aus dieser Bit-Map holt. Sie können in diesem Modus die Farben bestimmen und schließlich auch Punkte exakt in die Bit-Map setzen. Wenn Ihnen der Inhalt der Bit-Map nicht mehr gefällt, können Sie sie löschen.
In dieser vierten Folge werden wir lernen, wie man die Speicher des Commodore 64 für Grafikanwendungen umkrempelt. Wir werden »Dornröschen« in mehreren Farben erleben und schließen diesen Teil der hochauflösenden Grafik mit einer kleinen Unterprogrammbibliothek ab. Der einleitenden Worte sind genug gesagt, Dornröschen wartet.
Wir krempeln den Commodore 64 um: Speicherveränderungen für hochauflösende Grafik
Wenn Sie als stolzer Besitzer eines C 64 früher auch mal ebenso stolzer Besitzer eines VC 20 waren, dann ist Ihnen sicherlich in wehmütiger Erinnerung, was Sie sehen, wenn Sie durch die POKE-Kommandos
POKE 51,255:POKE 52, 31:POKE 55,255:POKE 56,31
in der letzten Folge die Bit-Map vor dem Überschreiben durch Basic geschützt haben und dann mal mit PRINT FRE (1) nach dem freien Basic-Speicher fragten: Da zeigte sich: 6144 (ohne Programm)!
Geht das Ringen um jedes Byte nun wieder los? Wie soll denn in diese 6 KByte ein besseres Spiel mit Hochauflösungsgrafik — ganz zu schweigen von anspruchsvolleren Programmen, zum Beispiel einer Kurvendiskussion — hineinpassen? Nun, keine Sorge: Wozu haben wir im C 64 denn 64 KByte RAM? Wir müßten nur wissen, wie wir sie nutzen können.
Dazu sehen wir uns nochmal den VIC-II-Chip an. Im Gegensatz zur CPU (unserem Prozessor 6510), die über 16 Adressenleitungen verfügen kann, stehen beim VIC-II-Chip lediglich 14 zur Disposition. Während man also mit 16 Leitungen alle Adressen
von 0 bis 1111 1111 1111 1111 = 65535
ansteuern kann, ist bei 14 Leitungen ein Maximum
von 11 1111 1111 1111 = 16383 Adressen möglich, also 16 KByte.
Der gesamte Speicherraum des C 64 ist in vier solche 16 KByte-Blöcke aufteilbar und wie wir wissen, blickt der VIC-II-Chip im Normalfall auf den ersten 16 KByte-Block (siehe Bild 1). Nun kann man dem VIC-II-Chip mitteilen, daß er seine Aufmerksamkeit auf die anderen Speicherviertel richten möge. Das erfordert die Mitarbeit des »Portiers« CIA 2 (siehe Folge 1). Er hat im Gebäude 56576 zwei Zimmer (Bits 0 und 1), aus denen dem VIC-II-Chip die Anweisungen gegeben werden, um welches Viertel unseres Speichers er sich kümmern soll. Auf welche Weise diese Bits den VIC-II-Chip-Zugriff regeln, sehen Sie aus Tabelle 1.
![](162-1.jpg)
Commodore empfiehlt nun noch sicherzustellen, daß die zu dieser Abschnittsauswahl gehörigen Bits des Datenrichtungsregisters Port A im CIA 2 (Speicherstelle 56578) auf 1, also auf Ausgabe, gestellt werden. In allen Programmen, die ich bisher verwendet habe, wäre das eigentlich nicht nötig gewesen, jedenfalls habe ich nichts bemerkt, als ich das nicht getan habe.
Trotzdem gebe ich hier diese Empfehlung weiter, falls in Ihren Programmen diese Maßnahme notwendig wird. Falls Sie vergessen haben sollten (Folge 2), wie man Bits setzt oder löscht, hier die nötigen Programmzeilen dazu:
20 POKE 56576, (PEEK(56576) AND 252) OR I
30 POKE 56578, PEEK(56578) OR 3
I ist dabei der in Tabelle 1 gezeigte Dezimalwert der Bits 0 und 1.
56576 BITS 10 |
I BITWERT DEZIMAL |
ABSCHNITT | SPEICHERBEREICH von – bis |
BEMERKUNGEN |
---|---|---|---|---|
11 | 3 | 0 | 0 – 16 383 | EINSCHALTZUSTAND VON 4096 – 8191 ZEICHEN-SPIEGELBILDER |
10 | 2 | 1 | 16 384 – 32 767 | KEINE ZEICHEN VERFÜGBAR |
01 | 1 | 2 | 32 768 – 49 151 | VON 36 864 – 40 959 ZEICHEN-SPIEGELBILDER VON 40 960 – 49 151 BASIC-ROM |
00 | 0 | 3 | 49 152 – 65 535 | KEINE ZEICHEN VERFÜGBAR VON 53 258-65 535 I/O, ZEICHEN-ROM BETRIEBSSYSTEM |
Der VIC-II-Chip managt auch den Bildschirm. Im Einschaltzustand packt er den Bildschirmspeicher — wie wir schon wissen — in den Bereich 1024 und 2023. Wenn wir nun einen anderen 16 KByte-Abschnitt wählen, legt er den Bildschirm an die entsprechende Stelle dieses Abschnittes, also:
In Abschnitt 0: Bildschirm von 1024 bis 2023
in Abschnitt 1: Bildschirm von 16384 + 1024 = 17408
bis 16384 + 2023 = 18407
in Abschnitt 2: Bildschirm von 32768 + 1024 = 33792
bis 32768 + 2023 = 34791
in Abschnitt 3: Bildschirm von 49152 + 1024 = 50176
bis 49152 + 2023 = 51175.
Damit brauchen wir uns aber nicht zufrieden geben.
Multivisio — unter 64 Bildschirm wählen
Im 16 KByte-Speicherabschnitt hat der Bildschirmspeicher ja 16mal Platz und wir können ihn ohne weiteres an eine andere Stelle schieben. Damit kehren wir nochmal zur schon oft besungenen Speicherstelle 53272 zurück. Die Bits 4 bis 7 geben dem VIC-II-Chip den Ort des Bildschirmspeichers an. Durch Verändern dieser 4 Bits können wir tatsächlich die 16 Bildschirme pro 16 KByte-Abschnitt einrichten. Der Zusammenhang zwischen den Bits 4 bis 7 von Adresse 53272 und dem Ort des Bildschirmspeichers im Abschnitt 0 (und entsprechend parallelverschoben in den anderen Abschnitten) ist in Tabelle 2 gezeigt.
Um die entsprechende Bitanordnung zu erreichen, müssen wir also eingeben:
40 POKE 53272, (PEEK(53272) AND 15) OR W
Dabei ist W der Dezimalwert der Bits 4 bis 7 aus Tabelle 2.
SPEICHERSTELLE 53272 BITS: 7654 |
DEZIMALWERT (BITS 0-3 als 0 angenommen) W | BILDSCHIRMSTARTADRESSE (IM ABSCHNITT 0) | |
DEZIMAL | HEX | ||
0000 | 0 | 0 | 0 |
0001 | 16 | 1024 | 400 |
0010 | 32 | 2048 | 800 |
0011 | 48 | 3072 | C00 |
0100 | 64 | 4096 | 1000 |
0101 | 80 | 5120 | 1400 |
0110 | 96 | 6144 | 1800 |
0111 | 112 | 7168 | 1C00 |
1000 | 128 | 8192 | 2000 |
1001 | 144 | 9216 | 2400 |
1010 | 160 | 10240 | 2800 |
1011 | 176 | 11264 | 2C00 |
1100 | 192 | 12288 | 3000 |
1101 | 208 | 13312 | 3400 |
1110 | 224 | 14336 | 3800 |
1111 | 240 | 15360 | 3C00 |
Das Betriebssystem muß auch noch erfahren, daß der Bildschirmspeicher verlegt worden ist. Man kann es ihm mitteilen, indem man die Pagenummer der Bildschirmstartadresse in Speicherstelle 648 einPOKEd. Also ist zum Beispiel der normale Inhalt von Speicher 648: 1024/256 = 4. Auf Page 4 beginnt der Bildschirm ja im Einschaltzustand. Die Pagenummer ergibt sich aus I und W (siehe Tabelle 1 und 2) durch folgende Rechnung:
50 P = (W/16*1024 + 16384*(3-I))/256
und wird dann eingePOKEd:
55 POKE 648,P
So! Jetzt können wir theoretisch 64 Bildschirme erzeugen. Um uns das in der Praxis mal anzusehen, ergänzen wir die bisher verwendeten Programmzeilen (die Sie hoffentlich noch nicht mit RUN gestartet haben, das wäre nämlich mit etwas Glück eine gute Methode, den Rechner abstürzen zu lassen!) noch um folgendes:
10INPUT"I,W";I,W:PRINTCHR$(147)
60 PRINT CHR$(147)I,W:END
65 PRINT CHR$(147)
70 POKE 56576,151:POKE 56578,63: POKE 53272,21:POKE 648,4
80 I = 3:W=16:PRINT CHR$(147)I,W
90 END
Bevor Sie das starten, sollten Sie bitten daran denken, daß einige I,W-Kombinationen den Computer zum totalen Black-Out führen: Zum Beispiel I = 3,W= 0 legt den Bildschirmstart direkt in die Zeropage, ist also nicht empfehlenswert. I = 3 ,W = 32 zerstört unser Programm, das genau bei 2048 anfängt. Am besten speichern Sie das Programm vor dem Starten ab.
Jeweils nach RUN und Eingabe der Werte I und W, zum Beispiel I = 3, W = 48, wird der Bildschirm gelöscht und in der obersten Zeile wird der I- und der W-Wert angegeben. Danach meldet sich READY und ein Cursor. Jetzt befinden wir uns im neuen Bildschirm (unser Beispiel also 3072 bis 4071) und können damit herumexperimentieren.
Bildschirm-Experimente
Wenn wir jetzt (falls keine Programmänderung in der Zwischenzeit durchgeführt wurde) CONT eingeben, wird der Ursprungszustand (Bildschirm bei 1024) wieder hergestellt und dies durch die Angabe der I- und W-Werte 3 und 16 angezeigt.
Wenn Sie mit diesem Programm in den Bereich 4096 bis 8191 vorstoßen, werden Sie feststellen, daß hier kein normaler Bildschirm möglich ist. Hier stören die mehrfach schon beschworenen Geisterbilder des Zeichen-ROM, die in diesem Bereich liegen. Es kann sogar passieren, daß der Rechner nach der Eingabe von CONT nur noch SYNTAX ERRORs meldet und nicht mehr in den Normalzustand zurückzuführen ist. Ab 8192 bis 15360 (jeweils Start des Bildschirmes) kann man wieder ohne Störung Bildschirme einrichten. Wenn Sie jetzt mal I = 2 und verschiedene W-Werte versuchen, sehen Sie nur Nonsens oder gar nichts auf dem Bildschirm, dasselbe geschieht bei I = 0.
Das ist wieder eine Besonderheit des VIC-II-Chip. Er ist so strukturiert, daß der (im Normalfall) in diesen beiden Abschnitten keinen Zugang zu den normalen Zeichenspeichern hat. Dafür gibt es in Abschnitt 1 (I = 2) keine Störung durch die Zeichen-Geisterbilder, ebenso in Abschnitt 3 (I = 0). In Abschnitt 2 (I = 1) begegnen wir zwischen 36864 und 40959 wieder den hier ein zweites Mal vorhandenen Zeichen-Gespenstern. Unterhalb von 36864 läßt sich der neue Bildschirm gut verwenden.
Der verborgene Speicher — RAM-Bereiche unter dem ROM
Ein Problem stellt sich hier und auch im obersten Abschnitt aber noch auf andere Weise: Wenn wir den Bildschirm zum Beispiel mit I = 1 und W=128 nach 40960 legen (tun Sie es bitte nicht!), dann erhalten wir bei jeder Eingabe nur noch »SYNTAX ERRORs« und können den Computer nur durch Aus- und Einschalten wieder zu normaler Tätigkeit bewegen. Was ist da los? Die Erklärung ist, daß von 40960 bis 49151 das Basic-ROM und von 57344 bis 65535 das Betriebssystem dem RAM überlagert sind. Wenn man in diese Regionen hineinPOKEd, landet die Information natürlich im darunterliegenden RAM. Das was auf dem Bildschirm erscheint wenn wir aus dem Programm-Modus aussteigen, ist allerdings — leider — der Inhalt des darüberliegenden ROM. Ersetzen Sie aber mal das »END« in Zeile 60 durch die folgenden Zeilen:
60 PRINT CHR$(147)I,W:PRINT" BILDSCHIRM LIEGT UNTER DEM ROM«
65 GET A$:IF A$ = ” ”THEN 65
Siehe da! Es funktioniert also im Programm-Modus (zum Beispiel mit I = 1 und W= 128). Wir können daher unter das Basic-ROM auf diese Weise acht Bildschirme legen. Unter das Betriebssystem lassen sich so auch Bildschirme legen, nur können wir hier den Text nicht lesen, weil der VIC-II-Chip — wie gesagt — hier keinen Zugriff zum Zeichen-ROM hat. So legt zum Beispiel I = 0 und W=240 den Bildschirm nach 64512, was leicht nachprüfbar ist durch die Zeile:
62 POKE 65000, 1:POKE 55784,1.
Damit wenden wir uns nun dem kritischen Bereich zwischen 53248 und 57343 zu. Hier liegen ja das Zeichen-ROM und die Ein- und Ausgabebausteine. Normalerweise — wie man auch durch unsere POKE in diesen Bereich erkennen kann — sind hier die Ein- und Ausgabe-Bausteine eingeschaltet. Wenn wir hierher Bildschirme legen, kann alles mögliche passieren, weil wir Register des VIC-II-Chip, des SID und CIAs beeinflussen. Hier sollte man mit viel Vorsicht und gegebenenfalls nur in Maschinensprache operieren.
Was wir durch die Programmzeile 62 noch erkennen können: Das Bildschirmfarben-RAM verschiebt sich nicht, egal, welches Speicherviertel wir wählen und wohin wir den Bildschirm auch legen: Das Farb-RAM liegt immer von 55296 bis 56295.
Wohin mit der Bit-Map?
Nun aber zum großen Speicherfresser: Zur Bit-Map. Mit ihren 8000 Byte paßt sie im Prinzip achtmal in unseren Computer. Im ersten Viertel (0 bis 16383) haben wir sie schon gehabt und das als unbefriedigend empfunden. Nun wollen wir uns andere Möglichkeiten ansehen und dabei noch bedenken, daß wir auf den normalen Zeichensatz verzichten können (wir stellen ja hochauflösende Grafik dar!). Zu diesem Zweck werden wir das bisher verwendete Bildschirmtestprogramm um einen Hochauflösungsteil erweitern (Listing 1). Um die leidige Eintipperei minimal zu halten, wurde auf Schönheit und erläuternde REM-Zeilen verzichtet. Der Hochauflösungsteil stimmt weitgehend mit dem Programm aus der letzten Folge überein. Geben Sie also jetzt das Listing 1 ein, speichern Sie es möglichst gleich ab und probieren Sie es aus.
In Bild 2 sind alle möglichen Positionen der Bit-Map und des Bildschirmes angegeben.
![](162-2.jpg)
Wie man sehen kann, scheiden die Kombinationen Nummer 1 und Nummer 7 von vornherein aus, weil wir mit dem Löschen der Bit-Map auch das Lebenslicht unseres Rechners ausblasen. Die Kombination Nummer 2 kennen wir schon: So haben wir in der letzten Folge hochauflösende Grafik betrieben und waren enttäuscht über den geringen verbliebenen Basic-Speicher. Bei Nummer 5 funken uns die Zeichen-Spiegelbilder in die Bit-Map, diese Kombination scheidet also auch aus. Nett sieht es aus, wenn wir die Kombinationen Nummer 6 und Nummer 8 testen. Hier machen sich die ROM-Inhalte grafisch zwar ganz interessant aus, aber mit dem Sinn unserer hochauflösenden Grafik hat das nichts mehr zu tun. Für uns brauchbar sind die Positionen im Abschnitt 1: Kombinationen Nummer 3 und Nummer 4. Ein Maximum an Basic-Speicher findet man bei der letzten gezeigten Kombination Nummer 9, wo ab 23552 der Bildschirm und ab 24576 die Bit-Map liegen. Wenn Sie den Basic-Speicher für diese Anordnung schützen, mit POKE 55,0:POKE 56,92:POKE 52,92 und den Computer dann mit PRINT FRE(I) nach dem freien Speicherplatz fragen, dann erhalten Sie als Antwort immerhin satte 21501 freie Byte.
Der Idealfall wäre es, wenn man die Bit-Map unter das ROM legen könnte (Kombinationen 6 oder 8). Das geht natürlich auch! Von Basic aus wird ein Programm dann allerdings noch langsamer, weil man für jeden Punkt ähnliche Operationen vornehmen müßte, wie wir sie in der zweiten Folge beim Kopieren des Zeichen-ROM ins RAM verwendet haben. Auch so ist die ganze Hochauflösungsgrafik schon ziemlich langsam. Wir werden aber in kommenden Folgen einige Routinen in Maschinensprache kennenlernen, die uns mehr Möglichkeiten eröffnen.
Die Grafik bekennt Farbe: Der Bit-Map-Mehrfarben-Modus
Hochauflösende Grafik in Farbe: Kann das der C 64 überhaupt? Die Antwort lautet Ja und Nein. Ja, weil der bislang von uns verwendete Bit-Map-Modus anstelle der zwei bisher benutzten auch mit vier Farben ablaufen kann. Nein, weil die Punkteauflösung dann eigentlich nicht mehr die Bezeichnung »hochauflösend« verdient. Die horizontale Auflösung geschieht hier nämlich nur noch in 160 Positionen anstelle der bisher ansprechbaren 320. Meine persönliche Meinung dazu ist, ernsthafte hochauflösende Grafik sollte sich im bisher besprochenen Bit-Map-Modus abspielen, denn ein Bildschirm mit 64000 Bildpunkten ist schon eine Minimalanforderung. 32000 Bildschirmpositionen sind allenfalls für Spielereien ganz nett, wenn auch etwas teuer, denn ohne Farbmonitor haben Sie von der Farbe nicht viel und über den Unterschied zwischen Farbfernseher und Farbmonitor haben wir im Verlauf dieser Serie schon etwas erfahren.
Nach dieser etwas desillusionierenden Vorrede widmen wir uns also der mehrfarbigen Bit-Map-Grafik. Es handelt sich um eine Kombination aus der bisher bekannten Bit-Map-Grafik und dem Mehrfarben-Modus, den wir schon bei den mehrfarbigen Zeichen kennengelernt haben. Auf dem Bildschirm wird der Inhalt der Bit-Map wiedergegeben, aber die Farben der Zeichnungen sind abhängig von Bit-Paaren. Welche Farben Sie bei welcher Paarung sehen, darüber gibt Tabelle 3 Aufschluß.
BIT-PAAR | FARBQUELLE | |
---|---|---|
00 | SPEICHER 53 281, HINTERGRUNDREG. Nr.0 | |
01 | BITS 4-7 | DES VIDEO-RAM |
10 | BITS 0-3 | |
11 | BILDSCHIRMFARBSPEICHER |
Es existiert also eine Hintergrundfarbe für den gesamten Bildschirm, die überall dort auftaucht, wo in der Bit-Map ein Bit-Paar 00 vorhanden ist. Diese Hintergrundfarbe ist durch den Zahlenwert in Register 53281 bestimmt. Die anderen drei Farben treten jeweils in den 8 x 8-Bit-Bildschirmfeldern auf, in die das Video-RAM, beziehungsweise der Bildschirmfarbspeicher aufgeteilt sind.
Wir ergänzen unser Programm 1 für den Mehrfarben-Modus: Außer dem Bit-Map-Modus muß hier also noch der Mehrfarb-Modus eingeschaltet werden. Das haben wir in der letzten Folge kennengelernt: 145 POKE 53270,PEEK(53270) OR 16 Weiterhin ändern wir noch die Zeile 5 und schreiben anstelle von F=6 jetzt GOSUB 300. Folgende Zeilen kommen neu hinzu:
300 PRINT CHR$(147)CHR$(17)"FARBENWAHL:”
310 PRINT CHR$ (17)"HINTER-GRUNDFARBE"TAB (30);:INPUT F0
320 PRINT "BITS 4-7 VIDEOMATRIX"TAB(30);:INPUT F1
330 PRINT "BITS 0-3 VIDEOMATRIX"TAB(30);:INPUT F2
340 PRINT "BILDSCHIRMFARBSPEICHER"TAB(30);:INPUT F3
350 POKE 53281,F0:F=16*Fl+F2: FOR I = 55296 TO 56295:POKE I,F: NEXTI
360 PRINT CHR$(147):RETURN
Um des Effektes willen ändern wir noch die Zeile 220, in der die Videomatrix mit den Farbzahlen belegt wird:
220 FOR J = 0TO 998 STEP 2:POKE J,F:NEXT J:FOR J = 1TO 999 STEP 2:POKE J,F+l:NEXTJ
Starten Sie dieses Programm nach dem Abspeichern mit RUN, probieren Sie alle möglichen Farbkennzahlen aus. Sie werden fast immer bemerken, daß die Hoch- und Tiefpunkte der Kurve nicht mitgezeichnet werden. Das liegt daran, daß unter Verfahren der Berechnung, welches Bit in welchem Byte gesetzt werden soll, noch auf einzelne Bits und nicht die paarweise Verwendung abgestimmt ist. Es gibt zwei Möglichkeiten: Entweder ändert man das Berechnungsverfahren in Zeile 240, um an die richtige Stelle die passenden Bit-Paare zu bekommen, oder wir ändern die Programmzeile 230 zu:
230 FOR I = 0 TO 319 STEP 2:Y= FNA(X)
Das ist zwar mal wieder etwas primitiv, aber — wie gesagt — sind meine Ambitionen zur Mehrfarben-»Hochauflösung« sowieso nicht so stark. Wer Lust hat, kann sich ja gerne mal mit der korrekten Weise der neuen Berechnungherumschlagen. Das Wissen, diese Aufgabe zu bewältigen, haben Sie jetzt. Auf dem Schwarzweiß-Monitor sieht zum Beispiel folgende Zahlenkombination ganz gut aus:
Hintergrundfarbe: 7
Bits 4 bis 7: 5
Bits 0 bis 3: 1
Bildschirmspeicher: 0
Außerdem natürlich noch: I = 2, W=112, B = 1.
Mit dem hier folgenden Abschnitt soll zunächst einmal die hochauflösende Grafik beiseite gelegt werden. Dornröschen ist erwacht und genesen, allerdings noch nicht voll bei Kräften. Das wird in einer späteren Folge noch anders werden. Davor wollen wir aber noch weitere Grafik-Besonderheiten des C 64 behandeln. An dieser Stelle wollen wir jedoch einen Zwischenhalt einlegen und eine kleine Sammlung von Basic-Unterprogrammen zur Grafik-Programmierung vorstellen. In Listing 2 sind diese Grafik-Unterprogramme, in Listing 3 ein Beispiel-Aufrufprogramm abgedruckt. Beim Eintippen beider Programme können Sie die REM-Zeilen ohne Schaden weglassen.
Erläuterungen zu Listing 2 (Zeilenbereich 49990 bis 51500)
Sprungtabelle:
Das ist in Basic im allgemeinen nicht üblich, sondern wird häufiger bei Maschinensprache-Programmen verwendet. Trotzdem hat es auch hier seine Vorteile. Es kann ja sein, daß Sie einige Änderungen oder Ergänzungen in den Unterprogrammen vornehmen wollen. Sie müßten dann auch immer die Adressen in den jeweils aufzurufenden Hauptprogrammen umschreiben. Mit der Sprungtabelle ist das nicht mehr nötig, denn die GOSUB-Adressen im Hauptprogramm bleiben unverändert, nur die neuen GOTO-Adressen im Unterprogramm sind einzusetzen.
HIRES an
Hier machen wir uns die Erkenntnisse dieser Folge zunutze und legen den Bildschirm nach 23552 und die Bit-Map nach 24576. Beides müssen wir — wie gehabt — vor dem Überschreiben durch Basic schützen mit:
POKE 52,92:POKE 56,92.
Am besten packt man diese POKE-Befehle gleich in die ersten Zeilen des aufrufenden Hauptprogramms.
Bit-Map-Löschen
Hierzu gibt es nichts mehr zu sagen, außer, daß I als Laufvariable dient.
Farbgebung
Bevor dieses Unterprogramm aufgerufen wird, müssen im Hauptprogramm
F1 = Zeichenfarbe und
F2 = Hintergrundfarbe
definiert sein. An Variablen treten noch auf:
F = Farbcodezahle auf Fl und F2
I = Laufvariable
HIRES aus
Dieses Programm stellt die ursprüngliche Speicherorganisation wieder her (Bildschirm- und Zeichenspeicher) und schaltet in den Normalmodus zurück.
Punkt setzen
Auch hier müssen vor dem Aufruf des Unterprogramms im Hauptspeicher die
Punktkoordinaten X,Y
definiert sein (siehe Bild 3) sowie die
L — Löschmarke
![](162-3.jpg)
Wenn L = 0 ist, wird der Punkt gesetzt (Zeile 50930), so, wie wir das schon kennen. Ist L = 1, dann wird ein vorhandener Punkt gelöscht (Zeile 50920).
Die Zeile 50905 achtet darauf, daß keine Punktkoordinate außerhalb des Bildschirms liegt, was unter Umständen ein Aussteigen mit Fehlermeldung im Hochauflösungsverfahren zur Folge hätte. Das ist hier zwar nicht so tragisch, weil man durch Eingeben von GOTO 50030 »RETURN« schnell wieder in den Normalmodus gelangen kann (eventuell muß vorher noch »SHIFT« + »CLR/HOME« gedrückt werden). Trotzdem ist es dumm, wenn inmitten all dieser zeitraubenden Grafiktätigkeiten auch noch der Rechner aussteigt. Eine Grenzüberschreitung der Koordinaten ist um so leichter möglich, als die Punkt-Routine von allen folgenden Unterprogrammen aufgerufen wird. Außer X,Y und L tauchen noch die Variablen BY und BI auf, die wir schon kennengelernt haben als das Byte, in dem ein Bit zu setzen oder zu löschen ist.
Punkt löschen
Hier geschieht nichts anderes, als die Löschmarke L auf 1 zu setzen und dann in die Punkt-Setz-Routine zu springen. Deswegen gilt für dieses Unterprogramm dasselbe wie für das vorangegangene.
Strecke zeichnen
Vor dem Aufruf müssen dem Rechner schon
der Startpunkt (X1,Y1) und
der Endpunkt (X2,Y2) der Strecke
bekannt gemacht sein (siehe Bild 4).
![](162-4.jpg)
Den mathematisch Versierten wird es bei der Betrachtung der Zeilen 51120 beziehungsweise 51160 schon aufgefallen sein, daß zur Berechnung der Punkte, aus denen sich die Strecke zusammensetzt, die sogenannte 2-Punkte-Form der Geradengleichung verwendet wurde:
(Y-Y1)/(X-X1) = (Y2-Y1)/(X2-X1)
Den mit Mathematik nicht so vertrauten Lesern sei gesagt, daß es sich um eine Formel aus der sogenannten analytischen Geometrie handelt. Das ist ein Gebiet der Mathematik, das für die Grafik auf Computern eine nicht unerhebliche Rolle spielt.
Die Punkte (XI, Y1) und (X2, Y2) dürfen auch außerhalb des Bildschirmsystems liegen. Im ersten Teil des Unterprogramms dient die Übergabevariable X auch gleichzeitig als Laufvariable während Y berechnet wird. Wenn allerdings der Absolutbetrag von X2-X1 kleiner als 5 wird, verkehren sich die Verhältnisse: Y wird Laufvariable und X berechnet. Das beschleunigt das Zeichnen von Senkrechten und verhindert außerdem eine Division durch Null. Der Wert von 5 ist dabei ziemlich willkürlich gewählt. L ist wieder die Löschmarke.
Strecke löschen
Es gilt dasselbe wie für das Unterprogramm Strecke zeichnen, nur daß hier wieder die Löschmarke gesetzt wird.
Ellipse zeichnen
Vor dem Aufruf müssen folgende Werte schon definiert sein (siehe auch Bild 5):
(XM,YM) = Mittelpunktkoordinaten
HX = Halbmesser in X-Richtung
HY = Halbmesser in Y-Richtung
WU,WO = Der zu zeichnende Ellipsenbogen beginnt beim Winkel WU und endet beim Winkel
WO (Gradmaß)
![](162-5.jpg)
Eine volle Ellipse wird also gezeichnet, wenn WU = 0 und WO = 360 ist. Der Kreis ist ein Sonderfall der Ellipse. Dann muß nur HX = HY sein.
Für mathematisch Interessierte: Es werden die Parametergleichungen der Ellipse verwendet:
X = XM + HX*COS(WB) und Y=YM + HX*SIN(WB)
Auch hier gibt es keine Einschränkung wie beim Strecken-Zeichnen m der Größe von XM, YM, HX, WU, WO.
W ist eine Laufvariable (ein Winkel) der in WB (gleicher Winkel im Bogenmaß) umgerechnet wird. L ist wieder die Löschmarke.
Ellipse löschen
Bis auf das Setzen der Löschmarke gilt dasselbe wie für das Zeichnen der Ellipse.
Kombination
Erfordert schon definierte Farbkennzahlen F1 und F2 (siehe Farbgebung) und schaltet dann die Hochauflösung an, löscht die Bit-Map und sorgt für die Farbe.
Soweit die Unterprogramme in Listing 2.
Ein Beispiel für die Möglichkeiten der Grafik-Bibliothek
Als ein Beispiel für die Möglichkeiten der Unterprogramm-Sammlung habe ich (ohne nun besonders auf Schönheit zu achten — das sind Sie ja von mir schon gewohnt), noch ein Hauptprogramm angefügt, mit dem Sie etwas herumprobieren können (Listing 3). Das Listing ist ausführlich kommentiert, so daß hier nur wenige Erläuterungen folgen müssen.
Beim Eintippen müssen Sie für einige Zeilen die Abkürzungen (siehe Handbuch Seite 130 ff) der Basic-Befehle eingeben, da die Zeilen sonst länger als 80 Zeichen werden.
Nach »RUN« sehen Sie ein Menü, das alle Möglichkeiten der Grafik-Unterprogramme ansteuert. Die Optionen 8 (Strecke zeichnen) bis B (Ellipse löschen) sowie 4 (Farbgebung) und 5 (Kombinationen) erfordern Eingaben. Es ist daher sinnvoll, diese Optionen nur im Normalmodus anzuwählen. Der Normalmodus ist immer dann zu erreichen, wenn Zeichenoperationen im Hochauflösungsmodus abgeschlossen sind. Drücken Sie dann "2', sind Sie wieder im normalen Rechnerbetrieb.
Sollten Sie durch irgendeinen Umstand (zum Beispiel durch Drücken der »RUN/STOP«-Taste) im Hochauflösungsmodus aus dem Programm fallen, dann hilft der folgende Weg:
- »SHIFT« + »CLEAR/HOME«
- »RUN« »RETURN«
- dann »2« eingeben
Die Option »C« zeigt eine kleine Demonstration von Möglichkeiten der Grafik-Unterprogramme. Allerdings sollten Sie ein bißchen Zeit mitbringen, wenn Sie C anwählen: Das ganze dauert zirka 25 Minuten.
Option 6 (Punkte zeichnen) ist so eingerichtet, daß 320 Punkte in Form einer Sinus-Funktion gezeichnet und mit Option 7 (Punkte löschen) teilweise wieder gelöscht werden.
Ausblicke — schnellere Grafik durch Maschinensprache
Diese Folge soll nicht beendet werden, ohne einen kleinen tröstlichen Ausblick. Wie Sie — besonders im letzten Programm — feststellen konnten, braucht man schon einiges Sitzfleisch für hochauflösende Grafik in Basic. Wenn Sie aber ein kommerzielles Grafik-Programm laufen sehen, geht das alles erheblich schneller. Was ist der Unterschied? Da wäre zunächst einmal die Programmiersprache: Unser C 64 kann eigentlich gar kein Basic. Er braucht den Basic-Interpreter, der zunächst jeden Befehl liest und dann in Maschinensprache übersetzt. Die versteht unser Rechner zwar, die Übersetzung und das Lesen dauern jedoch lange Zeit. Eine starke Beschleunigung der Grafik ist möglich durch Programmieren in Maschinensprache. Einige solche Maschinenspracheprogramme zur beschleunigten Grafik werden in den nächsten Folgen gezeigt. Allerdings stoßen wir da bald an die Grenzen unseres Commodore. Ein 8-Bit-Computer mit zirka 1 Megahertz Taktfrequenz wie unser C 64 ist beispielsweise in der Fließkomma-Arithmetik (wie sie für das Zeichnen von Ellipsen nötig ist) zeitlich gehandicapt, und deswegen sind der Geschwindigkeit bei komplexerer Grafik doch einige Grenzen gesetzt.
(Heino Ponnath)1 rem ************************* 2 rem * programm 1 * 3 rem ************************* 5 f=6:deffna(x)=50*sin(x/30)+100 10 printchr$(147)chr$(17)"eingabe der werte"chr$(17) 20 printchr$(17)"i(siehe tab.1) abschnitt-kennziffer"chr$(17) 30 print"w(siehe tab.2) bildschirm-kennziffer"chr$(17) 40 print"b=0,bit-map im unteren abschnitt-bereich" 50 print" =1,bit-map im oberen abschnitt-bereich"chr$(17) 60 printchr$(17):input"i,w,b=";i,w,b:a1=3-i:a2=w/16*1024+a1*16384:a3=a1*16384+b*8192 70 p=a2/256:printchr$(17)chr$(17)"mit diesen eingaben haben sie" 80 print"den abschnitt "a1" gewaehlt." 90 print"ihr bildschirm startet bei "a2 100 print"und ihre bit-map bei "a3 110 printchr$(17)"ist das so in ordnung?(j/n)" 120 geta$:ifa$=""then120 130 ifa$="n"then10 140 printchr$(147) 145 rem **** neuer speicher **** 150 poke56576,(peek(56576)and252)ori 160 poke56578,peek(56578)or3 170 poke53272,(peek(53272)and15)orw 180 poke648,p 190 poke53265,peek(53265)or32 200 poke53272,peek(53272)or(8*b) 210 forj=0to7999:pokea3+j,0:nextj 220 forj=0to999:pokea2+j,f:nextj 230 forx=0to319:y=fna(x) 240 by=(xand504)+40*(yand248)+(yand7):bi=7-(xand7) 250 pokea3+by,peek(a3+by)or(2^bi):nextx 260 geta$:ifa$=""then260 265 rem **** alter speicher **** 270 poke53272,21:poke53265,27:poke648,4:poke56578,63:poke56576,151 280 printchr$(147):end
49990 rem ----------------- 49991 rem --unterprogramme- 49992 rem ----------------- 49993 rem 49996 rem ----------------- 49997 rem --sprungtabelle-- 49998 rem ----------------- 49999 rem 50000 goto50500:rem hires an 50010 goto50600:rem bit-map-loeschen 50020 goto50700:rem farbgebung 50030 goto50800:rem hires aus 50040 goto50900:rem punkt setzen 50050 goto51000:rem punkt loeschen 50060 goto51100:rem strecke zeichnen 50070 goto51200:rem strecke loeschen 50080 goto51300:rem ellipse zeichnen 50090 goto51400:rem ellipse loeschen 50100 goto51500:rem kombiniertes hires an 50490 rem 50491 rem ----------------- 50492 rem ----hires an----- 50493 rem ----------------- 50494 rem 50500 poke56576,(peek(56576)and252)or2 50510 poke56578,peek(56578)or3 50520 poke53272,120:poke648,92 50530 poke53265,peek(53265)or32 50540 return 50590 rem 50591 rem ------------------- 50592 rem -bit-map-loeschen-- 50593 rem ------------------- 50594 rem 50600 fori=24576to32575:pokei,0:nexti 50610 return 50690 rem 50691 rem -------------------- 50692 rem -----farbgebung----- 50693 rem -------------------- 50694 rem 50700 f=16*f1+f2 50710 fori=23552to24551:pokei,f:nexti 50720 return 50790 rem 50791 rem ------------------- 50792 rem -----hires aus----- 50793 rem ------------------- 50794 rem 50800 poke53265,27:poke53272,21:poke648,4 50810 poke56578,63:poke56576,151 50820 return 50890 rem 50891 rem ---------------------- 50892 rem -----punkt setzen----- 50893 rem ---------------------- 50894 rem 50900 l=0 50905 ifx<0orx>319ory<0ory>199then50940 50910 by=(xand504)+40*(yand248)+(yand7):bi=7-(xand7) 50920 ifl=1thenpoke24576+by,peek(24576+by)andnot(2^bi):goto50940 50930 poke24576+by,peek(24576+by)or(2^bi) 50940 return 50990 rem 50991 rem ----------------------- 50992 rem -----punkt loeschen---- 50993 rem ----------------------- 50994 rem 51000 l=1:goto50905 51090 rem 51091 rem ------------------------ 51092 rem ----strecke zeichnen---- 51093 rem ------------------------ 51094 rem 51100 l=0 51110 ifabs(x2-x1)<5then51150 51115 forx=x1tox2step(x2-x1)/319 51120 y=(y2-y1)/(x2-x1)*(x-x1)+y1 51130 gosub50905:nextx 51140 return 51150 fory=y1toy2step(y2-y1)/199 51160 x=(x2-x1)/(y2-y1)*(y-y1)+x1 51170 gosub50905:nexty 51180 return 51190 rem 51191 rem ------------------------- 51192 rem ----strecke loeschen----- 51193 rem ------------------------- 51194 rem 51200 l=1:goto51110 51290 rem 51291 rem -------------------------- 51292 rem -----ellipse zeichnen----- 51293 rem -------------------------- 51294 rem 51300 l=0 51310 forw=wutowo:wb=w*~/180 51320 x=xm+hx*cos(wb):y=ym+hy*sin(wb) 51330 gosub50905 51340 nextw:return 51390 rem 51391 rem -------------------------- 51392 rem -----ellipse loeschen----- 51393 rem -------------------------- 51394 rem 51400 l=1:goto51310 51490 rem 51491 rem -------------------------- 51492 rem --kombiniertes hires an--- 51493 rem -------------------------- 51494 rem 51500 gosub50000:gosub50010:goto50020
1 rem ******************************* 2 rem * grafik test programm von * 3 rem * h.ponnath 1984 verbrochen * 4 rem ******************************* 5 poke52,92:poke56,92:deffna(x)=50*sin(x/30)+100 7 rem ******************************* 8 rem * menue-guten appetit * 9 rem ******************************* 10 printchr$(147)chr$(17)"unterprogramme grafik test"chr$(17) 20 printtab(2)"nur hires an"tab(25)"1" 30 printtab(2)"nur hires aus"tab(25)"2" 40 printtab(2)"bit map loeschen"tab(25)"3" 50 printtab(2)"farbgebung"tab(25)"4" 60 printtab(2)"kombination"tab(25)"5" 62 printtab(2)"punkte setzen",tab(25)"6" 64 printtab(2)"punkte loeschen"tab(25)"7" 66 printtab(2)"strecke zeichnen"tab(25)"8" 68 printtab(2)"strecke loeschen"tab(25)"9" 70 printtab(2)"ellipse zeichnen"tab(25)"a" 72 printtab(2)"ellipse loeschen"tab(25)"b" 74 printtab(2)"demonstration"tab(25)"c" 76 printtab(1)"menue"tab(25)"m" 78 printtab(1)"aussteigen"tab(25)"_" 80 geta$:ifa$=""then80 90 ifa$="_"thenend 92 ifa$="a"thena$="10" 94 ifa$="b"thena$="11" 96 ifa$="c"thena$="12" 98 ifa$="m"then10 100 onval(a$)gosub50000,50030,50010,200,300,400,500,600,700,800,900,1000 110 goto80 190 rem *************************** 191 rem * farbgebung * 192 rem *************************** 200 input"zeichenfarbe,hintergrundfarbe=";f1,f2:goto50020 290 rem *************************** 291 rem * farbe fuer kombination * 292 rem *************************** 300 input"zeichenfarbe,hintergrundfarbe=";f1,f2:goto50100 390 rem *************************** 391 rem * beispiel punkte setzen * 392 rem *************************** 400 gosub50000:forx=0to319:y=fna(x):gosub50040:nextx 410 return 490 rem *************************** 491 rem * beispiel punkte loeschen* 492 rem *************************** 500 gosub50000:forx=20to250:y=fna(x):gosub50050:nextx 510 return 590 rem *************************** 591 rem * aufrufprogramm fuer * 592 rem * strecke zeichnen * 593 rem *************************** 600 print"{rvon}strecke{rvof} von (x1,y1) bis (x2,y2)":input"x1,y1,x2,y2=";x1,y1,x2,y2 610 gosub50000:goto50060 690 rem *************************** 691 rem * aufrufprogramm fuer * 692 rem * strecke loeschen * 693 rem *************************** 700 print"{rvon}strecke{rvof} von (x1,y1) bis (x2,y2)":input"x1,y1,x2,y2=";x1,y1,x2,y2 710 gosub50000:goto50070 790 rem *************************** 791 rem * aufrufprogramm fuer * 792 rem * ellipse zeichnen * 793 rem *************************** 800 print"{rvon}ellipse{rvof} mit mittelpunkt (xm,ym),":printtab(1)"halbmessern hx und hy" 810 printtab(1)"zeichnen von winkel wu":printtab(1)"bis winkel wo (gradmass)" 820 input"xm,ym,hx,hy,wu,wo=";xm,ym,hx,hy,wu,wo:gosub50000:goto50080 890 rem *************************** 891 rem * aufrufprogramm fuer * 892 rem * ellipse loeschen * 893 rem *************************** 900 print"{rvon}ellipse{rvof} mit mittelpunkt (xm,ym),":printtab(1)"halbmessern hx und hy" 910 printtab(1)"loeschen von winkel wu":printtab(1)"bis winkel wo (gradmass)" 920 input"xm,ym,hx,hy,wu,wo=";xm,ym,hx,hy,wu,wo:gosub50000:goto50090 990 rem *************************** 991 rem * aufrufprogramm fuer * 992 rem * demonstration * 993 rem *************************** 1000 gosub50030:printchr$(147):fori=1to10:printchr$(17);:nexti 1010 printtab(8)"bitte etwas geduld":printtab(5)"die bit-map wird geloescht" 1020 gosub50010:printtab(5)"und mit farbe versehen":fori=1to1000:nexti 1030 f1=7:f2=6:gosub50000:gosub50020:fori=1to500:next:gosub50030 1040 printchr$(147):fori=1to10:printchr$(17);:nexti 1050 printtab(5)"zeichen von strecken":fori=1to500:nexti 1060 gosub50000:fori=0to12 1070 x1=30+i*10:y1=180-i*14.17:x2=150+i*13.3:y2=10+i*14.583 1080 gosub50060:nexti:fork=0to9:f1=k:f2=k+1:gosub50020:forj=1to500:nextj:nextk 1090 gosub50030:print:printtab(5)"strecken loeschen" 1100 fori=1to500:nexti:gosub50000:x1=30:y1=180:x2=150:y2=10:gosub50070 1110 x1=310:y1=185:gosub50070:fori=1to500:nexti:gosub50030 1120 print:printtab(5)"ellipsen zeichnen":fori=1to500:nexti:gosub50000 1130 xm=170:ym=150 1140 fori=0to16:wu=i*90:wo=wu+90 1150 hx=20+8*int((3+i)/4):hy=10+8*int((2+i)/4) 1160 gosub50080:nexti:x1=0:y1=0:x2=319:y2=0:gosub50060 1170 x2=0:y2=199:gosub50060:x1=319:y1=199:gosub50060:x2=319:y2=0:gosub50060 1180 x1=100:y1=0:x2=100:y2=80:gosub50060:x1=0:y1=80:gosub50060 1190 forx=0to100:y=40+25*sin(x/15):gosub50040:nextx 1200 x1=219:y1=0:x2=219:y2=80:gosub50060:x1=319:y1=80:gos50060 1210 xm=249:ym=40:hx=20:hy=30:wu=0:wo=360:gosub50080 1220 x1=279:y1=10:x2=279:y2=70:gosub50060:y1=40:x2=309:y2=10:gosub50060 1230 y2=70:gosub50060 2000 return