The following article was printed in issue 2/88 of the magazine „PC AMSTRAD INTERNATIONAL".
|
Grundlagen |
XBIOS
-Routine #35 (Screen-Run-Routine) aufgerufen werden, die BASIC aus- und den Bildschirmspeicher einblendet.
Gleichzeitig sind der sogenannte Roller-RAM und der Character-RAM erreichbar.
Der Artikel im JOYCE Sonderheft Nr. 1 beschrieb den Aufbau von Bildschirmspeicher und Roller-RAM aber nur sehr ungenau.
Klar war von Anfang an eigentlich nur, daß der Bildschirmspeicher bitmapped aufgebaut ist.
D.h., daß jedem Pixel auf dem Bildschirm ein Bit im Speicher entspricht.
Außerdem sollten im Roller-RAM die Adressen der einzelnen Pixelreihen stehen.
Das ist eine der Besonderheiten des JOYCE.
Die Daten im Bildschirmspeicher stellen zwar ein Abbild des Bildes auf dem Schirm dar, aber wo sie auf dem Schirm erscheinen, wird vom Roller-RAM bestimmt.
Das hat den Vorteil, daß beim Scrollen nicht der gesamte Bildschirmspeicher umgeschrieben werden muß.
Geändert werden lediglich die Adressen der Pixelreihen im Roller-RAM.
Das Roller-RAM gibt beim Bildschirmaufbau also an, wo die Daten für die einzelnen Pixelreihen im Speicher stehen.
Wie das alles aber nun genau organisiert ist, das war vorerst noch äußerst unklar.
Mit folgender kleinen Routine haben wir uns dann daran gemacht, die Sache genauer zu untersuchen:
ORG #F000 BEGIN LD
BC,ADRESS
CALL #FC5A
DW #00E9
RET ADRESS LD HL,QUELLE
LD DE,#F100
LD BC,#0200
LDIR
RET
QUELLE
definierten Stelle 512 Byte von Bank 0 in den Common-Bereich.
Dort können die Werte dann auch von BASIC verarbeitet werden.
Zunächst haben wir versucht, die Anfangsadresse des Bildschirmspeichers zu finden.
Dazu haben wir von BASIC aus den Bildschirm gelöscht, verschiedene Werte für QUELLE
in den Speicher gepoked und uns dann das Resultat im Common-Bereich angesehen.
Bald war die Stelle gefunden, an der über einen großen Bereich nur noch Nullen folgten.
Der Bereich begann bei #5930
.
Jetzt ging es darum, den genauen Aufbau einer Pixelreihe herauszufinden.
Unsere Überlegung war, daß nach dem Einschalten und bevor der Bildschirm gescrollt wird, der Bildschirmspeicher noch "in Ordnung" ist, d.h. in den ersten Bytes auch das erste Zeichen auf dem Schirm codiert ist.
Wir gingen dann wie oben vor, nur daß wir nach dem Löschen genau ein Zeichen in die linke obere Ecke des Bildschirms schrieben und daß wir das Programm gleich nach dem Booten gestartet haben.
Es stellte sich dann heraus, daß nicht etwa (wie es die Beschreibung des Roller-RAMs vermuten ließ) nacheinander von den ersten 90 Zeichen jeweils die oberste Reihe im Speicher stand, sondern, daß die ersten acht Byte genau für das erste Zeichen standen.
Im Bildschirmspeicher stand also eine Kopie des Zeichens aus dem Character-RAM.
Jetzt wollten wir wissen, wie denn dann aber das Roller-RAM organisiert ist.
Die Startadresse des Roller-RAMs ist #B600
, jedenfalls behauptete das der Artikel.
Also mußten wir mal wieder CP/M booten, BASIC laden und uns dann mit unserem Programm den Bereich ab #B600
ansehen.
Auf den ersten Blick sah es so aus, als ob wir einer Ente aufgesessen wären.
Jeweils zwei Byte als Adresse gewertet, ergaben Zahlen wie #2C98
.
Aber dort lag der Bildschirmspeicher doch gar nicht.
Allerdings lagen alle Werte in einem festen Bereich, und das war dann doch ein Zeichen dafür, daß es sich tatsächlich um den Roller-RAM handelte.
Des Rätsels Lösung ergab sich schließlich aus den Differenzen.
Die erste Zwei-Byte-Adresse sollte ja auf den Beginn der ersten Pixelreine zeigen, die neunte mußte logischerweise auf den Beginn der ersten Pixelreihe der zweiten Bildschirmzeile zeigen.
Da in einer Zeile 90 Zeichen stehen und ein Zeichen von acht Byte dargestellt wird, ergibt sich eine Differenz von 720 Byte.
Die Differenz zwischen der neunten und der ersten Zwei-Byte-Adresse betrug aber tatsächlich 360 Byte, genau die Hälfte.
Die Adressen im Roller-RAM müssen also erst verdoppelt werden, wenn man die entsprechende Stelle im Bildschirmspeicher finden will.
Und schon ist man reingefallen.
Es gibt da nämlich noch ein kleines Problem (und das übergeht Matthias Uphoff in seinem Artikel in Heft 6 dieses Jahres).
Zur Erinnerung:
Im Bildschirmspeicher liegen die acht Byte, die ein Zeichen darstellen, direkt hintereinander.
Nach dem Booten steht das Byte für die oberste Pixelreihe des ersten Zeichens auf dem Schirm in #5930
, das für die zweite Reihe in #5931
usw.
Diese Adressen beziehen sich aber nicht nur auf das erste Zeichen, sondern sind die Anfangsadressen der gesamten ersten bzw. zweiten Pixelreihe.
Die sollten aber auch im Roller-RAM stehen, allerdings durch zwei geteilt.
Und da liegt der Hase im Pfeffer.
Teilen Sie mal #5931
durch 2!
Das Ergebnis läßt sich nicht als Zwei-Byte-Zahl darstellen.
Tatsächlich stehen im Roller-RAM aufeinanderfolgende Adressen, also #2C98
, #2C99
, ..., #2C9F
für die ersten acht Pixelreihen.
Der erste Wert verdoppelt ergibt genau die gewünschte Adresse, beim zweiten ist das Ergebnis um 1 zu groß, beim dritten um 2 usw.
Das ist aber so regelmäßig, das sich daraus leicht eine Formel ableiten läßt.
Wenn die oberste Pixelreihe die Nummer Null erhält, gilt folgende Formel:
Adresse im Bildschirmspeicher =
Adresse im Roller-RAM * 2
- Zeilennummer Modulo 8
Das Programm "Screeny" |
SETCHAR
" (ab Zeile 50030),SETPOINT
" (ab Zeile 51000),DRAW
" (ab Zeile 52000) undPOINT2
" (ab Zeile 53000).SETCHAR
" kann man ASCII-Zeichen an jede beliebige Bildschirmposition setzen.
SETCHAR
" hat folgende Syntax:
CALL setchar (zeile%, spalte%, zeichennummer%)
zeile%
" kann 0-248, für "spalte%
" 0-712 als Variable eingesetzt werden.
Diese Begrenzung ergibt sich daraus, daß jedes Zeichen aus 8*8 Pixeln besteht.
Für "zeichennummer%
" wird der jeweilige ASCII-Code-Wert eingesetzt.
SETPOINT
" kann man einzelne Pixel ansprechen durch:
CALL setpoint (zeile%, spalte%, modus%)
zeile%
" zwischen 0 und 255, für "spalte%
" zwischen 0 und 719.
Außerdem gibt es drei Modi:
SETCHAR
" und "SETPOINT
" können alleinstehend als Unterprogramm verwendet werden.
"POINT2
" und "DRAW
" gehören zusammen und benutzen "SETPOINT
" als Unterprogramm.
Zum Zeichnen einer Linie werden so alle drei Teile benötigt.
POINT2
" wird zuerst der Zielpunkt festgelegt.
Falls mehrere Linien den gleichen Zielpunkt haben, muß dieser nicht vor jedem Aufruf neu festgelegt werden, sondern kann für alle Linien verwendet werden.
"POINT2
" hat die Syntax:
CALL point2 (zeile%, spalte%)
DRAW
" zeichnet eine Linie im jeweiligen Modus (0, 1 oder 2) von dem angesprochenen Punkt zu dem mit "POINT2
" bestimmten Zielpunkt.
Die Syntax:
CALL draw (zeile%, spalte %, modus%)
(Variablen wie gehabt!)
POINT2
" muß außer bei der erwähnten Mehrfachbenutzung immer vor "DRAW
" aufgerufen werden.
|
|
Hinweise zum Abtippen des Programms:
Die links neben den Zeilennummern befindlichen Zahlen in spitzen Klammern nicht mit abtippen, es handelt sich hierbei um Prüfsummen für den Checksummer aus Heft 2/87. Die geschweiften und eckigen Klammern und den senkrechten Strich im Lisling durch die jeweiligen Umlaute ersetzen. Nach dem Abtippen mit SAVE"SCREENY.BAS" abspeichern! Start des Programms: RUN"SCREENY" |
LISTING >SCREENY <, REMARK = >'<. |
[BASIC source] |
LISTING >ROUTINEN<, REMARK = >;<. |
[The individual routines for the M80 assembler: SETCHAR, SETPOINT, DRAW, POINT2 and additionally character definition (line 2080 in the BASIC program) ] |
Scanned by
Werner Cirsovius
January 2005
© DMV-Verlag