In der „PC AMSTRAD INTERNATIONAL 6/87" wurde der folgende Artikel abgedruckt.
Ein interessantes Paket zur Manipulation des JOYCE-Zeichensatzes.

Superscript

- Abenteuer im Inneren des Joyce

Wo liegt bei meinem Computer der Bildschirmspeicher, wie ist er aufgebaut, wie komme ich an ihn heran? Diese Frage stellt sich früher oder später bei jeder neuen Maschine. Sobald man nämlich weiß, welche Bytes in welcher Form wo auf dem Bildschirm erscheinen, stehen alle Gestaltungsmöglichkeiten offen: Grafik, Sprites, Windows mit ein paar Assemblerroutinen ist das kein Problem mehr. Doch auf dem Weg dorthin trifft der Programmierer mitunter auf unerwartete Hindernisse...


Ein Brief mit Folgen
Eines Tages erreichten mich per Post ein paar Fotokopien, die allesamt am oberen Rand mit einer Lokomotive verziert waren - ein diskreter Hinweis auf ein bekanntes britisches Softwarehaus. Und diese Zettel beschrieben detailliert die Zugriffsmöglichkeiten auf den Bildschirmspeicher des Joyce! Zum Glück war auch der Joyce-Assembler fertig (siehe Heft 3/87 Siehe Hinweis am Ende), also begab ich mich auf direktem Weg an die Arbeit. Ein paar Linien auf dem Bildschirm - das haben wir gleich, dachte ich, so schwierig kann das ja nicht sein. Aber weit gefehlt...
Doch bevor ich das nun folgende Drama schildere, möchte ich ein paar Facts zum Joyce liefern, die klarstellen, warum der Zugriff auf den Bildschirmspeicher im Gegensatz zum CPC bei dieser Maschine so kompliziert ist. Eine kurze Rechnung: Der Bildschirm des Joyce umfaßt 32 Zeilen mit 90 Zeichen und wird im sog. Bitmap-Grafikmodus betrieben, d.h. jedem Bildpunkt ist ein Bit im Speicher zugewiesen. Ein Buchstabe besteht aus 8 X 8 Pixeln, das macht 8 Bytes und damit insgesamt 8 X 32 X 90 = 23040 Bytes Bildschirmspeicher. Der Z 80-Prozessor kann 64 KByte Speicher an einem Stück verwalten.
Wenn der Bildschirm davon mehr als ein Drittel beansprucht und dann noch der Basic-Interpreter dazugeladen wird, so kann man sich leicht ausrechnen, daß kaum noch Platz für eigene Programme bleibt. Doch die Joyce-Konstrukteure haben sich einiges einfallen lassen, um die vorhandenen 256 KByte möglichst gut auszunutzen. Der gesamte Speicherplatz ist in vier »Bänke« zu je 64 Kbyte eingeteilt, und diese wiederum in 4 »Blöcke« zu je 16 Kbyte. Während sich das Hauptgeschehen meistens in Bank 1 abspielt (hier befindet sich auch das Basic), wird ein großer Teil der internen Vorgänge über die Bank 0 abgewickelt, in der u.A. der Bildschirmspeicher residiert. Bei jeder Bildschirmausgabe schaltet das Betriebssystem einfach auf die andere Bank um, schreibt z.B. einen Buchstaben ins Video-RAM und kehrt dann in die Bank 1 zurück, um das Kommando wieder dem BasicInterpreter zu übergeben. Aus diesem Grund kann man den Bildschirmspeicher auch nicht durch PEEK und POKE erreichen, da er ja zu diesem Zeitpunkt für den Prozessor überhaupt nicht vorhanden ist - der Z 80 »sieht« immer nur die 64 KByte, die gerade eingeschaltet sind.


Banküberfall
mit Hindernissen

Zwar könnten wir die Hardware des Joyce auch unter Basic durch einen entsprechenden OUT-Befehl dazu veranlassen, diese Umschaltung für uns vorzunehmen, doch damit ist der Basic-Interpreter in der Bank 1 dann »verschwunden« - wie sollen wir wieder zurückkommen? Außerdem weiß der Prozessor ja überhaupt nicht, welches Programm er in der Bank 0 ausführen soll, ein Absturz wäre sehr wahrscheinlich. Das ist wirklich ein Problem: Nach dem Bankswitching muß das gerade laufende Programm irgendwie sinnvoll weitergehen, d.h. entweder muß es in beiden Bänken parallel vorhanden sein, oder es muß einen Bereich geben, der allen Bänken gemeinsam ist, vom Umschaltvorgang also nicht berührt wird. Und genau das ist beim Joyce auch der Fall: Die sogenannte »Common-Area« umfaßt 16 KByte und liegt im Adressbereich von #C000 bis #FFFF. Alle Umschaltvorgänge laufen über Routinen in diesem Speicherbereich, weshalb sich auch ein Teil des Betriebssystems hier niedergelassen hat. Fragen Sie mal nach dem Start des Interpreters die Speicherobergrenze mit
PRINT HEX-(HIMEM)
ab, dann wird Ihnen der Joyce mitteilen, daß etwa ab #F600 sein privater Bereich beginnt: Zutritt verboten!
Mit diesem Wissen versehen, können wir uns an folgende Assemblersequenz wagen, die den Schlüssel zum sogenannten »Screen Environment« des Joyce darstellt:
LD    BC,adresse
CALL  #FC5A
DW    #00E9
RET
Dieses Kurzprogramm schaltet alle Speicherblöcke zusammen, die in irgendeiner Weise etwas mit der Bildschirmausgabe zu tun haben und sorgt dann dafür, daß die Routine, deren Adresse wir im BC-Registerpaar übergeben haben, in dieser Umgebung ausgeführt wird, wobei nur eines streng zu beachten ist: Sie muß im Common-Bereich ab #C000 stehen! Abschließend wird für die Rückkehr zum aufrufenden Programm gesorgt. Der hier benutzte CALL ruft übrigens das XBIOS (erweiterte BIOS) des Joyce auf, das nachfolgende Datenwort bewirkt die Ausführung der internen »Screen Run Routine«, die die eben beschriebenen Umschaltvorgänge organisiert. Wer sich weitergehend mit den Möglichkeiten des XBIOS beschäftigen möchte, sollte sich den Artikel von Michael Anton im Joyce-Sonderheft nicht entgehen lassen!


Das geheimnisvolle
Roller-RAM

Jetzt aber zurück zur Bildschirmumgebung: Nachdem das XBIOS die richtigen Blöcke zusammengefaßt hat, sieht der Speicher so aus:
ab #0000: Betriebssystem
ab #4000: Bildschirmspeicher
ab #B600: Roller-RAM
ab #B800: Zeichensatz
ab #C000: Common-Area
Der Kenner wittert hier schon die Gelegenheit, den Zeichensatz nach eigenem Geschmack umzubauen. Das Zeichen 0 belegt 8 Bytes ab Adresse #B800, danach folgt Zeichen 1 usw. Man muß nur die gewünschten neuen Bitmuster in einem geschützten Bereich der Common Area ablegen, mitsamt einer kleinen Routine, die den Standardzeichensatz an der gewünschten Stelle überschreibt, und das Ganze dann von Basic aus mit Hilfe der oben angegebenen XBIOS-Sequenz aufrufen - fertig! Einige Rätsel gibt jedoch das Roller-RAM auf. Haben Sie sich schon einmal Gedanken darüber gemacht, wie Ihr Joyce eigentlich das relativ schnelle Scrolling hinkriegt?
Ich zitiere aus der Dokumentation:
»Das Roller-RAM beginnt ab #B600 und hat das folgende Format:
Bytes 0,1:
Bytes 2,3:
   :     :
Bytes 511,512:
Adresse der 1. Pixelreihe
Adresse der 2. Pixelreihe

Adresse der 255. Pixelreihe <
Schlau gemacht, nicht wahr? Wenn der Video-Chip den Speicherinhalt ausliest, schaut er erst einmal im Roller-RAM nach, wo sich die jeweilige Pixelreihe überhaupt im Bildspeicher befindet. Rollt der Bildschirminhalt um eine Textzeile nach oben oder unten, so müssen nur die 512 Adressen im Roller-RAM entsprechend umsortiert werden.


Schicksal,
nimm deinen Anlauf...

Ich saß also vor dem Joyce und wollte zunächst eine horizontale Linie über die volle Bildschirmbreite erzeugen. Zu diesem Zweck schrieb ich eine Assemblerroutine, die sich die Startadresse einer Pixelreihe aus dem Roller-RAM besorgte und 90 Bytes mit dem Wert #FF füllte. Das Ganze war nach etwa 10 Minuten fertig, wurde von Basic aus gestartet, und es passierte... rein überhaupt nichts! Ich überprüfte nochmals das Programm, noch ein Versuch: RUN, und die Kiste stürzte sang- und klanglos ab! Ich beschloß, mich zunächst darauf zu beschränken, aus dem Bildschirmspeicher zu lesen, da das Schreiben dem Rechner offensichtlich nicht gut bekam. Also wurde ein Basic-Programm erstellt, das die oberste Zeile mit einem Buchstaben füllte, um dann per Assemblerroutine das dazugehörige Bitmuster aus dem Bildschirmspeicher in einen Puffer im Common-Bereich zu übertragen, wo man es anschließend bequem untersuchen konnte. In der Tat erschienen nach dem Start lauter Hexzahlen, die allerdings kaum ein sich wiederholendes Bitmuster im Video-RAM darstellen konnten, dazu war die Folge zu unregelmäßig. Ich ließ das Ganze in ASCII ausgeben, und was erschien? Ein Listing meines Assembler-Quellcodes, gut durchsetzt mit allen möglichen Sonderzeichen!


Erste Indizien -
007 läßt grüßen.

Also gut - zähneknirschend schrieb ich eine weitere Assemblerroutine, die den Inhalt des ominösen Roller-RAMs zwecks näherer Inspektion in den Common-Bereich übertragen sollte. Und in der Tat schienen die Werte etwas mit dem Video-RAM zu tun zu haben: Auf dem Bildschirm erschien eine Ansammlung von hexadezimalen 2-Byte-Werten, die etwa so aussah:
2168 2169 216A 216B
216C 216D 216E 216F
22D0 22D1 22D2 22D3
22D4 22D5 22D6 22D7
und so weiter, immer 8 aufeinanderfolgende Werte, dann ein Sprung, und das alles im Bereich von Hex 2000-4000. Also immerhin etwas - aber sollten nicht angeblich in diesem Adressbereich der Bank 0 Teile des Betriebssystems liegen und der Bildschirmspeicher ab Hex 4000 beginnen? Langsam wurden mir zwei Dinge klar:
- Bei meinem Leseversuch aus dem Video-RAM hatte ich offenbar einen Diskettenpuffer erwischt, der noch Daten vom letzten Ladevorgang enthielt; nur so war das geheimnisvolle Erscheinen des Assembler-Quellprogramms zu erklären.
- Die Dokumentation des ScreenEnvironment war schlicht und einfach falsch. Offenbar handelte es sich hier um eine vom britischen Geheimdienst lancierte Falschinformation, um den Vorsprung britischer Softwarehäuser vor den dummen Germans zu sichern: Kaufen sollen sie, nicht selber machen!


Der rettende Anker
Die mühsame Detektivarbeit, die nötig war, um die Bildschirmverwaltung des Joyce zu entschleiern, will ich hier nicht mehr in allen Einzelheiten ausbreiten. Als Rettungsanker im wahrsten Sinne des Wortes erwies sich letztendlich die Statuszeile, die ja beim Scrollen den einzigen Fixpunkt bildet. Also ließ ich ein Testprogramm solange etwas unter Angabe der jeweiligen Adresse in den Bildschirmspeicher ab Hex 4000 schreiben, bis ich dabei zufällig die Statuszeile erwischte. Die dazugehörige Stelle im Roller-RAM war schnell gefunden, und nach Vergleich der dortigen Eintragungen mit der tatsächlichen Adresse ergab sich folgendes Bild:
Damit die Adressen aus dem Roller-RAM überhaupt in den Bildschirmspeicher zeigen, müssen Sie zunächst einmal mit 2 multipliziert werden! Von Belang ist für den Programmierer überhaupt nur jede 8. Eintragung, die genau die Startadresse (geteilt durch 2) einer Textzeile (90 Bytes lang, 8 Bytes hoch) angibt, oder mit anderen Worten: Hier steht die oberste Pixelreihe des ersten Zeichens in einer Zeile. Die Adressen der nächsten Pixelreihen darunter erhält man durch Addition von jeweils 1, danach folgt dann direkt das zweite Zeichen der selben Zeile, und so weiter - Bild 1 zeigt das sogenannte »Mapping« noch einmal genau.
Diese interne Organisation läßt zwei Rückschlüsse zu: Erstens ist das Roller-RAM keine Programmierhilfe, um sich im Video-RAM zurechtzufinden, sondern eindeutig auf die Bedürfnisse des VideoControllers zugeschnitten. Zweitens wird deutlich, daß der Joyce schon von seiner internen Konzeption her ein Textsystem ist: Da die Pixelreihen eines Zeichens im Video-RAM direkt aufeinander folgen, kann ein Zeichen ohne Umstände mit dem schnellen Blocktransferbefehl LDIR dorthin übertragen werden. Will man nun also ohne Hilfe des Betriebssystems in Assembler ein Zeichen z an der Position Reihe, Spalte (in Textkoordinaten von 0..31 bzw 0..89) erscheinen lassen, so sind folgende Schritte notwendig: - Man berechnet die Adresse der Matrix im Zeichensatz nach der Formel
MAdr = B800 + z * 8
- Die Adresse im Roller-RAM ergibt sich aus der gewünschten Reihe:
RAdr = B600 + Reihe * 16
- Aus der dort befindlichen Eintragung und der Textspalte berechnet man die Screenadresse:
SAdr = (RAdr) * 2 + Spalte * 8
- Die 8 Pixelreihen der Zeichenmatrix werden mit LDIR von MAdr nach SAdr übertragen. Ein Verfahren, um Hires-Grafik zu erzeugen, d.h. einzelne Pixel zu setzten oder zu löschen, fällt etwas komplizierter aus (es erfordert zusätzlich logische Verknüpfungen mit Bitmasken) und soll an dieser Stelle nicht näher erläutert werden.


Superscript
und Happy End

Ist Ihnen aufgefallen, daß die meisten Joyce-Programme wegen der geringen Gestaltungsmöglichkeiten des Textbildschirms ein ziemlich langweiliges Design aufweisen? Wenn man nun verschiedene Schriftgrößen und -typen zur Auswahl hätte, auf dem Bildschirm unterstreichen und vielleicht sogar Exponenten und Indizes darstellen könnte, wäre das nicht fein? Wie gesagt - ist man erst einmal im Bildschirmspeicher drin, sind der Phantasie im Prinzip keine Grenzen gesetzt. Hier also nun das Ende der Geschichte: Superscript heißt die kleine Basicerweiterung, die auf dem Joyce folgende Variationen bei der Textausgabe erlaubt:
-
-
-
-
-
-
-
-
Dünnschrift
Fettschrift
Unterstreichen
Invers
doppelte Breite
doppelte Höhe
Exponenten
Indizes
(1)
(2)
(4)
(8)
(16)
(32)
(64)
(128)


Extrabreit in Basic
Was die Nummern hinter den Schrifttypen bedeuten, kommt gleich - zunächst soll geklärt werden, wie Sie diese Erweiterung für eigene Programme nutzen können. Als erstes brauchen Sie das Unterprogramm, das Sie im Listing ab Zeile 720 finden; es schreibt den »Superscript<-Maschinencode in den Speicher und muß zu Beginn Ihres Programmes einmalig mit GOSUB 720 aufgerufen werden. Natürlich können Sie die Zeilennummern auch nach Bedarf mit RENUM ändern, das tut der Sache keinen Abbruch. Weiterhin sollte zu Beginn eine Variable mit der Startadresse &HF000 belegt werden, also z.B. so
script = &HF000
Um einen Text in der gewünschten Form auf den Bildschirm zu bringen, brauchen Sie zwei Integervariablen (nur solche;), die die Spalten- und Zeilenposition des Textes enthalten:
x% = 1:y% = 15
Als nächstes belegen Sie eine Stringvariable mit dem EscapeZeichen CHR-(27), einem Code, der den gewünschten Schrifttyp wählt, und natürlich dem Ausgabetext:
text$=CHR$(27) + CHR$(16) + "TEST"
Und wenn Sie jetzt das Ganze mit
CALL script(x%,y%,text$)
abschicken, erscheint das Wort »TEST< in doppelter Breite auf dem Bildschirm. Damit wird auch die Bedeutung der Nummern hinter den Schrifttypen klar: Sie geben den dazugehörigen Code an. Die Typen können weiterhin beliebig gemischt werden, indem man die Codes addiert; so ergibt z.B. CHR$(27) + CHR$(48) Schrift mit doppelter Breite und doppelter Höhe (48 = 16 + 32).
Dabei sind nur folgende Ausnahmen zu beachten:
- Dünnschrift hat Vorrang vor Fettschrift
- Doppelte Höhe hat Vorrang vor Exponenten und Indizes
Diese Möglichkeiten können also nicht gemischt werden; tun Sie es trotzdem, so entscheidet Superscript wie angegeben. Exponenten und Indizes werden dargestellt, indem das Zeichen um eine halbe Zeile nach oben bzw. unten versetzt wird; sie belegen also zwei Zeilen auf dem Bildschirm. Weiterhin ist noch zu erwähnen, daß CHR$(27) + CHR$(0) auf normale Schrift zurückschaltet, und CHR$(27) + CHR$(255) ergibt eine besonders dicke Überraschung, die hier nicht verraten werden soll man muß sie einfach sehen; Superscript ist übrigens vollkommen unabhängig vom Betriebssystem, weshalb die gewohnten Steuerzeichen und auch der automatische Zeilenvorschub bei dieser Art der Ausgabe nicht funktionieren: Falls Sie über den Rand hinausschreiben, wird der Text einfach abgeschnitten. Dafür können Sie problemlos mit y% = 32 die Statuszeile beschriften, falls nötig!
Das Demolisting soll im wesentlichen die verschiedenen Gestaltungsmöglichkeiten mit Superscript zeigen und als Referenz dienen, falls Sie bei der Programmierung eigener Anwendungen auf Schwierigkeiten stoßen. Den Zeilen 450-510 können Sie z.B. entnehmen, wie sich mathematische Formeln effektvoll darstellen lassen. Abschließend bleibt nur noch zu erwähnen, daß die Assemblerspezialisten unter den Joyce-Besitzern den kommentierten Superscript-Quellcode1 (hier als M80-Quellcode) in der DATABOX finden - passend zum Assembler aus Heft 3/87 Siehe Hinweis am Ende.
(M. Uphoff)

Hinweis: Der erwähnte Assembler aus Heft 3/87 - geschrieben in BASIC - findet sich hier. Er erzeugt auf Wunsch DATA-Zeilen für BASIC.
1. Unter TURBO PASCAL erfolgt die Parameterübergabe anders als bei BASIC. Der hier aufgeführte Artikel zeigt eine Übersicht über die Unterschiede und stellt das Demolisting in TURBO PASCAL vor.

Eingescanned von Werner Cirsovius
Juli 2003, März 2006
© DMV-Verlag