Turbo-Grafik für den JOYCE
Im „DMV-Sonderheft 2/87" wurde der folgende Artikel abgedruckt. Ein gelungenes Grafik-Paket. Die Programmierung setzt auf der Hardware des JOYCE auf. ... Grafik mit Turbo Pascal und ohne GSX ... |
So ziemlich für alle Rechner, auf denen Turbo Pascal läuft, gibt es grafikfähige Versionen dieser leistungsfähigen Programmiersprache — nur nicht für den JOYCE. Aber damit ist Schluß, denn jetzt gibt es eine leistungsfähige Grafikerweiterung, die sogar ohne die speicherplatzfressende GSX-Ein-bindung auskommt... Am Anfang stand eine aus England stammende RSX, die unter Mallard Basic das Setzen und Löschen von Bildschirmpunkten erlaubte. Das Ganze war zwar etwas umständlich zu handhaben, aber immer noch besser als umständliches und speicherverschwendendes Operieren mit GSX, vor allem, wenn es nur um ein paar Linien ging. Mit der Zeit kam dann das Turbo Pascal-Fieber auf und die Idee einer Grafikerweiterung nahm Gestalt an. Was lag da näher, als auf Bekanntes zurückzugreifen. Allerdings war das RSX-Konzept nicht unbedingt das Wahre, hier mußte eine Abhilfe ersonnen werden, was schließlich zur Ersetzung der RSX durch External-Routinen führte. Dazu mußte natürlich die RSX erstmal »geknackt« werden — eine harte Nuß, die nach zwei durchhackten Nächten und einigen sehenswerten Systemabstürzen endlich zum ersten Erfolg führte: Pixel konnten schonmal gesetzt und gelöscht werden. Mit der Zeit wurde der maschinenabhängige Teil erweitert, erste Linien-Routinen geschrieben, Zeichensätze verändert und vieles mehr. »With a little help from our friends« — speziell Martin Schlöters Grafikserie »Vom Punkt zur 3.Dimension« aus »PASCAL International« verdanken wir viele Anregungen und »Zitate« — wuchs die JOYCE-Grafik-Erweiterung immer mehr, bald tummelten sich Kreise, Ellipsen und vieles mehr auf dem Schirm und der Drucker wurde mit einer Hardcopy-Routine zum Glühen gebracht. Schließlich war die vorliegende Turbo-Grafik fertig. Sie besteht aus einer Reihe von Include-Files, die, in den Programmtext eingebunden, ein völlig neues Turbo-Gefühl aufkommen lassen. Es wurde ein modularer Aufbau gewählt, der zwar zu mehr Files führte, aber ein selektives Einbinden der benötigten Funktionen erlaubt: nur das File KERNEL.INC muß eingebunden werden, der Rest nur je nach Bedarf. Im folgenden werden die einzelnen Funktionen der Erweiterung nach Include-Files geordnet vorgestellt. Auf umfangreichen theoretischen Ballast wird verzichtet, wissenschaftliche Hintergründe können vor allem in obiger Serie nachgelesen werden. Also ran an die Tastatur... Als Arbeitsbereich wurde der Bildschirmbereich von 0/0 bis 719/247 (Grafik-Koordinaten) gewählt, was im Textmodus dem kompletten Bildschirm außer der Statuszeile (0/0 bis 89/30 in Textkoordinaten) entspricht. Dies hat zwei praktische Gründe: einerseits bleibt die Statuszeile frei für Meldungen, die nicht in die Bilder gehören, andererseits werden die Pixel der Statuszeile nicht gelöscht, wenn diese Statuszeile eingeschaltet ist. Eine Anpassung an den vollen Bereich des Bildschirms ist zwar möglich, dazu muß aber einiges in den Files geändert werden. Auch sollte Ihr Turbo Pascal an den großen Bildschirm des JOYCE angepaßt werden, da es sonst interessante »Nebeneffekte« geben könnte. Borland liefert Turbo Pascal aus unverständlichen Gründen in einer 80x24-Zeichen-Version aus, während der JOYCE ja 90x32 Zeichen unterstützt. Erreicht wird dies durch einen Durchlauf des Installationsprogramms TINST. Dort sollten die Fragen nach dem Senden von Terminal-Init- und TerminalExit-Sequenzen verneint und die neue Bildschirmgröße angegeben werden. KERNEL.INC ist das Herz unserer Grafik-Erweiterung und muß auf alle Fälle in die Programme
eingebunden werden. GraphInit: Plot(x,y,mode): Screen_Off, Screen_On: HideCursor, ShowCursor: DotColor(x,y):boolean: Symbol(Code,rp0,..,rp7): Symbol(255,'********', '* *', '* *', '* *', '* *', '* *', '* *, '********')ändert also die Darstellung des Zeichens #255 in die angegebene Form. Diese Routine wurde samt der zugehörigen Umwandlungsroutine GX_Convert (wandelt die Binärzeile in einen Bytewert um) mit freundlicher Genehmigung der Autoren aus der CGX-Grafikerweiterung für den PC 1512 übernommen. Hex_Symbol(Code,r0,..,r7):
Dieses Include-File stammt eigentlich noch aus der Anfangsphase der Grafikerweiterung und enthält sehr einfache Grafikbefehle. Wesentlich leistungsfähigere Versionen finden sich in GRAPHLIB.INC, auf Grund ihrer etwas anderen Syntax und dadurch für manche Anwendungen vielleicht besser geeignet, seien sie hier vorgestellt: S_Horizontal(x1,x2,y,mode): S_Vertikal(x,y1,y2,mode): S_Box(x,y,xlen,ylen,mode):
GRAPHLIB.INC Mancher von Ihnen wird sich vielleicht beim Lesen gesagt haben: »Jetzt kann ich also Punkte an beliebige Bildschirmpositionen setzen, sie löschen, invertieren, ihren Status abfragen... Was soll's, damit habe ich noch lange keine Grafik!!!« Richtig, aber die kommt jetzt; Die Prozeduren in GRAPHLIB.INC sind zumindest zum Teil an GSX orientiert. Sie bieten vieles, was der Bildschirmtreiber DDSCREEN.PRL ebenfalls kann; manchmal etwas weniger, manchmal etwas langsamer, aber oft vielseitigere und vor allem variablere Funktionen und — ohne den Rattenschwanz an Funktionen, die man für das Programm, was man gerade schreibt, sowieso nicht braucht, die aber Speicherplatz »fressen«, den man wiederum oft sehr nötig hat. Aus GRAPHLIB.INC kann man sich die entsprechenden Teile wenn nötig einzeln in den Quelltext kopieren. Die Prozeduren und ihre Verwendung Um eine Linie, einen Kreis oder andere Objekte auf den Bildschirm zu zaubern, müssen zunächst alle Punkte berechnet und einer nach dem anderen gesetzt werden. Dafür sind die Prozeduren LINE() bis CIRCLE() zuständig. LINE() zieht eine Linie zwischen den Bildschirmpunkten, deren Koordinaten im Aufruf übergeben wurden. Die einfachste Art dies zu tun, wäre, mit Hilfe der Geradengleichung f(x) = Steigung * x + y-Achsenabschnitt für die xKoordinaten die Werte für f(x) zu berechnen, sie zu Runden, da der Bildschirm ja nur diskrete Schritte erlaubt, und sie anschließend mit PLOT() zu setzen. Da die Berechnung über REAL-Werte erfolgen müßte, wäre dies aber mit Sicherheit auch die langsamste Art. LINE() rechnet nur mit INTEGER-Variablen. Ausgangspunkt ist auch hier wieder die Geradengleichung — diesmal aber ohne Achsenabschnitt. Vielleicht erinnern Sie sich noch mehr oder weniger dunkel an den Mathematikunterricht?! Die Steigung einer Geraden läßt sich aus den Differenzen der Koordinaten zweier Punkte — in unserem Fall von Start und Endpunkt — berechnen. Umgeformt lautet die Gleichung dann: x * DY = y * DX. Mit DX und DY werden bei jedem Schritt die Abweichungen der gesetzten Punkte von der vorgegebenen Gerade bestimmt und zur Auswahl der nächsten Schrittrichtung benutzt. Mehr Details will ich Ihnen und mir ersparen. BOX() bringt ein Rechteck auf den Bildschirm. Übergeben werden die Ecken links oben und rechts unten und — wie auch bei den anderen Prozeduren — der Modus (setzen, löschen oder invertieren). Um die Box nicht unnötig zu bremsen, wird nicht LINE() zum ziehen der Linien benutzt, sondern die Sonderfälle für horizontale und vertikale Linien. POINT_SWAP() sorgt dafür, daß die Startkoordinate immer kleiner als die Endkoordinate ist und nicht über den Rand gemalt wird. SQUARE() ist ein Spezialfall von BOX() und zeichnet — wie zu erwarten — ein Quadrat. Als Parameter werden die linke obere Ecke und die Breite in Pixeln übergeben. Wie wird nun das Quadrat zum Quadrat? Hierfür ist die Konstante ASPECT_RATIO zuständig; sie gibt das Verhältnis von Pixelhöhe zu Pixelbreite an. Ihr Wert kann durch Ausmessen von »Boxen« am Bildschirm ermittelt werden — probieren geht auch hier über studieren. Auch ELLIPSE() und CIRCLE() erhalten durch das Einbeziehen der ASPECT_RATIO erst das gewünschte Aussehen. Ebenso wie die Prozedur LINE() vermeidet auch ELLIPSE() weitgehend REALzahlen. Auch hier wird nur die Abweichung betrachtet, die durch jeden Schritt entsteht. Außerdem macht sich die Symmetrie der Figur nutzbringend bemerkbar: Wir müssen nur ein Achtel aller Punkte berechnen, alle ändern erhalten wir durch Spiegelungen entsprechende Kombination von Addition und Subtraktion der errechneten Werte von den Koordinaten des Mittelpunktes. Radius_x gibt die Anzahl der Pixel der x-Halbachse an, die y-Halbachse wird so umgerechnet, daß das Größenverhältnis auf dem Bild dem gewünschten Verhältnis von Radius_y zu Radius_x entspricht. CIRCLE() wird als Ellipse mit gleich langen Halbachsen berechnet; übergeben wird dementsprechend außer den Mittelpunktskoordinaten nur noch der Radius. Soweit die »Grundfunktionen«. Aber es gibt noch mehr; Die Prozedur PLOT_MARKER() ist vor allem zur Darstellung von Meßwerten u.a. gedacht. Sie setzt auf den Punkt x,y einen »Marker« aus den in MARKER_TYPE deklarierten Elementen. PLOT_MARKER(10,20,Kreis,0) zieht also einen Kreis um den Punkt 10,20. Wenn Sie MARKER_HEIGHT mit im Aufruf übergeben, können Sie auch verschiedene Größen erhalten, was mir persönlich allerdings nicht besonders sinnvoll erschien, da die Marker dann zum Teil doch etwas unförmig werden. Als nächstes folgen vier POLY_...-Prozeduren. Wie der Name vermuten läßt, wird hier etwas mehrfach ausgeführt. Die erforderlichen Koordinaten werden als zweidimensionales Array übergeben; POINT_ARRAY(.i,0.) enthält die x-, POINT_ARRAY(.i,1.) die y-Werte. POINT_ARRAY ist auf 250 dimensioniert, was für die meisten Anwendungen völlig ausreichen dürfte. Die Zahl der wirklich benutzten Arrayelemente wird als Parameter mit übergeben. POLY_PLOT() setzt lediglich Punkte auf die Koordinatenpaare des Arrays; POLY_MARKER() die in MARKER_TYPE definierten Marker. POLY_LINE() verbindet die Punkte in der Reihenfolge, in der sie im Array enthalten sind, mit Linien. Alle Prozeduren können vielseitig eingesetzt werden. Etwa um Funktionen oder Meßwerte darzustellen, wie im Beispiel GRAPHIC.PRO demonstriert, oder um Polygone zu zeichnen. Bei POLY_LINE() muß dann das erste Koordinatenpaar noch einmal als letztes im POINT_ARRAY enthalten sein, damit eine geschlossene Figur entsteht. Ebenso verhält es sich bei der vierten POLY_..-Prozedur POLY_FILL(). Sie füllt ein Polygon, dessen Eckpunkte als POINT_ARRAY übergeben wurden, im entsprechenden Modus, d.h. sie füllt den Inhalt »weiß«. löscht oder invertiert alle Punkte. Allerdings wird es durch die Art der Berechnung notwendig, im Anschluß an das Füllen mit PLOY_LINE() noch die 'Begrenzung' zu zeichnen. Ihre Anwendung im Demo-Programm DREID (entnommen aus PASCAL INTERNATIONAL 2(2), Januar 1987) zeigt am besten den Effekt, den man so erzielen kann: Hidden-LineDarstellungen ohne komplizierte Umrechnungen und große Arrays. Soweit zum Inhalt von GRAPHLIB.INC.
PATTERNS.INC Das Modul PATTERNS.INC bietet neben PATTERN_LINE(), einer erweiterten LINE()-Prozedur, eine analoge Erweiterung von POLY_FILL(), PATTERN_FILL(). Wo in der einfachen Version nur der Modus angegeben werden konnte, werden jetzt die Muster LINE_TYPE und FILL_TYPE übergeben: fünf Linienarten und mindestens neun Füllmuster sind in den beiden Prozeduren verfügbar. Wem dies nicht ausreicht oder wem das Vorhandene nicht gefällt, der kann sich seine eigenen Varianten definieren - als * und Leerzeichen in einem String, einfacher geht's wirklich nicht. Um die Füllmuster zu erhalten, werden die Zeichen 248 bis 255 des Bildschirmzeichensatzes benutzt. Zwar gehen diese dabei für den Gebrauch innerhalb des Programms verloren, aber diese Vorgehensweise hat zwei Vorteile, die dies rechtfertigen. Der erste ist, daß auf diese Weise auch die »normalen« Zeichen als Füllmuster verwendet werden können; z.B. das Leerzeichen (# 32) oder die Zeichen zwischen 128 und 159, die die Rahmen vieler JOYCE-Programme aufbauen. Im Demoprogramm ist die einfache, senkrechte Linie als Beispiel aufgenommen worden. Der zweite Vorteil ist, daß man großflächige »Füllungen« auch mit write(chr(# #)) vornehmen kann, wenn die Felder sich an die Zeilen und Spalten des Textbildschirms halten, was um einiges schneller ist, als der Weg über die Grafik.
GRAPHIC.PRO Zum Abschluß noch einige Worte über die »Programmgestaltung«. Für viele Anwendungen ist es sinnvoller, wenn man im »eigenen Koordinatensystem« arbeiten kann, ohne vorher alle Daten in Systemkoordinaten umrechnen zu müssen. GRAPHIC.PRO stellt als Beispiel eine Prozedur vor, mit der man an beliebige Bildschirmausschnitte, die durch die VIEW_...- Parameter festgelegt werden, ein Koordinatensystem zeichnen kann, in dem ein POINT_ARRAY dargestellt wird. Die Koordinaten der Achsen können als WIN_...-Parameter angegeben werden. Zur Umrechnung der Benutzerkoordinaten in die Systemkoordinaten dient die Prozedur KOORD_TRANS(). Wenn das ganze Programm in Benutzerkoordinaten arbeiten soll, kann man VIEW_X_MIN, VIEW_X_MAX, usw. als globale Variablen deklarieren und beispielsweise über eine Prozedur SET_VIEWPORT(), ähnlich wie CBASIC sie zur Verfügung stellt, verändern. Weiterhin kann man dann in der PLOT()-Prozedur abfragen, ob diese Grenzen überschritten werden, und nur in solchen Fällen die Punkte setzen, in denen sie innerhalb dieser Grenzen liegen. Weist man zusätzlich jeweils die aktuelle Position des 'Grafikcursors' zwei globalen Variablen - C_X_POS und C_Y_POS zu, so ist der Weg offen zur eigenen Turtlegrafik a la LOGO mit Prozeduren wie DRAW_TO usw. oder zur Abfrage der Cursorposition und damit zum Beispiel zu Menueauswahlen wie sie in DR DRAW verwendet werden.
PRINTAT.INC enthält einen wesentlich erweiterten Ausgabebefehl für Turbo Pascal. Mit ihm ist es
möglich, eine Positionierung der Ausgabe mit verschiedenen Schriftarten sowie verschiedenen Ausgaberichtungen zu
verbinden. Die Syntax des neuen Printbefehls ist PRINTAT(x,y,font,heading,text). X und y sind die Koordinaten
(bezogen auf Zeichen-Koordinaten, 0<x<90, 0<y<32) für den Beginn der Textausgabe und entsprechen denen
des Turbo-Standards »gotoxy()«. Font spezifiziert eine von 5 verschiedenen Schriftarten. Im einzelnen sind
5 Schriftarten möglich:
Heading bestimmt die Ausgaberichtung des Textes. Es sind hierbei vier Richtungen möglich, die den Text um jeweils 90 Grad gedreht ausgeben. (Heading 0 = normale Ausgabe, vgl. PTEST.PAS). Der Text kann maximal 90 Zeichen umfassen (Typ Strg_90, definiert in KERNEL.INC). Er wird Zeichen für Zeichen ausgeben, wobei die Zeichenmatrix für jedes Zeichen aus dem Matrix-RAM ausgelesen wird (GX_Get), je nach Bedarf gedreht und anschließend als Zeichen mit dem ASCII-Wert 0 in die Matrix zurückgeschrieben und normal ausgedruckt wird. Die Matrix für dieses Zeichen ist also geändert, Sie sollten in Ihren Programmen den Befehl »write #27,#0« vermeiden. (Oder das entsprechende Sonderzeichen bei Bedarf neu definieren...) Eine Demonstration des neuen Ausgabebefehls finden Sie in PTEST.PAS.
Dieses Include-File verdankt seine Entstehung eigentlich einem Tippfehler bei der Entwicklung von PRINTAT.INC. Es stellt den Befehl SPRINTAT zur Verfügung, der von Syntax und Optionen genau PRINTAT entspricht, nur daß hier die Ausgabe in Spiegelschrift erfolgt...
Grundlage dieser Hardcopy-Routine war ein Listing in PASCAL International, welches jedoch zuerst funktionsfähig gemacht und anschließend erweitert wurde. Mit ihr ist es möglich, einen beliebigen Bildschirmbereich in 4 Größen horizontal oder vertikal auszudrucken. Die Syntax ist Hardcopy(left,top,right,bottom,size,direction). Dabei sind »left« und »top« die Koordinaten des linken oberen, »right« und »bottom« des rechten unteren Pixels, die den Ausgabebereich festlegen. »Size« legt den Vergrößerungsfaktor fest und muß 1,2,4 oder 8 betragen. »Direction« gibt die Ausgaberichtung (»V« oder »H«) an. Zu beachten ist, daß diese Optionen gewissen Einschränkungen unterliegen. Da der JOYCE-Drucker nur 960 Punkte in einer Zeile drucken kann, ist es z.B. nicht möglich, eine Hardcopy des ganzen Bildschirms horizontal in doppelter Größe auszudrucken, weil dazu eine Auflösung von 1440 Punkten nötig wäre. Man sollte also schon vor dem Aufruf etwas rechnen. Ist eine Ausgabe wegen falscher Parameter nicht möglich, bricht die Routine von allein ab. In vertikaler Richtung sind keine Grenzen gesetzt, allerdings kommt es in diesem Modus zu Verzerrungen der Proportionen.
Natürlich möchte man die Grafiken nicht nur auf Papier, sondern zur späteren Verwendung eventuell auch auf Diskette speichern. Hierzu dient das Include-File DISKETTE.INC. Es stellt die Prozeduren SaveScreen (Dateiname) und LoadScreen(Dateiname) zur Verfügung. Als Parameter wird der Dateiname über den vordefinierten Typ Strg_14 übergeben, wodurch neben Namen und Extension auch ein Laufwerk angegeben werden kann. Diese Funktionen übertragen den Bildschirminhalt auf Diskette oder umgekehrt und haben nur einen kleinen Nachteil: sie benötigen einen 22 KByte großen Puffer, der natürlich vom Hauptspeicher abgeht (desgleichen auf der Diskette...). Dafür sind sie allerdings sehr schnell: da der Bildschirm nicht Pixel für Pixel sondern zeichenweise abgetastet wird, ist der Vorgang ca. 64 mal so schnell wie ein Vorgehen mit DotColor und Plot (Diskettenzugriffe vernachlässigt). Der Bidaufbau erfolgt nach dem Einlesen etwa genau so schnell wie eine Ausgabe mit dem write-Befehl. (Evtl. kann man auch mit Screen_Off einen versteckten Bildaufbau erreichen.) Anzumerken ist noch, daß keine Fehlerprüfung über IORESULT erfolgt ( - ein Zeichen des Vertrauens in die Anwender...).
Ähnlich wie mit den Bildern verhält es sich mit den Zeichensätzen. Schön, wenn man sie auch auf Diskette hat. Die in diesem File enthaltenen Prozeduren LoadFont(Dateiname) und SaveFont(Dateiname) ermöglichen das Laden und Speichern von kompletten Zeichensätzen. Die Zeichensätze müßen als 2 KByte großes File vorliegen (dies ist auch die benötigte Puffer-größe). Die *.LET-Files des »Mouse Packs« können direkt verwendet werden, bei mit dem »Character Designer« erstellten Zeichensätzen muß erst der Ladervorspann mit LETCOM entfernt werden.
In Sachen Musik ist auf dem JOYCE ja relativ wenig los, CD-Qualtät kann der eingebaute Beeper beim besten Willen nicht liefern. Etwas mehr als das kurze Piepsen bei der Ausgabe von »^G« ist aber dennoch möglich, wenn man den Beeper direkt ansteuert. Zuständig hierfür ist unser Universal-port 248, über den man den Beeper beliebig ein- und ausschalten kann. SOUND.INC stellt hierzu folgende Funktionen zur Verfügung: Sound_On: Sound_Off: Play(Anzahl,Einzeit,Auszeit):
Technisches Das Listing GRAFINC.PRN [hier als Z80-Quelldatei] zeigt den Assemblercode der External-Routinen GX_Plot, GX_Symb GX_Get und GX_ScrAcc. Sie machen alle vier von der XBIOS-Funktion #35 (Einblenden des Screenenvironments) Gebrauch und leisten folgendes: GX_Plot: GX_Symb: GX_Get: GX_ScrAcc: Mit diesen Informationen ist es möglich, die External-Routinen auch direkt anzusprechen. Dies dürfte besonders für versiertere Programmierer interessant sein, da sich durch die Verwendung von Inline-Code noch eine wesentliche Steigerang der Geschwindigkeit erreichen ließe. Anregungen und Verbesserungsvorschläge aus der Leserschaft sind jederzeit willkommen...
Demo-Programme Zu dieser Grafikerweiterang existieren einige Demos (teilweise noch aus der Entwicklungsphase),
die hier jedoch nicht alle abgedruckt werden können. Auf der DATABOX zum Sonderheft sind sie jedoch komplett als
Quelltexte enthalten. Ein besonderes 'Zuckerl' ist das Programm DREID.*. Es stellt Funktionen als dreidimensionale Netz-Grafiken dar und wurde in PASCAL International 2/87 für den ATARI ST veröffentlicht. Hierzu steht ein eigenes Include-File namens DREID.INC für die Grafikroutinen zur Verfügung. Das Programm läuft jetzt auch auf dem JOYCE und ist nur rund 10 mal langsamer, was angesichts der Tatsache, daß es sich um reines Turbo Pascal handelt, recht beachtlich ist. Es sind 3 Hardcopy-Arten möglich, die nach dem Bildaufbau über <FORM> (=normal), <TAB> (=doppelt hoch) und <EXIT> ( = fett + doppelt hoch) ausgewählt werden. <RETURN> ermöglicht die Eingabe von neuen Winkeln, wird für Beta 0 eingegeben, so endet das Programm.
Ausblick Es dürfte klar sein, daß die vorliegende Version der Grafik-Erweiterung noch nicht unbedingt der Weisheit letzter Schluß ist, aber für den Anfang ist sie bestimmt brauchbar. Daher also viel Vergnügen mit dem neuen Turbo Pascal. Neue Teile dürften bald folgen, wir sind auch auf die Resonanz bei der Leserschaft gespannt. Also bitte keine Zurückhaltung in Sachen Anregungen, Verbesserangen und Anwendungen... (Besonders interessieren würde uns ein Tip, wie man etwas mehr Farbe auf den JOYCE bekommt, denn das ist uns beim besten Willen nicht gelungen...) Was im Augenblick noch in unseren Köpfen heramspukt, wären Erweiterungen in Richtung Sprites und Windows mit Wiederherstellung des Bildschirms sowie eine generelle Geschwindigkeitssteigerang und eine Möglichkeit zur Verwendung mit dem »Mouse Pack« erstellter Bilder. Für diese Sonderheft soll es jedoch genug an Grafik sein, wir wünschen »Auf Wiederlesen« im nächsten Sonderheft oder im Stamm-Magazin... M. Anton / E. Wirth
Bearbeitet von Werner Cirsovius
|