CODING ★ Der Gläsernen CPC ★

Gläsernen CPC : Fill-routinen (CPC Amstrad International)
★ Ce texte vous est présenté dans sa version originale ★ 
 ★ This text is presented to you in its original version ★ 
 ★ Este texto se presenta en su versión original ★ 
 ★ Dieser Text wird in seiner Originalfassung präsentiert ★ 

In dieser Folge des gläsernen CPC wird, wie im letzten Heft angekündigt, die FILL-Routine weiter ausgearbeitet. Unter anderem wird dazu die Speicheraufteilung des CPC analysiert.

Fragt sich jetzt nur noch, wo wir die Adressen und Masken herbekommen.
Die Antwort ist sehr einfach: Das Betriebssystem des CPC wird sie uns liefern. Für diese Fälle hält es nämlich einen Haufen feiner Routinen parat, die uns eine Menge Arbeit abnehmen werden. Insbesondere die komplizierte Umrechnung der Anwenderkoordinaten in Bildschirmdaten, die ja zumindest einmal zu Beginn unserer FILL-Routine durchgeführt werden muß. bleibt uns so erspart.

Um welche Routinen es sich im einzelnen handelt, können Sie einer Liste entnehmen, in der wir alle für die schnelle FILL-Routine wichtigen CALLs zusammengestellt haben. Hier wollen wir jetzt einfach annehmen, daß uns die Werte bereits zur Verfügung stehen, und zwar in folgender Form:

  • Die Bildschirmadresse im HL-Registerpaar
  • die Bitmaske im C-Register
  • die Farbmaske im B-Register.

Diese Belegung ist übrigens nicht willkürlich gewählt, sondern der Art und Weise angepaßt, in der uns das Betriebssystem die Daten zur Verfügung stellt.

Wie sieht nun die Assemblersequenz aus,dieeinen Punkt auf den Bildschirm bringt? Passen Sic gut auf:

LD A,C befördert die Bitmaske in den Akku
AND B verleiht ihr etwas Farbe
LD (HL)A schreibt sie in den Bildschirmspeicher.

Damit haben wir exakt drei Bytes gebraucht und nicht etwa 300, wie die PLOT-Routine. Toll, nicht wahr?

Und wenn Sic jetzt energisch Protest einlegen, so ist das ein Zeichen dafür, daß Sie sehr aufmerksam mitgedacht haben. Unser »Hauruck-Verfahren« enthält einen groben Fehler! Wenn wir nämlich einfach die komplette Bitmaske in den Bildschirmspeicher schreiben, wird zwar der Punkt korrekt erscheinen, aber gleichzeitig werden alle weiteren Punkte in dem aktuellen Byte gelöscht. Hier müssen wir schon etwas feinsinniger vorgehen und exakt nur die Bits ändern, die für unseren Punkt zuständig sind. Mit einigen verwirrenden logischen Operationen läßt sich jedoch auch das recht schnell erreichen. Hier ist die Sequenz:

LD A,B Farbmaske in den Akku holen
XOR (HL) mit Bildschirmbyte XOR-verknüpfen
AND C interessierenden Bereich ausmaskieren
XOR (HL) nochmals XOR
LD (HL),Aund ab auf den Bildschirm

Zugegeben - diese Folge ist etwas undurchsichtig. Da wir Sie aber nicht auch noch mit Tabellen voller Nullen und Einsen bombardieren wollen, belassen wir es hier bei dem Versprechen, daß es funktioniert.

Glücklicherweise ist die Sequenz, die einen Punkt auf eine bestimmte Farbe testet, einfacher zu verstehen. Dazu muß man nur wissen, daß eine XOR-Verknüpfung das Resultat Null ergibt, wenn zwei Werte gleich sind. Sie können es in Basic ausprobieren:

PRINT 1 XOR 1

Wenn wir also untersuchen wollen, ob die Farbe eines Bildpunktes mit der durch die Farbmaske repräsentierten Farbe identisch ist, so können wir mit XOR vergleichen:

LD A,B Farbmaske in den Akku holen,
XOR (HL) mit dem Bildschirmbyte vergleichen
AND C und den Bildpunkt ausmaskieren.

Danach befindet sich im Akku eine Null, lalls die Farben gleich sind und das Zero-Flag entsprechend gesetzt ist.

Mit einem bedingten Sprungbefehl kann das Programm dann auf das Ergebnis reagieren.

Damit stehen uns die. für das Ausfüllen einer Fläche notwendigen. PLOT- und TEST-Sequenzen bereits zur Verfügung. Offen ist jedoch noch, wie wir die Programmteile »schaue nach links« und »schaue nach rechts« in diesem System realisieren. Leider steht uns ja keine x-Koordinate mehr zur Verfügung, die wir einfach um einen bestimmten Betrag verändern können.

Doch auch dafür gibt es eine elegante Lösung. Um zum Beispiel den Punkt rechts neben dem gerade aktuellen Bildpunkt anzusprechcn, brauchen wir nur die Bitmaske um ein Bit nach rechts zu verschieben. In der Praxis benutzt man dazu am besten den Asscmblerbe-fehl RRC ("Rotiere rechts"), der ein auf der rechten Seite herausfallendcs Bit auf der anderen Seite gleich wieder hineinschiebt.

Gleichzeitig wird dieses Bit auch noch in das Carry-Flag übertragen, wie Sie der folgenden Zeichnung entnehmen können:

Allerdings kann es dabei passieren, daß sich der Punkt bereits im nächsten Bild-schirmbytc befindet. In diesem Falle wird bei der Rotation der Bitmaske auf der rechten Seite eine 1 herausfallen, was sich über das Carry-Flag testen
läßt. So kann dann bei Bedarf die Systemroutine SCR NEXT BYTE aufgerufen werden, die die Bildschirmadresse um ein Byte weiterrechnet.

Der langen Rede kurzer Sinn - so sieht die Sequenz aus:

RRC C Bitmaske nach rechts rotieren
CALL C, SCR NEXT BYTE aufru-&0BF9 fen. falls das Carry-Flag gesetzt ist

Die Verschiebung nach links funktioniert ebenso, indem der Befehl RLC ("Rotiere links”) benutzt wird und notfalls die Systemroutine SCR PRE-V'IOUS BYTE in Aktion tritt.

So abstrakt diese Vorgänge auch sein mögen-ganz nebenbei hat sich hierein weiteres Problem von selbst erledigt. Da der aktuelle MODE in der Struktur der Bitmaske bereits impliziert enthalten ist, brauchen wir uns darum keine weiteren Gedanken mehr zu machen - unser System funktioniert in jedem Fall!

Wie man sich in x-Richtung bitweise durch den Bildschirmspcicher bewegt, wäre damit klar, aber was ist mit der y-Richtung? Diese Frage läßt sich in einem Satz beantworten: Wir benutzen einfach die Systemroutinen SCR PREVIOUS LINE und SCR NEXT LINE, die die Bildschirmadresse um eine Position nach oben bzw. unten weilerrechnen, und damit hat sich's.

Ohne Zweifel - es kommt Land in Sicht. Die ersten beiden Punkte auf unserer Problemliste (Arbeitsgeschwindigkeit, Abhängigkeit vom MODE) können wir bereits provisorisch abhaken, und es bleibt nur noch Punkt 3: Wohin mit der Unmenge von Eckpunktkoordinaten, die eine FILL-Routine mitunter produziert?

Daraus ergibt sich eine weitere Gelegenheit, etwas über professionelle Programmiertechnik zu lernen. Können Sie etwas mit dem Begriff »dynamische Speicherverwaltung« anfangen? Falls nicht, hier eine kurze Erklärung:

Eine dynamische Speicherverwaltung ist immer dann von Vorteil, wenn größere Datenmengen mit sehr unterschiedlichen Eigenschaften verwaltet werden müssen. So hat es zum Beispiel der Basic-Interpreter des CPC nicht nur mit Programmen zu tun, sondern auch mit verschiedenen Variablentypen (Real, Integer, String). Dazu kommt noch der Zeichensatz, die Ein- und Ausgabepuffer für Kassette/Diskette... und alles muß irgendwo untergebracht werden.

Bei einer statischen Verwaltung wird der Speicher von vornherein fest aufgeteilt, indem zum Beispiel 20KByte für Programme reserviert werden, und wenn die voll sind, heißt es eben »Memory full«, auch wenn im Variablenbereich noch gähnende Leere herrscht.
Eine dynamische Verwaltung kennt dagegen keine festen Grenzen, sondern teilt den Platz nach Bedarf ein, womil sie sich flexibel auf sehr verschiedene Anforderungen einstellen kann. So findet im CPC ein kurzes Programm mil vielen Variablen genauso Platz, wie ein langes Programm mit relativ wenigen Variablen, und der Speicherplatz wird optimal genutzt.

Da der Speicherbedarf der FILL-Routine von Fall zu Fall sehr verschieden sein kann und die Daten auch nui kurzfristig gespeichert werden brauchen, ist es also unsinnig, dafür einen Bereich ständig reserviert zu halten. In unserei Basic-Version hatten wir allerdings kaum eine andere Wahl, als uns mit dei DIM-Anweisung auf eine bestimmte Arraygröße festzulegcn. In Assembler besteht jedoch die Möglichkeit, die FILL-Routine in die bereits vorhandene dynamische Speicherverwaltung des Interpreters einzubinden - soll der sich mal darum kümmern, wo die, im Extrem-fall benötigten, 20KByte noch hinpassen!

Die Speicheraufteilung des CPC

Bevor wir dieses Projekt in Angriff nehmen, sollten Sie sich jedoch einen Gesamtüberblick über die Speicheraufteilung des CPC verschaffen. Alle wichtigen Speicherbereiche haben wir deshalb in einem Diagramm zusammengcstellt.

Die mit &XXXX gekennzeichneten Grenzen sind variabel, werden also vom Interpreter nach Bedarf gesetzt.

Auffallend ist dabei, daß ein Teil des Adressbereiches doppelt genutzt wird. Die unteren 16KByte werden zusätzlich vom Betriebssystem-ROM belegt, und die oberen 16KByte vom Basic-ROM. Solange es darum geht, etwas in den Speicher hineinzuschreiben, stellt die Überlagerung von RAM und ROM kein Problem dar - ein ROM (Read only Memory) kann nicht beschrieben werden. Anders verhält es sich jedoch, wenn in dem Bereich Daten gelesen oder Programme aufgerufen werden. In diesem Fall muß mit einem Umschaltmechanismus entschieden werden, ob eine ROM- oder eine RAM-Adresse gemeint ist.

Angenommen, wir rufen von Basic aus mit CALL die FILL-Routine auf, so ist zu diesem Zeitpunkt im gesamten Adressbereich RAM selektiert; der direkte Zugriff auf Routinen im Betriebssystem- oder Interpreter-ROM ist also nicht ohne weiteres möglich. Zwar gibt es spezielle Einsprungvektoren ins Betriebssystem, die beim Aufruf automatisch die richtige Speicherkonfiguration wählen, doch sie haben einen kleinen Nachteil: Bei jedem Aufruf wird zwischen ROM und RAM umgeschaltet.

Solange es nicht auf Höchstgeschwindigkeit ankommt, spielt diese kleine Verzögerung keine Rolle, aber bei unserer FILL-Routine würde sich das bereits bemerkbar machen. Hier bietet es sich also an. das Betriebssystem-ROM gleich zu Beginn »aufzuwecken« und dann in diesem Zustand zu belassen.

Ein weiteres Problem stellt das Basic-ROM dar. Da wir es ja wegen der dynamischen Speicherverwaltung anzapfen wollen, müssen wir auch hier Mittel und Wege finden, den passenden ROM/RAM-Status einzuschalten.

Allerdings gibt cs auch einen guten Grund, den direkten Zugriff auf die ROMs zu vermeiden, den wir hier nicht verschweigen wollen: Programme, die diese Technik benutzen, werden mit hoher Wahrscheinlichkeit nur auf dem CPC 464 laufen. Da der CPC 664/6128 aber ohnehin schon mit einer sehr guten FILL-Routine gesegnet ist, brauchen wir uns in diesem Fall keine Hemmungen auferlegen.

Will man die Auswahl des ROM/ RAM-Status in eigener Regie übernehmen, so ist es allerdings unumgänglich, sich mit einer sehr speziellen Gruppe von Z-80-Befehlen auseinanderzusetzen, den Restart-Befehlen. Es handelt sich dabei um Unterprogrammaufrufe. die nur zu ganz bestimmten Adressen verzweigen. Sie können sich darunter so eine Art GOSUB mit fest vorgegebener Zeilennummer vorstellen. Da die Zicladresse bereits in den Befehlen eingebaut ist (implizierte Adressierung). kann er durch ein einziges Byte codiert werden, was eine besonders schnelle Ausführung erlaubt - der Prozessor weiß halt sofort, wo es hingeht und braucht keine Adresse mehr zu lesen.

Deshalb werden diese Befehle gerne benutzt, um besonders häufig gebrauchte Routinen anzuspringen oder spezielle Erweiterungen des Z-80-BefehIssatzes zu realisieren. Und genau von dieser Möglichkeit haben die geistigen Väter des CPC Gebrauch gemacht und einige RST-Befehle so »umgebogen«, daß man mit ihrer Hilfe kreuz und quer durch ROM und RAM turnen kann, so wie es einem gerade paßt.

Einen von dieser Sorte, der für unsere Zwecke besonders gut geeignet ist, werden wir jetzt herausgreifen:

Der RST &18 ('Tar Call") ermöglicht es. eine Routine aufzurufen und dabei eine beliebige ROM/RAM-Konfiguration auszuwählen. Auf den Befehl muß die 2-Byte-Adresse eines Parameterblocks folgen, der drei Bytes lang ist ufid folgende Informationen enthält:

  • Die Adresse der Routine (2 Bytes)
  • Den gewünschten ROM/RAM-Status (1 Byte), der auf folgende Weise verschlüsselt wird:

unteres ROMoberes ROM
DB 252einein
DB 253ausein
DB 254einaus
DB 255ausaus

Die Werte von 0 - 251 wählen ein externes ROM aus (z.B. das Floppy-ROM) und sind für uns im Moment nicht weiter interessant.

Mit den nachfolgenden zwei Bytes wirkt der "Far Call" genau wie ein CALL-Befehl, was durch einige raffinierte Manipulationen in der durch RST &18 aktivierten Systemroutine erreicht wird, die auch den Parameterblock liest und verarbeitet.

Wenn wir also erreichen wollen, daß während der Ausführung der FILL-Routine das untere ROM eingeschaltet bleibt, so können wir sie in Assembler wie folgt aufrufen:

RST &18
DW PBLOC Adresse des Parameter blocks
RET
PBLOCK
DW FILL Adresse der FILL-Routine
DB 254 unten ROM und oben RAM wählen

Beim Rücksprung wird übrigens automatisch die vorherige RAM/ROM-Konfiguration wieder hergestellt, so daß Sie keine Angst haben brauchen, daß bei der Anwendung von RST & 18 der Rechner durcheinander gerät. Auch ineinander verschachtelte RST&18-Aufrufe mit verschiedenem ROM/RAM-Status sind ohne weiteres möglich.

So - und jetzt zur letzten Station unserer Odyssee durch das Innenleben des CPC! Wenn Sie noch einmal einen Blick auf das Speicher-Diagramm werfen, können Sie feststellen, daß zwischen den Basic-Variablen und den Zeichen-ketten ein freier Raum existiert, der die Daten der FILL-Routine aufnehmen kann. Je nach Länge eines vorhandenen Basic-Programmes kann dieser Bereich parallel zum unteren ROM liegen: also auch hier müssen wir auf den richtigen Speicherstatus achten.

Das Ende des Variablenbereiches kann man beim CPC 464 den Speicherstellen &AE89 - &AE8A entnehmen, fragt sich also noch, wieviel Platz bis zum Beginn der Strings zur Verfügung steht. Doch darum brauchen wir uns nicht zu kümmern - das erledigt eine Interpreterroutine für uns, die im oberen ROM an der Adresse &F618 zu finden ist:

Beim Einsprung muß ihr im HL-Registerpaar das Ende des Variablenbereiches übergeben werden. Zusätzlich können wir über das BC-Registerpaar eine bestimmte Anzahl von Bytes beantragen, die wir gerne loswerden möchten. Falls die Routine feststellt, daß dafür Platz ist, kommt sie mit gelöschtem Carry-Flag zurück, und im DE-Registerpaar befindet sich die neue Variablen-Obergrenze.

Ist das Carry-Flag allerdings gesetzt, so konnte sie uns beim besten Willen keinen Platz mehr einräumen: Memory full! In diesem Falle müssen wir leider darauf verzichten, die Koordinaten abzuspeichern; die FILL-Routine wird dann eben ein paar Lücken lassen.

Übrigens ist diese Interpreterroutine sogar so nett, für uns eine »Garbage Collection« durchzuführen, falls der Speicher mit Stringmüll verstopft sein sollte. Es kann also passieren, daß die FILL-Routine bei umfangreichen Aufgaben plötzlich ein paar Gedenksekunden einlegt, wenn sich viele Zeichen ketten im Speicher befinden.

FILL-Assembler

So - und damit wären wirklich alle Voraussetzungen für eine schnelle FILL-Routine geklärt! Das Assemblerlisting zeigt, wie die Realisierung im Zusammenhang aussieht. Da die Grundstruktur der Basic-Version kaum geändert wurde, dürfte es einigermaßen verständlich sein. Ein paar Feinheiten wären vielleicht noch zu erwähnen:

Neben dem ausführlich besprochenen Adresse/Masken-System werden in dem Programm zusätzlich noch die physikalischen Koordinaten der aktuellen Position im Hintergrund bereitgehalten, da sich mit ihrer Hilfe sehr leicht feststellen läßt, ob man sich noch innerhalb des Grafikfensters befindet. Meistens treiben sie sich in den Indexregistern IX und IY herum, die eigentlich zur Speicheradressierung gedacht sind. In diesem Falle erweist es sich als günstig, sie als Variablenspeicher zu »mißbrauchen«, um den zeitraubenden Datentransfer zwischen CPU und Speicheraufein Minimum zu beschränken.

Deshalb wird sogar einer der »geheimen« Z-80-Befehle genutzt, die kein Assembler kennt: Die Kombination DW &95FD in Zeile 340 und 890 sorgt dafür, daß das Lowbyte des IY-Registers zu Testzwecken vom Akkuinhalt subtrahiert wird. Weitere Informationen dazu finden Sie in CPC International 6 und 8/85. »Z-80-Profis«.

Auch die Eckpunkte werden in Form von physikalischen Koordinaten abgespeichert, mit dem Vorteil, daß der y-Wert im Bereich von 0 - 199 liegt und nur noch ein Byte belegt; der beschreibbare Bildschirmbereich des CPC umfaßt nämlich exakt 200 Rasterzeilcn. Im Vergleich zu unserer Basic-Version sparen wir also 25 % Speicherplatz.

FILL-Basiclader

Und jetzt zur praktischen Anwendung: Aufgerufen wird die Assembler-Version der FILL-Routine mit

CALL &A200,f

wobei f die Farbstiftnummer der gewünschten Füllfarbe ist. Der Startpunkt wird durch die aktuelle Position des Grafikcursors bestimmt, er muß also vorher mit MOVE x,y in die zu füllende Fläche gesetzt werden. Der auf den Basic-Lader folgende Demoteil produziert etwas moderne Kunst und zeigt dabei, wie es funktioniert.

Natürlich kann die Routine auch in ein bereits existierendes Grafikprogramm eingebaut werden, doch Vorsicht: Überzeugen Sie sich bitte immer davon, daß die Umrandung der Fläche kein Loch aufweist, da die FILL-Routine Ihnen sonst eiskalt den gesamten Bildschirm füllt. Sie kann nicht mit ESC aufgehalten werden und ist bei mangelnder Sorgfalt ohne weiteres in der Lage, eine mühsam erstellte Grafik zu vernichten!

Damit wären wir jetzt am Ende einer langen und hoffentlich nicht allzu anstrengenden Folge des »Gläsernen CPC« angelangt - wir hoffen, daß Sie die Programmiertechniken und Tricks
für eigene Programme gut gebrauchen können.

Falls Sie Lust haben, damit etwas ganz Besonderes zu programmieren, hier eine Anregung: Grafikprogramme der Spitzenklasse bieten die Möglichkeit, Flächen nicht nur mit einer Farbe, sondern auch mit einem vorgegebenen Muster zu füllen (Pattern-Fill). Die Realisierung einer solchen Routine ist sicherlich eine anspruchsvolle und schwierige Aufgabe - sollten Sie eine Lösung finden, so lassen Sie es uns wissen!

Matthias Uphoff

★ PUBLISHER: CPC Amstrad International
★ YEAR: 1986
★ LANGUAGE:
★ LiCENCE: ???
★ COLLECTION: CPC AMSTRAD INTERNATIONAL 1986
★ AUTHOR: M. Uphoff
 

★ AMSTRAD CPC ★ DOWNLOAD ★

Type-in/Listings:
» Der  Glaesernen  CPC-FILL-Routine    (86-04)    (CPC  Amstrad  International)    GERMANDATE: 2021-08-12
DL: 128
TYPE: ZIP
SiZE: 4Ko
NOTE: 40 Cyls
.HFE: Χ

» Der  Glaesernen  CPC-FILL-Routine    (86-04)    (CPC  Amstrad  International)    GERMAN    LISTINGDATE: 2021-08-12
DL: 129
TYPE: PDF
SiZE: 465Ko
NOTE: 2 pages/PDFlib v1.6

» Der  Glaesernen  CPC-FILL-Routine    (Source)    (CPC  Amstrad  International)    GERMAN    LISTINGDATE: 2021-08-12
DL: 139
TYPE: PDF
SiZE: 538Ko
NOTE: 2 pages/PDFlib v1.6

★ AMSTRAD CPC ★ A voir aussi sur CPCrulez , les sujets suivants pourront vous intéresser...

Lien(s):
» Coding » Cours et initiation du magazine CPC Revue
» Coding » Cours et initiation d'ANTIBUG
» Coding » Gläsernen CPC (CPC Amstrad International)
» Coding » CROSSDEV - SDCC - Developper en C par Stephbb75
» Coding » Etude du FDC par Michel Maignot
» Coding » Cours et initiation du Magazine Micro News
Je participe au site:
» Vous avez des infos personnel ?
» Vous avez remarqué une erreur dans ce texte ?
» Aidez-nous à améliorer cette page : en nous contactant via le forum ou par email.

CPCrulez[Content Management System] v8.7-desktop/c
Page créée en 685 millisecondes et consultée 547 fois

L'Amstrad CPC est une machine 8 bits à base d'un Z80 à 4MHz. Le premier de la gamme fut le CPC 464 en 1984, équipé d'un lecteur de cassettes intégré il se plaçait en concurrent  du Commodore C64 beaucoup plus compliqué à utiliser et plus cher. Ce fut un réel succès et sorti cette même années le CPC 664 équipé d'un lecteur de disquettes trois pouces intégré. Sa vie fut de courte durée puisqu'en 1985 il fut remplacé par le CPC 6128 qui était plus compact, plus soigné et surtout qui avait 128Ko de RAM au lieu de 64Ko.