Der erste Schritt

Die Befehle kennenlernen

C64 Studio, AMCE & TASM

Hier dreht sich alles um den Befehlssatz des 6510. Wie bereits erwähnt, gilt dieser auch für den 6502. In Zukunft werde ich nur vom 6510 reden und einzig bei Bedarf den 6502 erwähnen. Wir werden uns Stück für Stück den Befehlssatz erarbeiten.

Da der 6510 ein 8-Bit Prozessor ist, steht ihm je Befehl ein Byte zur Verfügung. Somit kommt man auf max. 256 unterschiedliche Befehle. Aber längst nicht jedes Byte steht für einen gültigen Befehl. Viele Befehle haben außerdem unterschiedliche Adressierungsarten, die unterschiedliche Bytecodes besitzen. So gibt es z. B. allein acht verschiedene Versionen vom LDA-Befehl, je nachdem, wie die Adressierung vorgenommen wird. „Wirkliche“ Befehle gibt es nur 56 und außerdem noch einige undokumentierte.

Wir werden uns das ASM-Programm vom Basic-Vergleich noch mal anschauen.

Startet jetzt euren Editor. Nun solltet ihr auch spätestens einen Blick auf die Einrichtung eurer Entwicklungsumgebung werfen und wissen, wie man damit Programme erstellt und startet. Beim Turbo Assembler auf dem C64, solltet ihr im Auge behalten, dass sich die Syntax hin und wieder unterscheidet. Beim ersten Auftreten, erwähne ich diese Unterschiede im Tutorial, gesammelt findet ihr diese im Turbo Assembler-Beitrag.

Gebt nun das folgende Programm ein:

Turbo Assembler:Dieses Programm hatten wir ja schon bei den Grundlagen, schaut daher bei Bedarf nochmal dort nach, was alles zu beachten ist.

Nehmen wir uns dieses kleine Programm jetzt mal Zeile für Zeile vor.

Diese Angabe ist kein Befehl für den Prozessor, sondern teilt dem Assembler mit, an welcher Speicheradresse die nächsten Anweisungen stehen sollen. Dafür gebt ihr ein *= gefolgt von der Adresse (ich benutze die Hex-Schreibweise, ihr könnt aber auch Dezimalzahlen verwenden) ein.
Ab $0800 beginnt zwar das BASIC-RAM (s. „Kleine Hardwarekunde“), wie ihr aber eben gesehen habt, wollen wir die nächsten BYTEs erst ab $0801 ablegen. Dies ist die Adresse, an der die erste BASIC-Zeile erwartet wird. $0800 ist für uns tabu! Dort muss immer eine 0 stehen, sonst kommt es zu einer Fehlermeldung, wenn man RUN eingibt, um das Programm zu starten. Daher beginnen wir mit $0801.

!byte ist auch kein Befehl, es ist ein Schlüsselwort für den Assembler. Es weist den Assembler an, die folgenden Werte, direkt als Bytes im Speicher abzulegen. Hier generieren wir eine BASIC-Zeile für den Programmstart.
Wer nach Laden des Programms mal LIST eingibt, bekommt unsere Startzeile zusehen:

Schauen wir uns diesen BASIC-Start mal genauer an…

$0b, $08 : Hier haben wir die Adresse der nächsten BASIC-Zeile. Bei uns gibt es zwar keine, aber wir müssen diese Adresse trotzdem angeben. Ausrechnen können wir die Adresse, indem wir zu unserer Startadresse $0801 (s. o.) die Anzahl der von uns für die BASIC-Zeile benötigten Bytes (inkl. den beiden Bytes für diese Adresse, aber ohne die drei $00 am Schluss) addieren.
Adressen werden als 16-Bit (Word) gespeichert. Dabei erwartet der 6510 zuerst das niederwertige Byte (LSB) im Speicher, dann folgt das MSB (höherwertiges Byte)helpErklärungLSB = Least Significant Byte
MSB = Most Significant Byte
Die Groß-/Kleinschreibung ist wichtig, große Buchstaben stehen für BYTE, kleine für Bit.
. Also zeigen wir hier auf die Adresse $080b = $0801 + 10 Bytes.

$e2, $07 : Das ist unsere BASIC-Zeilen-Nr. als Hex-Zahl, da diese 16-Bit groß ist, wird auch hier wieder erst das LSB, dann das MSB im Speicher abgelegt. Wer $07e2 ins Dezimalsystem umrechnet, kommt auf unsere Zeilen-Nr. 2018.

$9e : Das nächste Byte steht für den BASIC-Befehl SYS, der BASIC-Interpreter weiß dadurch, zu welcher Routine er springen muss. Dieses Byte bitte nicht mit einem Opcode verwechseln!

$20, $32, $30, $36, $32 : Diese fünf Bytes stehen einfach für den Text hinter dem SYS-Befehl. Wenn ihr eine PETSCII-Tabelle zu Rate zieht, kommt ihr auf folgende Zeichen:

$20 = Leerzeichen
$32 = 2
$30 = 0
$36 = 6
$32 = 2

$00, $00, $00 : Diese drei Bytes definieren das Ende des BASIC-Programms. Sie beginnen bei unserer oben errechneten Adresse $080b in der eigentlich die nächste BASIC-Zeile steht. Aber durch diese drei $00 erkennt der BASIC-Interpreter das Ende des Programms und verhindert, dass es bei der Ausführung am Schluss evtl. einen Fehler gibt bzw. dass wir beim LIST Unfug angezeigt bekommen. Ohne dieses Ende würde unser Programm, das im Speicher ja direkt auf die erste BASIC-Zeile folgt, als weitere BASIC-Zeilen interpretiert werden.
Wenn man diese drei Bytes mal weg lässt, läuft unser Programm nicht mehr richtig:

Das Programm wird direkt ohne Ausgabe beendet.
Nach einem LIST sehen wir auch warum…

Leerzeilen dienen nur der Übersicht, diese könnt ihr je nach eurem Geschmack zur Formatierung eures Sourcecodes einsetzen.

Hier ist es endlich, unser erstes Mnemonic: LDA (die Groß- & Kleinschreibung ist beim C64 Studio und ACME übrigens egal, der Turbo Assembler zwingt euch zur Kleinschreibung).

LDA : LoaD Accumulator (lade / setze einen Wert in den Akku).
Um den Akku zu füllen, gibt es, wie eingangs erwähnt, acht unterschiedliche Möglichkeiten. Die hier verwendete nennt sich unmittelbare Adressierung, weil wir einen festen Wert #$41 (für das Pik-Symbol) direkt in den Akku schreiben. Das #$ ist hier sehr wichtig! Vergesst ihr die Raute # dann erkennt der Assembler $41 als Adresse! Ohne $ wird die Zahl als Dezimal interpretiert und ihr seht dann ein anderes Symbol auf dem BS. Bei einer unmittelbaren Adressierung benötigt LDA 2 Byte Speicher und die CPU braucht zwei Taktzyklen, um den Befehl zu verarbeiten. Der eindeutige OpCode für LDA mit unmittelbarer Adressierung lautet $A9. Außerdem werden das Negativ- und Zero-Flag beeinflusst.

In Zukunft gebe ich bei den Befehlen Opcode, Speicherbedarf, Taktzyklen und die betroffenen Flags in Kurzform an.

Hier lautet die Kurzform:
LDA unmittelbar ($A9, 2B, 2T, NZ)
.

Alle Details hierzu findet ihr auf der Seite Mnemonics.

LDX : LoaD X-Register (lade / setze einen Wert im X-Register / Indexregister-X)
LDX unmittelbar ($A2, 2B, 2T, NZ)
Der C64 besitzt zwei Indexregister die X– und Y-Register genannt werden. Diese Register werden häufig (so wie hier) für Schleifen verwendet, da sich deren Wert einfach verringern läßt, wie wir gleich noch sehen. Die Indexregister sind dem Akku von der Handhabung sehr ähnlich. Wir verwenden hier, wie beim LDA, die unmittelbare Adressierung um im X-Register unsere gewünschte Schleifenanzahl #$ff (255) zu hinterlegen (auch hier wieder auf #$ achten).

Wir haben jetzt also unser ?-Symbol im Akku und unsere Schleifenanzahl im X-Register. Als nächstes folgt der Hauptblock unseres Programms, wir geben die Zeichen mit einer Schleife auf dem Bildschirm aus.

loop ist wieder mal kein Assemblerbefehl, sondern ein Label. Diese Label werden vom Assembler verwendet, um eine bestimmte Adresse im Speicher zu finden. Ihr könnt das Label auch direkt vor den entsprechenden Befehl schreiben, ich empfinde das aber als unübersichtlich. Wir könnten Adressen auch selbst ausrechnen, dass ist aber sehr arbeitsaufwändig (wenn ihr Befehle hinzufügt verschieben sich die Speicherpositionen) und fehleranfällig (wie leicht vertipp man sich und schon sucht man ewig nach einem Fehler im Programm).
Hier kennzeichnet loop den Beginn unserer Schleife. Solange die von uns gewünschte Anzahl an Durchläufen (siehe LDX) noch nicht erreicht ist, wollen wir immer wieder mit dem folgenden Befehl fortfahren.

STA: STore Accumulator (speichere den Inhalt des Akkus in…)
STA absolut X-indiziert ($9D, 3B, 5T, <keine>)
Zu Beginn unserer Schleife, wollen wir das ?-Zeichen auf dem BS ausgeben. Wir verwenden dafür STA, um den Inhalt des Akkus, in die angegebene Speicherstelle (hier der BS-Speicher) zu schreiben. Adressen werden (wie oben erwähnt) im Format $03ff angegeben (hier also ohne #). Da wir nicht immer auf die selbe Adresse schreiben wollen, müssen wir noch dafür sorgen, dass wir nacheinander die von uns gewünschten Speicherzellen beschreiben. Das erreichen wir durch die Angabe von ,x hinter unserer Adresse $03ff. Damit weiß der Assembler, dass er die absolute Adressierung mit Hilfe des X-Registers nehmen soll. Wenn der Befehl abgearbeitet wird, addiert die CPU auf die feste Adresse (hier $03ff) den jeweils aktuellen Inhalt des X-Registers. Da wir dieses oben mit #$ff gefüllt haben und es gleich verringern werden, geben wir die Zeichen also „rückwärts“ auf dem BS aus. Wir beginnen bei $03ff + #$ff = $04fe, dann $04fd, $04fc usw. Das X-Register ist bei uns nie kleiner als 1 (s. unten), wenn STA ausgeführt wird. Somit erklärt sich jetzt auch für alle, die in der Hardwarekunde aufgepasst haben, wieso wir die Adresse $03ff verwenden, obwohl der BS doch bei $0400 beginnt: $03FF + 1 = $0400.

DEX: DEcrement X-Register (den Inhalt vom X-Register um 1 verringern)
DEX implizit ($CA, 1B, 2T, NZ)
Nachdem wir eben einen sehr „großen“ und zeitaufwendigen Befehl hatten, folgt nun ein schön schlanker und einfacher. Der Befehl DEX zieht einfach vom Inhalt des X-Registers 1 ab. Es gibt nur diese eine Schreibweise, da hier keine Adressierungen möglich / nötig sind. Das Zero- und Negativ-Flag werden hier beeinflusst. Ist die Zahl nach dem DEX negativ, dann wird das N-Flag gesetzt (1), anderenfalls wird es gelöscht (0). Ist die Zahl genau 0, dann wird das Z-Flag gesetzt (1), sonst wird es gelöscht (0). Das führt uns direkt zum nächsten Befehl.

BNE: Branch on Not Equal (springe wenn nicht gleich zu…)
BNE relativ ($D0, 2B, 2-4T, <keine>)
Dieser Befehl ist wieder etwas komplexer. BNE ist mit einer IF X <> Y THEN GOTO Anweisung aus BASIC vergleichbar. Unter Assembler gibt es insgesamt acht solcher bedingten Sprünge. Damit sind Befehle gemeint, die ein Statusregister prüfen und dann entweder zu einer anderen Speicherstelle springen oder mit der nächsten Anweisung fortfahren. Ob etwas gleich oder ungleich ist, wird über das Zero-Flag geprüft (1 bedeutet gleich und 0 ungleich, zieht man z. B. zwei Zahlen von einander ab und das Ergebnis ist 0, dann sind diese Zahlen offensichtlich gleich). Dementsprechend springt BNE zur angegebenen Adresse, wenn das Zero-Flag gelöscht (Z=0), die letzte Anweisung also nicht gleich (bzw. null) ist. Da wir gelernt haben, dass eine Adresse 16-Bit lang ist, wundert ihr euch evtl. warum BNE nur 2 Byte Speicher benötigt. Das liegt daran, dass die sog. relative Adressierung verwendet wird. Der OpCode benötigt wie gewohnt ein Byte. Das zweite Byte wird für die relative Adressierung verwendet. Dabei gibt das vorzeichenbehaftete Byte an, um wieviele Bytes vor oder zurück gesprungen werden soll. Ihr könnt also max. um 127 Byte nach vorne (zu höheren Adressen) oder um -128 Byte zurück (zu niedrigeren Adressen) springen. Wer die Taktzyklen zählen möchte, der muss noch weitere Besonderheiten beachten. Wie ihr oben sehen könnt, habe ich die Ausführungszeit mit 2-4T angegeben. 2T werden benötigt, wenn der Befehl nicht springen muss und es einfach mit der nächsten Einweisung weitergeht. Muss ein Sprung ausgeführt werden, dann werden 2T+1T benötigt, erfolgt der Sprung über eine Page(Seiten)-Grenze, dann wird ein weiterer Zyklus benötigt, also 2T+1T+1T. Solltet ihr euch jetzt fragen, was springen eigentlich bedeutet, nun das ist ganz einfach. Damit ist nichts Anderes gemeint, als den Programcounter (PC / Programmzähler) auf die angegebene Zieladresse zusetzen, damit es mit dem dortigen Befehl weitergeht.

Jetzt haben wir es fast geschafft, es folgt der letzte Befehl aus unserem Programm.

RTS: ReTurn from Subroutine (springe aus der Unter(Hilfs)-Funktion zurück zum Aufrufer)
RTS implizit ($60, 1B, 6T, <keine>)
Der Befehl springt zu der aktuell auf dem Stack zu findenden Adresse (implizite Adressierung) zurück. Normalerweise wird diese Adresse vom JSR-Befehl (folgt später) auf dem Stack abgelegt. Wie ein Blick aufs Listing verrät, gibt es den in unserem Programm aber nicht!?! Bei uns steht dort eine Rücksprung-Adresse zum BASIC, die beim Programmstart auf dem Stack abgelegt wurde. Wir landen nach dem RTS also wieder mit dem blinkenden Cursor in der Eingabe des C64. Da RTS einfach die aktuelle Adresse vom Stack nimmt, ist es immens wichtig, dass wir dafür sorgen, dass der Stack die richtige Adresse enthält. Damit beschäftigen wir uns später beim JSR-Befehl.


So, das wars fürs Erste an Befehlen. Ihr solltet damit etwas experimentieren. Keine Angst, der C64 nimmt keinen Schaden, falls ihr mal Kraut und Rüben produziert. Es kommt höhstens mal vor, dass ihr Reset drücken oder den Rechner gar ausschalten müsst.

Kommentare und Variablen

Lasst uns unser Programm noch etwas verbessern. Damit wir auch in drei Jahren noch wissen, was wir uns bei bestimmten Programmstellen gedacht haben, sind Kommentare ratsam. Ein Kommentar ist nur für den Leser des Sourcecodes gedacht. Er hat keine Auswirkung auf das fertige Programm. Erstellt ihr eure Programme auf dem PC, dann könnt ihr hemmungslos Kommentare einsetzen. Auf einem echten C64 sieht das schon anders aus. Da auch beim Schreiben des Programmes im Editor, wie immer der Speicher knapp ist, müsst ihr im Hinterkopf behalten, dass auch Kommentare Platz benötigen. Einen Kommentar leitet ihr durch ein Semikolon ein. Alles was danach folgt, wird bei der Programmerstellung ignoriert.

Beim Turbo Assembler müsst ihr darauf achten, dass Kommentare nur für sich alleine in einer Zeile stehen dürfen. Beim C64 Studio und ACME können die Kommentare auch hinter Befehlen stehen.

Variablen

Durch Variablen bzw. Konstanten könnt ihr die Pfleg- und Lesbarkeit eurer Programme verbessern. Ihr könnt euch dazu einfach einen Namen ausdenken und diesem einen Wert zuweisen.

oder

Ich schreibe die Namen hier absichtlich groß, damit die im Source besser zu erkennen sind. Alles andere schreibe ich in der Regel klein. Beim Turbo Assembler müsst ihr die Namen aber in Kleinbuchstaben schreiben!

Zum Schluß noch unser überarbeitetes Programm:

Beim Turbo Assembler sieht es so aus:

Turbo Assembler – Kommentare und Variablen

Schrott!!Naja...Geht so...Ganz gut...SUPER! (27 Bewertungen | Ø 4,81 von 5 | 96,30%)

Loading...


ZurückWeiter

17 Gedanken zu „Der erste Schritt“

  1. Hallo Jörn,
    ich habe ein kleine Frage zum Video und dem Assembly Dump
    Im Video zeigt der Assembly Dump in Zeile 14 den Speicherwert FF 03 (Also Adresse $03FF)
    Das ist der Wert der variable „Screen“ -1, aber dazu wird doch noch der Wert des x-Registers addiert.
    Wie passt das zusammen? Hätte da nicht irgendwas zwischen 00 04 und FF 04 stehen müssen?

    LG Jens

    1. Hallo Jens,
      der Befehl lautet sta $03ff,x. Im Speicher und daher auch im Dump, wird immer $9d $ff $03 stehen. Den Inhalt des X-Registers addiert die CPU intern zur angegebenen Adresse, der Befehl ändert sich dadurch nicht.

      Ich hoffe, ich habe mich verständlich ausgedrückt.

      Gruß,
      Jörn

  2. Pingback: Linksammlung C64
  3. „[…]Der Befehl DEX zieht einfach vom Inhalt des X-Registers 1 ab.[…]
    Ist die Zahl genau 0, dann wird das Z-Flag gesetzt (1), sonst wird es gelöscht (0). “

    //Also ist X=1, dann DEX …, dann ist X=0 und Z-Flag=1 ?

    „[…]Ob etwas gleich oder ungleich ist wird über das Zero-Flag geprüft
    (0 bedeutet gleich und 1 ungleich, zieht man z. B. zwei Zahlen von einander ab und das Ergebnis ist 0, dann sind diese Zahlen offensichtlich gleich).[…]“

    //Also wenn ich X=1 und DEX mache, ist das Ergebnis 0. Weil die Zahlen sind ‚gleich‘
    //Aber das Z-Flag wird gesetzt, also Z-Flag=1. Aber dann bedeutet Z-Flag=1 ja ‚ungleich‘?!?
    //Aber die Zahlen waren doch grade noch ‚gleich‘

    „[…]Dementsprechend springt BNE zur angegebenen Adresse, wenn das Zero-Flag gesetzt[…]ist.“

    //Aber das Z-Flag wird doch nur gesetzt wenn das X nach dem DEX 0 ist?!

    Mir ist schon klar, dass der Source-Code so funktioniert wie er da steht, aber diese Erklärung verknotet mir die Gehirnwindungen.
    Übersehe ich was oder missverstehe ich was? @-@“‘

    1. Oh Mann, ich fall vom Glauben ab! Was habe ICH denn da für einen Schwachsinn geschrieben?!!

      Sorry!
      Du hast nichts übersehen, sondern bist über einen Fehler gestolpert, der so seit über drei Jahren dort stand!
      Das so etwas ausgerechnet beim ersten Beitrag passiert und solange nicht auffällt, trifft mich echt hart.

      „[…]Der Befehl DEX zieht einfach vom Inhalt des X-Registers 1 ab.[…]
      Ist die Zahl genau 0, dann wird das Z-Flag gesetzt (1), sonst wird es gelöscht (0). “
      //Also ist X=1, dann DEX …, dann ist X=0 und Z-Flag=1 ?
      Ja, soweit ist es noch richtig!

      „[…]Ob etwas gleich oder ungleich ist wird über das Zero-Flag geprüft
      (0 bedeutet gleich und 1 ungleich, zieht man z. B. zwei Zahlen von einander ab und das Ergebnis ist 0, dann sind diese Zahlen offensichtlich gleich).[…]“
      //Also wenn ich X=1 und DEX mache, ist das Ergebnis 0. Weil die Zahlen sind ‚gleich‘
      //Aber das Z-Flag wird gesetzt, also Z-Flag=1. Aber dann bedeutet Z-Flag=1 ja ‚ungleich‘?!?
      //Aber die Zahlen waren doch grade noch ‚gleich‘
      Hier stand von mir verzapfter Unfug!
      Es ist natürlich genau umgekehrt: Z=
      1 bedeutet gleich und 0 ungleich
      Das Z-Flag wird immer auf 1 gesetzt, um anzuzeigen, dass die letzte Aktion NULL war, egal ob durch DEX, INY, LDA, CMP usw.

      „[…]Dementsprechend springt BNE zur angegebenen Adresse, wenn das Zero-Flag gesetzt[…]ist.“
      //Aber das Z-Flag wird doch nur gesetzt wenn das X nach dem DEX 0 ist?!
      Nochmal Blödsinn von mir.
      BNE (Springe wenn ungleich) verzweigt natürlich, wenn Z=0 also gelöscht ist!

      Mir ist schon klar, dass der Source-Code so funktioniert wie er da steht, aber diese Erklärung verknotet mir die Gehirnwindungen.
      Übersehe ich was oder missverstehe ich was?
      Nein, du hast nichts missverstanden, ich habe dich durch meine falsche Erklärung vollkommen verwirrt.
      Ich kann mich nur nochmal entschuldigen!

      Reichen die obigen Erklärungen oder kann ich dir noch weiterhelfen?

      Gruß,
      Jörn

    1. Da liegst du falsch! Das BASIC-RAM beginnt bei $0800! Die erste BASIC Zeile beginnt zwar bei $0801, das ändert aber nichts daran, dass das BASIC RAM schon bei $0800 beginnt.

      Falls du mir nicht glaubst, hier noch weitere Quellen:

      • C64-Wiki ($0800-$9FFF 2048-40959 Page 8-159 Free BASIC program storage area (38911 bytes) )
      • Compute’s Mapping the Commodores 64 & 64C Seite 82

        2048-40959 $800-$9FFF
        BASIC Program Text
        This is the area where the actual BASIC program text is stored. The text of a BASIC program consists of linked lines of program tokens…

      PS:
      Ich fand es aber trotzdem etwas missverständlich und habe den Abschnitt um eine Erklärung ergänzt:
      Ab $0800 beginnt zwar das BASIC-RAM (s. „Kleine Hardwarekunde“), wie ihr aber eben gesehen habt, wollen wir die nächsten BYTEs erst ab $0801 ablegen. Dies ist die Adresse, an der die erste BASIC-Zeile erwartet wird. $0800 ist für uns tabu! Dort muss immer eine 0 stehen, sonst kommt es zu einer Fehlermeldung, wenn man RUN eingibt, um das Programm zu starten. Daher beginnen wir mit $0801.

      PPS:
      Danke für deine Anmerkungen. Nur durch entsprechende Rückmeldungen, kann ich die Texte weiter optimieren und Fehler, sowie Ungereimtheiten eliminieren.

      1. Hallo Jörn,

        du hast natürlich recht, ich hab selber auch nochmal im C64 für Insider nachgesehen. Lag wohl an der Macht der Gewohnheit daß ich auf $0801 kam.

  4. Hallo Jörn, tolle Seiten, ich bin begeistert. Da ich grad selber wieder auf dem C64 Trip bin arbeite ich die Seiten mal durch und habe viel Spaß dabei, tolle Arbeit. Bei den Taktzyklen des DEX Befehls habe ich gestutzt, Du hast den mit 1T angegeben. Ich meine mich zu erinnern, das es keinen Befehl mit einem Taktzyklus gab, der müsste zwei haben. Da ich kürzlich gerade mal wieder den CRE177 Beitrag von Tim Pritlove und Michael Steil gehört habe, wird das hier bestätigt (selbst NOP hat zwei 🙂 Übrigens ist der Beitrag für C64 Fans sehr zu empfehlen.
    Gruß und danke HP

    1. Igitt!
      Da hast du vollkommen Recht!!

      Da bin ich wohl mit der BYTE-Anzahl durcheinander gekommen. Ich habe das eben korrigiert. Beim DEY war es übrigens auch falsch, aber unter Mnemonics stand es für beide richtig. INX, INY und NOP sind auch korrekt.

      Danke für den Hinweis,
      Jörn

      PS: Der Text in deinem Post war irgendwie doppelt, ich habe ihn daher bereinigt. Außerdem habe ich dort noch einen Link zum CRE177-Beitrag eingefügt.

  5. Pingback: Linksammlung C64 | Stefan.Waidele.info

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Protected by WP Anti Spam