ATOS - Around The Operating System Das ATOS-Magazin 5/96

# Forth-Kurs

In einer früheren Ausgabe der ATOS wurde Ihnen die Sprache FORTH kurz vorgestellt.

Heute möchte ich mit einem kleinen, mehrteiligen Kurs beginnen, der die Grundlagen von FORTH sowie die Systemprogrammierung unter FORTH (GEMDOS, BIOS, GEM, VDI) beleuchtet.

Dieser Ausgabe der ATOS liegt ein komplettes FORTH-System bei, mit dem Sie die Beispiele im Kursus sogleich ausprobieren können. Sie können den Ordner an einen Ihnen genehme Stelle kopieren. Dort sollte sich dann folgende Ordnerstruktur ergeben:

    \FORTH
       forth.ini
       forth.tos
       readme
    \FORTH\BIN
       bios.bin
       dsp.bin
       gemdos.bin
       mint.bin
       vt52.bin
       xbios.bin
    \FORTH\SOURCE
       date.4th
       files.4th
       heap.4th
       hello.4th
       iconify.4th
       sieve.4th
    \FORTH\SYSTEM
       command.4th
       copy.4th
       extend.4th
       util.4th
    \FORTH\GUIDES
       forth.hyp

### Welche Systemvoraussetzungen sind nötig?

Welche Systemvoraussetzungen müssen erfüllt sein, damit FORTH läuft?

- Sie benötigen einen Atari ST, STE, TT, Falcon oder einen kompatiblen Rechner (Medusa, Hades, Janus, ...).

- Die Speicherplatzanforderungen sind gering, 520Kb reichen aus.

- Um gleichzeitig diesen Hypertext und FORTH laufen zu lassen, ist ein Multitaskingbetriebssystem von Vorteil (Mag!C, MultiTos, Geneva). Forth.tos läuft dann in einem Fenster; etwas mehr als 520kb RAM sollten es dann schon sein.

- Für Multitaskinglose empfehle ich das Program TOS2GEM.

### Programmstart

Starten Sie FORTH.TOS (Im Ordner FORTH). Beim Start wird die Datei forth.ini nachgeladen.

    mForth Version 0.95

    (c) Rainer Saric (1995,96)
    (c) Joerg Plewe  (1988)

    Loading: FORTH.INI
       Loading: system\command.4th

    Command set loaded; Vocabulary: commands
    available commands:

    dir del rd md cd ls pwd p: l: k: j: i: h: g: f: e: d: c: b:
    a: rename DTA nil
    23 words in dictionary.

       Loading: system\util.4th
       Loading: system\extend.4th

    Current Directory: \FORTH\SOURCE
       Free ST memory: 1550028  bytes

    NVDI Version: 4.11 Date: 16.03.1996
    Geneva cookie is set to: 872218

    Transient: command
      Current: forth
      Context: command forth gem vdi only

    & 0 ok>

Betrachten wir zunächst die letzte Zeile.

Das Zeichen & zeigt uns die augenblickliche Zahlenbasis (Decimal) an. 0 gibt die Anzahl der auf dem Stack befindlichen Werte bekannt und ok> bedeutet, daß das System von Ihnen eine Eingabe erwartet.

> Hinweis: FORTH-Befehle (Worte) müssen durch mindestens ein Leerzeichen getrennt werden.

Tippen Sie words und anschließend drücken Sie Return (ab jetzt: <cr>). Das Fenster (oder der ganze Bildschirm) wird mit vielen Wörtern gefüllt. Aber keine Angst, zu Anfang sind nicht alle Worte nötig, um einfache Programme zu schreiben.

> Hinweis: Sie verlassen FORTH, indem Sie bye eingeben und <cr> drücken.

Bei FORTH handelt es sich um eine stackorientierte Sprache, die sowohl Compiler als auch einen Interpreter zur Verfügung stellt. Ihre Eingabe von vorhin ("words") wurde interpretiert.

FORTH gehört ebenso zu den 'typenlosen' Sprachen, d.h. der Stackwert kann z.B. als Adresse, als Zahl oder ähnliches interpretiert werden. Letztendlich entscheidet der Programmierer über die Weiterverarbeitung des oder der Werte.

FORTH besitzt einen Datenstack und einen Returnstack, die beide nach dem LIFO-Prinzip arbeiten (LIFO = Last In First Out). Zu Verdeutlichung des Prinzips führen wir nun ein paar einfache Rechnungen durch.

### Rechnen in FORTH

Tippen Sie 1 <cr>, der Forth-Prompt hat sich nun verändert: & 1 OK>.

Auf dem Stack sieht es jetzt so aus:

Stack
1

Geben Sie nun noch z.B. eine 4 ein

Stack
4
1

und jetzt + <cr>

Stack
5

Ist es Ihnen aufgefallen? FORTH rechnet nach der UPN - oder Postfix-Notation (UPN = Umgekehrte polnische Notation; sie ist durch HP-Taschenrechner sehr bekannt geworden). Im Gegensatz zur Infix-Notation gibt man bei UPN erst die beiden Operanden (Zahlen) und dann erst die gewünschte Operation ein.

1 + 4 . liefert eine Fehlermeldung, da der Operator + zwei Zahlen auf dem Stack erwartet.

Schön, wir wissen was 1 + 4 ist und wir wissen auch, daß es oben auf dem Stack liegt (ToS; Top of Stack), trotzdem wäre es schön, das Ergebnis 'schriftlich' zu haben. Dafür gibt es "dot", genauer . Geben Sie also einen . ein und drücken anschließend <cr>. Voila! Addition ist keine Kunst; doch FORTH kann noch etwas mehr.

Unsere nächste Aufgabe ist schon etwas schwieriger: Die Berechnung von Quadratzahlen!

2 2 * . <cr> Einfach! Oder? Das Problem läßt sich jedoch universeller lösen. Zunächst die Lösung des 'Problems':

: qz dup * . ; <cr>

Aber der Reihe nach.

- : schaltet den Compiler ein ( Colon )

- qz ist der Name des neuen Befehls (kann (fast) beliebig sein).

- dup (dupliziere) manipuliert den Stack so, das der Wert zweimal vorhanden ist.

- * führt die Multiplikation aus.

- . gibt das Ergebnis aus

- ; beendet die Definition und schaltet zurück in den Interpreter.

:qz löst einen Fehler aus. (Probieren Sie's aus)

Das neue Wort im Einsatz:

3 qz <cr>

qz ist jetzt Bestandteil des Forthvokabulars. Geben Sie nochmals words ein. Wenn Sie während der Ausgabe eine Taste drücken, wird die Ausgabe angehalten; bei nochmaligem Drücken wir die Ausgabe fortgesetzt - Escape bricht die Ausgabe ab.

Wort-Typen

Einige mathematische Befehle aus dem FORTH Grundwortschatz:

WORD | Stack | Beschreibung
+ | n1 n2 - n1+n2 | Addition
- | n1 n2 - n1-n2 | Subtraktion
* | n1 n2 - n1*n2 | Multiplikation
/ | n1 n2 - n1/n2 | Division
mod | n1 n2 - rest | Rest n1/n2
/mod | n1 n2 - rest quo | Division mit Rest
*/ | n1 n2 n3 - n1*n2/n3 | Mixed Mul/Div
negate | n1 - -n1 | Vorzeichen ändern
abs | n1 - abs(n1) | Absoluter Wert
1+ | n1 - n1+1 | Add 1 zum Top of Stack
1- | n1 - n1-1 | Sub 1 zum Top of Stack
2+ | n1 - n1+2 | Add 2 zum Top of Stack
2- | n1 - n1-2 | Sub 2 zum Top of Stack
2* | n1 - n1*2 | Mul mit 2 (schnell)
2/ | n1 - n1/2 | Div mit 2 (schnell)

### Übung 1

1. Lösen Sie: 120 * 3 / 4 unter Benutzung von zwei Forthbefehlen.

2. Lösen Sie: 120 * 3 / 4 unter Benutzung von nur einem[!] Forthbefehl.

3. Lösen Sie: ((69 + 3) * 5) / 18

4. Schreiben Sie ein neues Forth-Wort, das die Kubikzahl einer Zahl errechnet.

### Lösungen 1

1. 120 3 * 4 / . <cr> 90

2. 120 3 4 */ . <cr> 90

3. 69 3 + 5 18 */ . <cr> 20

4. : ku ( n - n*n*n ) dup dup * * ;

### Wort-Typen

FORTH stellt schon im Grundwortschatz einige Worttypen zur Verfügung:

- Colon-Worte, entstanden aus einer Colon-Definition; Beispiel: qz

- Variablen

- Konstanten

Der häufigste Worttyp ist das Colon-Wort. Theoretisch gibt es unendlich viele Worttypen, da Sie auch eigene Typen definieren können. In einer der nächsten Folgen schauen wir uns den Aufbau einzelner Worttypen genauer an.

### Colon

: <name> ... Befehlsfolge ... ;

Dabei ist <name> des neuen Wortes. Der Doppelpunkt leitet die Kompilation des Wortes ein. Das Semicolon beendet sie wieder, schaltet FORTH vom Compilier- in den Interpretermodus zurück.

### Variable

variable <name> | - | Definition Variable

"variable" ist ein Definitionswort. Es erzeugt eine Variable, indem <name> ins Wörterbuch eingetragen und Platz für den Wert geschaffen wird (4 Bytes). Die erzeugte Variable ist mit 0 initialisiert.

variable x <cr>

Der Zugriff auf die Variable kann, wie nicht anders zu erwarten war, schreibend oder lesend erfolgen.

Das Lesen der Variable erledigt das Wort @ (fetch): x @ . <cr> 0 Das Schreiben gelingt mit dem Wort ! (store): 123 x ! <cr>

In einer colon-Definition:

    : get-x ( -- x )  x @ ;

Speicherbefehle:

WORD | Stack | Beschreibung
@ | adr - n | Liest 32bit-Wert aus adr aus
! | n adr - | Schreibt 32bit-Wert in adr
w@ | adr - w | Liest 16bit-Wert aus adr aus
w! | w adr - | Schreibt 16bit-Wert in adr
( | - | Kommentar bis zur nächsten schließenden Klammer ")"

### Konstanten

constant <name> | n - | Definition Konstante

"constant" bewirkt einen Wörterbucheintrag von <name>, reserviert Platz und weist der Konstante den auf dem Stack befindlichen Wert zu.

100 constant hundert <cr>

Bei einem Aufruf von hundert wird jetzt sofort die Zahl 100 auf dem Stack abgelegt.

Mit einem kleinen Trick läßt sich der Wert von Kostanten jederzeit ändern - sinnvoll ist dies allerdings nicht.

### Stackmanipulationen

Da sich in FORTH alles um den Stack dreht, ist die Auswahl an Wörtern, die den Stack manipulieren, recht groß. Wir dürfen nämlich nicht erwarten, das Worte ihre Werte immer so auf dem Stack übergeben, daß das folgende Wort sie ohne weiteres übernehmen kann (Systemaufrufe). Dabei können wir noch Worte unterscheiden, die die Stacktiefe vergrößern, verändern oder die Anordnung ändern.

Die wichtigsten Worte zur Stackmanipulation sind swap, dup, over und drop.

FORTH stellt ein Servicewort zur Verfügung mit dem Sie sich den Inhalt des Stacks komplett ansehen können: .s (dot-s). Als Test geben Sie ein paar Zahlen ein und anschließend .s

Beispiel:

Stack vor Aufruf von swap

Stack
1
3

Stack nach Aufruf von swap

Stack
3
1

WORD | Stack | Beschreibung
.s | - | Inhalt des Stack ausgeben
sp0 | - addr | Startadresse des Stack
sp@ | - addr | Stackpointer-Inhalt vor sp@-Aufruf
sp! | - | Stackpointer initialisieren
depth | - n | Stacktiefe vor Aufruf von depth
over | n1 n2 - n1 n2 n1 | Kopiert 2. Stackwert auf TOS
rot | n1 n2 n3 - n2 n3 n1 | Macht 3. Eintrag zum TOS
drop | n - | Löscht TOS
dup | n - n n | Kopiert TOS
?dup | n - n n oder n - n | Dupliziert wenn n<>0
pick | ... n i - n n.i | Kopiert i.ten Eintrag auf TOS

Haben sich viele Zahlen auf dem Stack angesammelt, können Sie diese löschen, indem Sie ein unbekanntes Wort eingeben - oder alternativ dot'ten, bis der Stack leer ist.

### Logische Grundoperationen

Bei den logischen Grundoperationen handelt es sich um bitweise Verknüpfung von Bitmustern nach festen Regeln.

A B | A and B | A or B | A xor B
0 0 | 0 | 0 | 0
0 1 | 0 | 1 | 1
1 0 | 0 | 1 | 1
1 1 | 1 | 1 | 0

Die FORTH-Befehle and, or, xor und not wirken diesen Regeln entsprechend (bei mForth 32bit breit)

Beispiele:

- 3 1 and . <cr> . 1

- 4 2 or . <cr> . 6

- -1 not . <cr> . 0

### Vergleichsoperatoren

Vergleichsoperationen liefern als Ergebnis ein Flag auf dem Stack, das entweder den Wert 0 (FALSE) ober -1 (TRUE) annimmt.

Vergleichsoperatoren mit 0

WORD | Stack | Beschreibung
0= | n - flag | -1, wenn n=0
0> | n - flag | -1, wenn n>0
0< | n - flag | -1, wenn n<0
0<> | n - flag | -1, wenn n<>0
0>= | n - flag | -1, wenn n>=0
0<= | n - flag | -1, wenn n<=0

WORD | Stack | Beschreibung
= | n1 n2 - flag | -1, wenn n1=n2
> | n1 n2 - flag | -1, wenn n1>n2
< | n1 n2 - flag | -1, wenn n1<n2
<> | n1 n2 - flag | -1, wenn n1<>n2
>= | n1 n2 - flag | -1, wenn n1>=n2
<= | n1 n2 - flag | -1, wenn n1<=n2

Beispiele:

1 0= . <cr> 0
0 0= . <cr> -1
1 3 < . <cr> -1
3 1 > . <cr> -1

### Entscheidungen

Der Wert einer Programmiersprache hängt natürlich auch von den Möglichkeiten der Programmablaufsteuerung ab. FORTH bietet hier genug Worte und darüber hinaus die Möglichkeit, den Entscheidungsatz zu erweitern.

Die bekannteste Entscheidungsstruktur ist sicherlich die if ... else ... endif Struktur.

if <true-Teil> else <false-Teil> endif
if <true-Teil> endif

Die if .. endif-Struktur kann natürlich geschachtelt werden. Bei diesen Verschachtelungen muß jedoch auf folgendes geachtet werden:

- es müssen genauso viele if wie endif vorhanden sein

- if und endif müssen Klammerpaare bilden.

- else und endif sich auf das letzte if beziehen.

Beispiel:

    if
       if

       endif

    else

       if

       else

       endif

    endif

Ein Wort soll testen, ob die auf dem Stack befindliche Zahl gerade oder ungerade ist:

: | ?gerade ( n - )
| 1 and /* <>0, wenn Zahl ungerade */
| if ." Zahl war ungerade!"
| else ." Zahl war gerade!"
| endif ;

WORD | Stack | Beschreibung
/* | - | Ignoriere Text bis */
." | - | Kompiliert String ins Wörterbuch

### Erste Exkursion ins ST-Betriebssystem

Selbst mit den wenigen Worten, die wir bislang kennengelernt haben, können wir uns an das VDI-Interface wagen.

Schauen wir uns vorab die zur Verfügung stehenden Worte des VDI an. Geben Sie Sequenz mforth vdi words ein (es sind noch nicht alle VDI-Aufrufe implementiert).

Hier die Erläuterung der einzelnen Befehle:

- mforth stellt die Suchreihenfolge ein

- vdi macht das vdi Vocabular zum current Vocabular und

- words schließlich listet die Worte.

sf_updat sf_perimeter sf_color sf_style sf_interior st_alignment st_effects st_color st_font st_rotation st_point st_height sm_color sm_height sm_type sl_ends sl_color sl_width sl_udsty sl_type s_color swr_mode justified rfbox rbox ellpie ellarc ellipse circle pie arc bar recfl contourfill fillarea gtext pmarker marker pline line qt_extent qt_attributes qf_attributes qm_attributes ql_attributes q_color q_extnd r_trnfm ro_cpyfm ro_cpyfm s_clip st_unload_fonts st_load_fonts updwk clrwk clsvwk opnvwk clswk opnwk v_handle vq_gdos !intin !ptsin str>intin

Zu Beginn müssen wir eine virtuelle Workstation öffnen:

1 1 1 1 1 1 1 1 1 1 2 opnvwk <cr>

Die genaue Bedeutung der Parameter ist im Profibuch nachzulesen (ich möchte an dieser Stelle nicht näher darauf eingehen). opnvwk füllt die Werte in das interne INTIN-array. Die Rückgabewerte befinden sich im INTOUT-array (intin und intout sind im GEM-Vocabular definiert).

Jetzt müssen wir das vdi-handle holen und in der Variablen v_handle sichern:

contrl 12 + w@ v_handle w! <cr>

v_handle sollte > 0 sein. v_handle w@ . <cr>

Wenn Sie FORTH jetzt verlassen, müssen Sie vorher den Befehl clsvwk ausführen! Warum? Sie haben eine virtuelle Workstation geöffnet (opnvwk = open virtual Workstation) und sollten sie jetzt vor dem Verlassen wieder schließen (clsvwk = Close Virtual Workstation). Damit räumt man quasi den Arbeitsplatz, den man vorher eingerichtet hat, wieder auf.

Ein paar Worte zum Ausprobieren:

- 0 0 200 200 line

- 100 100 300 300 bar

- clrwk

- 100 100 20 circle

WORD | Stack | Beschreibung
line | x y x' y' - | Linie zeichnen von x,y -> x',y'
bar | x y x' y' - | Gefüllte Box zeichnen von x,y -> x',y'
clrwk | - | Workstation löschen
circle | x y radius | Kreis zeichnen
sl_width | width - | Liniendicke ändern
sl_color | color - | Linienfarbe ändern

### Eine kleine Anwendung: Turtle-Grafik

Nachfolgend erfolgt die etwas ausführlichere Erläuterung des kleinen Grafikpakets. Ein paar neue Worte sind dabei, lassen Sie sich dadurch bitte nicht verunsichern. In den nächsten Folgen werden die Grafikfunktionen um einen 2D- und 3D-Teil ergänzt.

Um Ihnen Tipparbeit zu ersparen, empfehle ich Ihnen, den Text hier im Hypertext zu markieren und in das Clipboard zu kopieren. Scrap.txt kann dann mit jedem ASCII-Editor ediert werden. Speichern Sie den Text unter \.\forth\source\turtle.4th ab.

Sie können das File in FORTH einladen mit der Befehlssequenz:

include \.\forth\source\turtle.4th

So jetzt aber los:

    /* -------------
       Turtle-Grafik
       -------------
       Original-Code von R. Zech, angepaßt an mForth
    */

    mforth vocabulary graphic /* Eine eigenes Vokabular für die Befehle */
    graphic also definitions  /* .. alle Definition ins graphic Voc */

    /* x,ycenter sind systemspezifisch, Initialisierung mit ST-Hoch */
    integer xcenter   320 to xcenter
    integer ycenter   200 to ycenter

    2variable center
    2variable pix_cur          /* x,y-Position der Kröte */
     variable angle            /* Richtung, in der unsere Kröte kriecht */
    create crt_bounds 8 allot  /* Ausmaße des Bildschirms/Fensters */

    /* Wir definieren ein paar Farben für die Linien */
    variable colors /* Anzahl der Farben */
    : color: ( -- <name> )
       create w, does> w@ sl_color ;

    0 color: white
    1 color: black
    2 color: red
    3 color: green
    4 color: blue
    5 color: cyan
    6 color: yellow
    7 color: magenta
    8 color: gray
    9 color: darkgray
    hide color: white  /* color: aus dem Wörterbuch "entfernen" */

    /* Verschieben des Koordinatensystems */
    : shift  ( deltay deltay -- )
       negate center 4+ +! center +!
       center 2@ pix_cur 2! ;

    /* Turtle bewegen ohne irgendwelche Spuren, sprich Linien, zu
    hinterlassen */
    : skip-xy ( deltax deltay -- )
       negate pix_cur 4+ +! pix_cur +! ;

    /* Absolut setzen */
    : goto-xy ( x y -- )
       center 2@ pix_cur 2! skip-xy ;

    : home ( -- ) /* Zurück zum Ursprung */
       0 0 goto-xy angle off ;

    : new  ( -- )
       xcenter ycenter center 2!
       home black ;

    : turn   ( winkel -- ) angle +! ;
    : turnto ( winkel -- ) angle  ! ;

    /* Der Sinus wird aus einer Tabelle ausgelesen, Genauigkeit 5-Stellen
    skaliert mit 10000.
    Dies ist die weitaus schnellste Methode. Die Genauigkeit reicht für
    die meisten Anwendungen aus. So habe ich, mit der um 3D-Funktionen
    erweiterten Turtlegrafik, Drahtmodelle chem. Verbindungen in hoher
    Geschwindigkeit drehen können */
    : skip ( radius -- )
       dup  angle @ cos * 10000 /
       swap angle @ sin * 10000 / swap skip-xy ;

    : goto ( radius winkel -- )
       angle ! center 2@ pix_cur 2! skip ;

    : dot   ( -- ) /* Punkt an Turtleposition zeichnen */
       pix_cur 2@ 2dup line ;
    : draw  ( radius -- )  /* Eine Linie mit dem Radius radius zeichnen */
       >r pix_cur 2@ r> skip pix_cur 2@ line ;

    /* Initialisieren, init muß(!) vor dem ersten draw-Befehl aufgerufen
    werden */
    : init ( -- )
       1 1 1 1 1 1 1 1 1 1 2 opnvwk /* Virtuelle Workstation öffnen */
       contrl 12 + w@ v_handle w!   /* Handle holen und sichern */
       intout 26 + w@ colors !      /* Anzahl der Farben */
       0 0 intout 2w@ crt_bounds 4w! /* Ausmaße des Bildschirms */
       intout 2w@ 2/ to ycenter      /* Center neu */
                  2/ to xcenter ;

    /* Aufrufen, bevor FORTH verlassen wird */
    : xinit ( -- )  clsvwk ;

    mforth
    bload vt52.bin >voc vt52
    vt52 also graphic also

    /* Eine Box mit der Turtle zeichnen */
    : BOX ( laenge breite -- )
       over [ graphic ] draw 90 turn dup draw
       90 turn swap draw 90 turn draw
       -270 turn ; /* Turtle wieder zurück */

    : wonder ( -- )
       new 45 turnto
       200 0 do i draw 90 turn
                i 7 mod sl_color
             loop black ;

    : stern  ( -- )
       361 0 do 100 draw 0 0 goto-xy i turnto loop ;

    : demo   ( -- )
       init new clrwk
       [ vt52 ] home ." Eine Box"
       100 100 BOX key drop
       clrwk [ vt52 ] home ." Ein Stern"
       new stern   key drop
       clrwk [ vt52 ] home ." I wonder"
       new wonder  key drop
       cr ." Vor Verlassen des FORTH-Systems bitte xinit aufrufen!" ;

Ich wünsche Ihnen viel Spaß beim Experimentieren.

### Aufbau eines Forth-Wortes

Ein FORTH-Wort ist ein in sich geschlossenes Modul, das in das Wörterbuch eingebunden ist und von von dort aus aufgerufen werden kann. Ein FORTH-Wort besteht aus zwei Teilen, dem Wort-Header und dem Wort-Body. Im Body des Wortes ist die eigentliche Funktion abgelegt (mForth unterscheidet sich hier etwas von anderen Systemen):

Feldname | Länge in Byte
Link Field | 4
Word Parameter Field | 4
Code Field Adress | 4
Name Field | >= 4
Code Len Field | 2
Parameter Field | >= 2

- Im link field ist die Adresse zum nächstälteren Wort gespeichert.

- Das word parameter field enthält ein Bitmuster, das das Verhalten des Wortes bestimmt, z.B. ob es immediate ausgeführt werden soll.

- Die code field adress enthält einen Zeiger auf das parameter field.

- Das name field hält den Name des Wortes.

- Das code len field enthält die Länge des Wortes in 16bit Worten und dient zur Erstellung von Standaloneprogrammen.

- Das parameter field schließlich enthält den Code (und) oder die Daten eines Wortes.

Die Länge der NFA und der PFA sind wortabhängig.

### Ausblick

Ich hoffe, der erste Teil war interessant genug, daß Sie auch in den zweiten Teil des Kurses reinschauen. Sie erfahren unter anderem etwas über:

- Ausgabe

- Strings

- Schleifenverarbeitung

- Forthvocabulare

Da dies mein erster "Lehrversuch" ist, möchte ich Sie bitten, etwas Nachsicht zu üben, wenn Unklarheiten aufgetreten sind. Für Anregungen und Fragen bin ich Ihnen dankbar.

Anrufen (06431-71188 ab 20:00 Uhr) oder schreiben.

EMail: Maus-Adresse von Rainer Saric

RS