C in ASM einbinden | ASM in C einbinden?

LotadaC

Sehr aktives Mitglied
22. Jan. 2009
3.547
70
48
Marwitz
Sprachen
  1. BascomAVR
  2. Assembler
Hallo,

folgendes erstmal zum Hintergrund:

vor kurzem bin ich beim neuen ATtiny814/816/(1616)/817/(1617) wieder einmal auf den integrierten "Peripheral Touch Controller" gestoßen, dessen Verwendung leider zwingend an eine C-Bibliothek (den Composer) gebunden ist.

Mir stellte sich nun im ATtiny-Übersichts-Thread die Frage, wie ich diesen Composer (bzw C-Code allgemein) in einem Assembler-Projekt nutzen kann, wie C- und ASM-Code zu "linken" ist.

Der PTC-Teil ist im Datenblatt wie üblich quasi gar nicht dokumentiert, man soll den QTouch-Composer verwenden, und das dann irgendwie mit seinem Code "linken" - wie geht sowas ( @Dirk - insbesondere mit ASM)?
Die dort erfolgten acht Antworten wurden hierher ausgelagert...
 
Selber habe ich den Composer auch noch nicht eingesetzt.
Mir gings um das "linken", was in Hochsprachen irgendwie üblich zu sein scheint (wo ich keine Ahnung von habe). In meinen ASM-Programmen mach ich ja selbst auf den Bits in den I/Os rum, Wie muß ich mir da jetzt das "linken" vorstellen? Woher weiß ich dann, welche Ressourcen die gelinkten Dateien wie verbrauchen?
Der PTC braucht auf alle Fälle irgendwie den ADC.
Wi linke ich denn irgendwelche C-Dateien mit meiner ASM- oder Hex-Datei?
 
Hi,

(Der PTC-Teil ist im Datenblatt wie üblich quasi gar nicht dokumentiert, man soll den QTouch-Composer verwenden, und das dann irgendwie mit seinem Code "linken" - wie geht sowas ( @Dirk - insbesondere mit ASM)?)

da wird es in Bascom wohl auch etwas ... naja ... sagen wir mal "schwierig" werden.

Gruß
Dino
 
Ich hatte ja schon damals versucht, über den ATMEL-Support Hilfe zu bekommen. Ich kann mich durch den Composer klicken, und erhalten dann 'n Haufen irgendwelcher C-Dateien, mir ist trotzdem nicht klar, wie ich erstens ein gemeinsames Hexfile daraus und meiner ASM-Datei erhalte, und zweitens in meiner ASM-Datei auf QTouch-Ereignisse reagiere.
wird es in Bascom wohl auch etwas ... naja ... sagen wir mal "schwierig" werden.
Ich bin sicher nicht schwergewichtig genug, vielleicht sollte ich mal Marc Alberts drauf stoßen...
Inwiefern die Arduino-Szene den konventionellen C-Code verdauen kann, kann ich nicht einschätzen...

(grundsätzlich müßte man natürlich mit den neuen Möglichkeiten des ADC (Akkumulation, Fenstermode) und des Timers und dieser Event-Verkettung selbst sowas hinbekommen können. Also weitgehend im Hintergrund.
Keine Ahnung, warum die sich mit der Verwendung des Hardware-PTC so geheimniskramerisch haben...)

Grundsätzlich finde ich diesen Controller (die Serie) schon recht interessant. Ein Tiny mit Master-TWI, UART, SPI und einem DAC...
 
Wi linke ich denn irgendwelche C-Dateien mit meiner ASM- oder Hex-Datei?
Hochsprache C mit (Inline)Assembler geht. Das nutzt man dann, wenn zum Beispiel etwas zeitkritisch ist (hier habe ich das verwendet für die WS2812B RGB LEDs).

Assembler-Projekt mit Teilen in Hochsprache geht nicht, bzw. ist nicht sinnvoll, habe ich noch nie gesehen :hmmmm:

Das was ich mir für ein Assembler Projekt vorstellen könnte wäre den C Soucecode für einen Mikrocontroller erstellen lassen und diesen dann analysieren und in Assembler umschreiben. Wie schon geschrieben, habe ich den Composer selber noch nicht eingesetzt (Bei einem Projekt verwende ich direkt QTouch 42QT). Ich vermute es wird nicht nur die Signalaufbereitung behandelt, sondern auch auch weitere Bereiche wie Touch-Delay, -Repeat, Slider usw. Der Code wird dann bestimmt auch etwas komplexer sein.
 
Hochsprache C mit (Inline)Assembler geht.
So kenn ich das auch, zB in Bascom...
Assembler-Projekt mit Teilen in Hochsprache geht nicht, bzw. ist nicht sinnvoll, habe ich noch nie gesehen :hmmmm:
Eben, das ist/war auch mein Weltbild...
Deswegen habe ich ja auch den "Rat" aus dem ATMEL-Support nicht verstanden - möglicherweise reicht mein Englisch nicht:
Dear ...,

Thanks for writing to us.

Please note that, in QTouch applications the PTC registers configuration would be taken care by the QTouch Library. Users must call appropriate QTouch Library APIs in the application for capacitive sensors initialization and also for periodic touch measurement. As such, users are not required to do PTC registers configuration explicitly at the application level.

The QTouch Library for Atmel MCU devices is implemented using C language. Also, the QTouch Composer for Atmel Studio7.0 generate QTouch projects in C language only.

In case the firmware is being developed in assembly, then standalone project has to be developed without using QTouch Composer. Only, the touch specific configuration has to be done in C language. The rest of the user application firmware can be developed in assembly language. To enable QTouch support in the application, QTouch Library and all the corresponding QTouch Library specific files have to be explicitly integrated to the project.

Please let us know if you need any further information.

Best Regards,
...
Den Composer-Kram in C, ok.
Mein Programm in ASM, ok
QTouch Library und specific files ... integrieren??

Oder wollen die, daß ich quasi C programmiere, und da dann inline-ASM verwenden darf???
 
Only, the touch specific configuration has to be done in C language. The rest of the user application firmware can be developed in assembly language. To enable QTouch support in the application, QTouch Library and all the corresponding QTouch Library specific files have to be explicitly integrated to the project.

Ja ich verstehe es so: Assembler Projekt und C Code einbinden.

Schau mal hier ...
Atmel AT1886: Mixing Assembly and C with AVRGCC

Das wäre wahrscheinlich eine Lösung für dich.
 
Moin.

Darin wird ausführlich beschrieben, wie man mit dem Atmel-Studio problemlos Assembler und C mischen kann. Der GCC, den das Studio verwendet, übersetzt C-Code zuerst in Assembler und linkt dann alle übersetzten C-Dateien mit vorhandenen Assembler-Dateien. Andere Plattformen, die den GCC benutzen, sollten das ebenso können, nur das man ggf. das Make-File selber schreiben muß (generiert das Studio automatisch).

Man kann also das ganze Projekt in Assembler schreiben und einige Programmteile in C dazu linken und umgekehrt genauso. Und man hat noch den Vorteil, das man C-Headerfiles, d.h. den C-Präprozesser nutzen kann.

  • Programmierinterface UPDI (??)
Das Datenblatt ist ... ähm ... sehr ... ähm ... gewöhnungsbedürftig, die Hardwarearchitektur auch...
Es findet sich kein Register Summary, keine Assembler-Codes (aber immerhin 'n Instruction Set) - riecht irgendwie alles sehr nach PIC...

Das Datenblatt ist eine Mischung aus Xmega- und Mega/Tiny-Datenblättern, wobei ich die vom Xmega wesentlich übersichtlicher finde. (Für mich waren die Letzteren ... sehr ... ähm ... gewöhnungsbedürftig;))

Da die Entwicklungszeit eines neuen Chips ca. zwei Jahre beträgt, haben die neuen Tinies wohl kaum etwas mit den PICs zu tun, auch wird im Datenblatt noch nicht mal der Name Microchip erwähnt.

Im übrigen beruhen die neuen Tinies auf einem abgespeckten Xmega-Kern, sogar die Custom-Logik des E5 haben sie mit übernommen.
http://lists.gnu.org/archive/html/avr-gcc-list/2016-11/msg00002.html
"tiny817 is not of AVR_TINY architecture, it's actually a xmega2 according to our classification."

UPDI ist die vereinfachte PDI-Schnittstelle der Xmegas (2 Pins, UPDI nur ein Pin). Wenn man den Reset-Pin anderweitig braucht und die Fuse umstellt, kann man ihn nur noch mit einem 12V Puls auf Reset in den Programmiermodus bringen.

Da es die neuen Tinies aber nicht als DIP gibt, kann ich auch gleich bei den Xmegas bleiben.
 
Mir stellte sich nun im ATtiny-Übersichts-Thread die Frage, wie ich diesen Composer (bzw C-Code allgemein) in einem Assembler-Projekt nutzen kann, wie C- und ASM-Code zu "linken" ist.
Wenn es Dir erstmal reicht zu verstehen, wie von Assembler aus C-Funktionen aufgerufen werden können, schreib mal ein kleines Assemblerprogramm, das nichts weiter tut, als so alle Sekunde zwei Unterprogramme aufzurufen.

Dem Erste übergibst Du z.B. einen Wert von 0 bis 359 in r25:r24, es zieht 180 davon ab und gibt den neuen Wert in den selben Registern zurück.

Das Zweite gibt die Zahl irgendwie aus, sodaß Du was sehen kannst.

Das Erste ersetze ich dann durch ein C-Unterprogramm, das die Mathlib aufruft und z.B. einen Sinuswert im Bereich von +/- 100 zurückgibt.

In der AppNote ist übrigens ein Druckfehler: in Table 5-1, vorletzte Zeile, muß es r30 heißen, statt r0. Der Tabelle kannst Du entnehmen, daß C die Register r0, r18-r27 und r30-r31 verändern kann, d.h. die mußt Du retten, wenn Du sie benutzt, und r1 muß immer null sein.

Ist für mich auch Neuland, habe bisher nur Assembler von C aus aufgerufen.
 
So in etwa wollte ich auch erstmal anfangen. Hätte jetzt irgendwas über den UART gemacht. Hab im Moment aber nicht wirklich Zeit dazu. Komme sicher irgendwann nochmal darauf zurück, Danke für Dein Angebot.

Was mir noch unklar ist: Der Composer wird ja auch diverse Variablen anlegen, die irgendwo im SRAM liegen. Wie stelle ich sicher, daß da nichts mit dem von ASM verwendeten SRAM kollidiert?
Wenn die Vars mit dem Stack kollidieren, ist das eh ein Platzproblem, aber was ist mit meinen ASM-Variablen? Ok, wenn man die konsequent mit der .byte-Direktive anlegt, könnte(!) sich der Compiler drum kümmern. Bei Ringspeichern o.ä. hab ich bisher die Startadresse immer nur als Konstante abgelegt, müßte man dann halt als label mit der maximalen Größe im Datensegment machen.

Der Fehler in der Tabelle ist mir auch schon aufgefallen - mit dem Text darüber ist das aber eigentlich klar.
 
Hätte jetzt irgendwas über den UART gemacht.
Dafür das Zweite Unterprogramm. ;) Kannst Du über UART ausgeben, ich über SPI auf LCD.
Wie stelle ich sicher, daß da nichts mit dem von ASM verwendeten SRAM kollidiert?
Kapitel 4
Überlaß dem Compiler die Buchführung. Alle Variablen, die Du in Assembler nutzt, in C als global und in Assembler als extern deklarieren und Du brauchst Dich nicht mehr darum zu kümmern wo was steht.
Mit dem Stack könnte es Probleme geben. Unter 1k RAM und 4-8 k Flash würde ich gar nicht erst anfangen. Die vielen Funktionen der QTouch Lib deuten darauf hin, das der Kram 'ne ganze Menge Platz braucht (sowohl RAM alsauch Flash).
 
Hmm...
Das schafft mir Variablen, auf die von beiden Seiten her zugegriffen werden kann, ok. Wo wird die Variable denn wirklich erzeugt? Im konventionellen ASM inkrementiert jedes .byte einfach nur den dseg-Zähler, das label übernimmt vorher die Adresse (also den Zeiger). Und hier?
Wenn ich richtig verstehe, bewirkt .extern in meiner ASM-Datei, daß die Variablen eben woanders definiert werden (bzw die Adresse). In irgendeiner C-Datei muß ich die Variable dann global definieren (außerhalb irgendeiner Routine) - hab aber keine Kontrolle über die tatsächliche Adresse.

Das Dokument bezieht sich auf den GCC-Compiler - ich verstehe das eher als C-Compiler (der halt dann mit eingebettetem ASM läuft - ggf mit nahezu 100% ASM)
Meine ASM-Datei müßte 'ne .S-Extension haben.
Ich bräuchte 'ne main-Subroutine (reicht da einfach ein label? Sowas wie 'ne Subroutine in C mit bekanntem Anfang und Ende gibts ja in ASM nicht zwingend. Du kannst mit 'nem (R)Call auch in 'ne Endlosschleife hüpfen, oder den als simplen Sprung benutzen (den Sinn jetzt mal dahingestellt). Oder mehrere alternative Endpunkte realisieren. Oder eine Subroutine als Teil einer anderen (ohne Call) mitausführen lassen. Oder...)
Diese main-Routine (?) muß global sichtbar gemacht werden (.global main), weil der linker dort anfängt (oder einfach nur mit der ganzen Datei, die irgendwo die Main-Routine enthält?).
In einer externen C-Datei definierte (globale) Variablen oder Subroutinen mache ich in meiner ASM-Datei mit .external "bekannt"/aufrufbar/verwendbar.
Ok, soweit.
Wie ist das jetzt zB mit den Interruptvektoren? In ASM schreib ich ja selbst den Code in die IVT (meist den Sprung, aber nicht unbedingt immer) - wie ist das im GCC-Assembler?
Werden alle Variablen/Routinen (in der externen Datei) in Maschinencode übersetzt, oder nur die, die aus der Datei mit der main heraus (mit .external) bekannt gemacht werden?
(Wenn letzteres der Fall ist, könnte man damit auch globale Subroutinen aus anderen ASM-Dateien einbinden (ohne die ganze ASM-Datei zu inkludieren) - wobei auch hier wieder die Frage steht, wo eine ASM-Subroutine konkret zuende ist...
 
Du kannst auch in C feste Adressen vergeben, so wie das bei den I/O-Registern gemacht wurde, aber wozu willst Du Dich damit zusätzlich rumschlagen, wenn es der Compiler erledigt?

Variablen erzeugen? Na, ja in C werden Variablen deklariert und definiert. Die Deklaration legt Bezeichner und Typ fest, die Definition reserviert Speicherplatz dafür.


CodeBox C
uint8_t var = 7;     // deklariert und definiert den Bezeichner var als Variable vom Typ unsigned
                     // char (also Byte), reserviert Speicher und initialisiert mit 7
extern uint8_t var;  // deklariert dieselbe Variable in einer anderen Datei, d.h. gibt bekannt,
                     // daß sie woanders schon definiert wurde

GCC steht für GNU Compiler Collection, früher mal für GNU C Compiler. Wenn man's genau nehmen will, könnte man „der“ GCC sagen, wenn man den Compiler meint und „die“ GCC wenn man die Collection meint. Also, die GCC beinhaltet neben dem Präprozessor, Assembler und Linker auch Compiler für C, C++, Java, Objective-C, Fortran, Ada und Go. (Einen Präprozessor, Debugger und GNU Pascal u.a. gibt es auch noch.) Nach dem Durchlauf des Präprozessors werden alle Programme, die in einer höheren Programmiersprache geschrieben sind in Assembler übersetzt. Vermutlich wird sogar vieles zuerst in C übersetzt und dann erst in Assembler. Dann wird der Assembler und anschließend der Linker aufgerufen.

Das Atmel-Studio benutzt die GCC auch für reine Assemblerprogramme, d.h. .asm versteht GCC auch, aber wenn man Assembler und C mischt (und das mußt Du, wenn Du C-Libraries benutzen willst), sollte man .S verwenden.

Wenn die main im Assemblerquelltext steht, mußt Du Dich um fast alles selbst kümmern, Stack initialisieren, IRQ-Tabelle etc. Wenn die main im C-Quelltext steht, erledigt das der Compiler für Dich, auch wenn Dein gesamtes Programm in Assembler geschrieben ist, das C-Programm nur aus Variablendeklarationen und defines besteht und in der main nur Dein Assemblerprogramm aufgerufen wird.
 
Variablen erzeugen?
Meinte/vermutete ich genau so, wie Du es hinterher auch erklärt hast. mit .extern mache ich die Variable in meiner ASM-Datei nur bekannt (Name für'ne Speicheradresse), eben daß der Speicher in einer externen Datei festgelegt/reserviert wird/wurde.
aber wozu willst Du Dich damit zusätzlich rumschlagen, wenn es der Compiler erledigt?
In ASM macht das .Byte doch genauso selbständig. Und feste Adressen können durchaus nützlich sein, wenn unterschiedliche Variablen denselben Speicher bzw überlappende Bereiche treffen sollen, oder Wenn man schnell auf Tabellen/Ringspeicher zugreifen will zB.
Das Atmel-Studio benutzt die GCC auch für reine Assemblerprogramme
Ich habe bisher den avrassembler (bzw den avrassembler2) verwendet - ist der ein Teil des GCC oder nicht?
Innerhalb eines Assembler-Projektes ist die .extern-Direktive "invalid".
mußt Du Dich um fast alles selbst kümmern, Stack initialisieren, IRQ-Tabelle etc
Den Stack brauch ich meist nicht zu initialisieren, ansonsten alles klar - Assembler...
Was muß ich denn im 6er (7er)-Studio konkret erstellen, um 'ne Assembler-Datei (meinetwegen .S)zu erhalten, die .extern versteht, und wo die main drinn sein darf?
 
Und feste Adressen können durchaus nützlich sein, wenn unterschiedliche Variablen denselben Speicher bzw überlappende Bereiche treffen sollen, oder Wenn man schnell auf Tabellen/Ringspeicher zugreifen will
Dennis Ritchie hat da mit C einen hervorragenden, universellen Makro-Assembler gebaut, ;) der kann mit unions auf überlappende Speicherbereiche zugreifen und mit Pointern kommt man überall ran, auch an Tabellen und Ringspeicher.
Ich habe bisher den avrassembler (bzw den avrassembler2) verwendet - ist der ein Teil des GCC oder nicht?
aus dem AVR- oder Atmel-Studio heraus oder Stand-Alone?
Die Preprocessor directives und die Expressions sehen sehr nach C aus.
Das Atmel-Studio 6 ist (nach WfW 3.11 ;)) so ziemlich das lahmarschigste Programm, das mir je begegnet ist. 7 ist etwas besser, läuft aber nicht mehr mit XP. Wenn Du nicht die allerneusten Controller brauchst (oder die Toolchain selber updaten willst): das AVR-Studio 4 ist sehr viel schneller und läuft auch mit wine unter Linux.
Was muß ich denn
Wenn ich heute abend dazu komme, mache ich mal ein Beispielprojekt.
 
Ja, das 4er war deutlich performanter, und mit dem 18er oder 19er Servicepack konnte es sogar TPI mit dem Tiny4/5/9/10, aber das war mMn der letzte. Das hab ich auch noch auf dem Rechner.

Ich bin gestern schon beim Erstellen des Projektes hängengeblieben. Erstelle ich ein Assembler-Projekt, wird (wie bisher immer) 'ne .ASM generiert, und .Extern ist invalid. mMn der Assembler2.
Erzeuge ich ein GCC-executable-Projekt, wird 'ne .c erzeugt, und ich kann 'ne Assemblerdatei hinzufügen (.s), die .extern kennt. Aber das mit der main bekomme ich nicht hin...
 

Die Doku hatten wir schon mal am Anfang.


Es ist wahrscheinlich recht anspruchsvoll und aufwändig den C-Source vom Composer in einem Assemblerprojekt einzubinden, zumindest lässt das der Umfang der Beiträge so erscheinen. Es wäre hier vielleicht die ideale Gelegenheit einen Einstieg in C zu probieren. Dies ist nur mal ein Vorschlag, vielleicht ist es ja dann so einfacher :)

Dirk :ciao:
 
Den Stack brauch ich meist nicht zu initialisieren
Wenn Du C-Funktionen aufrufen willst, muß er vorher initialisiert sein.


So, Schritt für Schritt. Erstmal die einfachere Version: Asm von C aus aufrufen.
Etwas C-Syntax mußt Du sowieso lernen, wenn Du C-Funktionen aufrufen willst und wenn Du den Präprozessor schon benutzt hast, wird Dir einiges bekannt vorkommen
Ich nehme grade Studio 7, müßte aber bei den älteren Versionen nahezu identisch sein.

File → New → Project __________ GCC C Executable Project
Projektnamen eingeben und ok
Controller ist egal, habe einen mega88pa ausgewählt, weil ich den grade auf dem Steckbrett habe.
Programm eingeben:


CodeBox C
#include <avr/io.h>
#include "test.h"                    // Headerdatei für Asm einbinden

uint8_t summe = 0;                   // Deklaration und Definition einer globalen Variablen
uint8_t summand = 0;                 // dito für eine Variable, die nur in Asm benutzt wird
                                     // aussagekräftige Namen ersparen übrigens Kommentare ;)
int main(void)
{
   FOREVER {                         // Endlosschleife
       summe = addiere(summe);       // Funktion mit Parameter und Rückgabewert
       nacktfrosch();                // Funktion ohne Parameter und Rückgabewert
    }
}

File → Save

File → New → File __________ Assembly File
Programm eingeben:


CodeBox Assembler
                  // mit der Endung .S (statt .s) kann man den Präprozessor auch in asm benutzen
#define INC       // wenn diese Anweisung auskommentiert wird, wird dekrementiert
                  //← diese C-Kommentare werden vom Präprozessor entsorgt

.extern summand           ; globale Variable, im C-Programm definiert

.func addiere
.global addiere         
addiere:
   lds r16, summand
   add r24, r16           ; Summe wird in r24 übergeben und braucht hier
   ret                    ; deshalb nicht bekannt gemacht zu werden
.endfunc                  ; Rückgabewert erwartet C auch in r24

.func nacktfrosch
.global nacktfrosch
nacktfrosch:
   lds r16, summand
#ifdef INC                // Präprozessoranweisungen
   inc r16                // mach irgendwas mit Summand
#else
   dec r16                // oder mach was anderes
#endif
   sts summand, r16
   ret
.endfunc

File → Save as __________________ <Pfad zum Projektordner>/test.S
File → Move test.S into _________ <Projektname>

File → New → File ______________ Include File
Programm eingeben:


CodeBox C
#ifndef TEST                    // Include-Guard, damit der Header nicht mehrfach eingebunden wird
#define TEST

#define FOREVER       for (;;)
                                // Funktionsdeklarationen
uint8_t addiere(uint8_t var);   // var wird in r24 übergeben, Rückgabewert auch in r24
void nacktfrosch(void);         // nackter Funktionsaufruf, entspricht call in asm

#endif

File → Save as ___________________ <Pfad zum Projektordner>/test.h
File → Move test.h into __________ <Projektname>
compilieren
Simulator wählen
testen

Auf diese Art und Weise kannst Du auch fast Dein gesamtes Projekt in Assembler schreiben und nur die Aufrufe der Qtouch-Lib über die .c und .h Files machen.
Es wäre hier vielleicht die ideale Gelegenheit einen Einstieg in C zu probieren. Dies ist nur mal ein Vorschlag, vielleicht ist es ja dann so einfacher :)
Dem schließe ich mich an ;)
Selbst große Assemblerprogrammteile in C einbinden finde ich einfacher.

btw: Tabs nach Text, sowohl innerhalb, als auch außerhalb von Codeboxen ist ein Krampf.
Gibt's da 'ne einfache Möglichkeit, die ich nicht gefunden habe?
 
  • Like
Reaktionen: LotadaC

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)