Ein Versuch in C

Schnelle objektorientierte, kompilierende Programmiersprache.
GerdV
Beiträge: 4
Registriert: Mi Jun 03, 2009 2:30 pm
Wohnort: Aham

Ein Versuch in C

Beitrag von GerdV » Do Jun 04, 2009 5:50 pm

Hallo,

erstmal ein Dankeschön an diese Plattform, hat mir zu den ersten Gehversuchen in C sehr geholfen.
Ich programmiere ansich meine Microcontroller in Assembler auf dem AVR-Studio, was jetzt aber nicht möglich ist.
Muss für mein Hobbyprojekt (Modell-U-Boot) einige complexe Rechenfunktionen durchführen und zwar mit den Funktionen
double cos(x) und atan(x) um damit meinen Kurs zu berechnen. Wenn es irgendwo dafür Codeschnipsel in Assembler gibt hab ich mein Problem gelöst.
Die Anfänge von meinem Programm sehen so aus:

Code: Alles auswählen

	#include <avr/io.h>
	#include <math.h>
	double	BA = 5936.841;		// Startposition Breitengrad N
	double	LA = 515.258;		// Startposition Längengrad E
	double	BZ = 5933.204;		// Zielposition Breitengrad N
	double	LZ = 512.688;		// Zielposition Längengrad E	
	
	double	BU = 0;			// Breigengradunterschied	
	double	MB = 0;			// Mittelbreite
	double	LU = 0;			// Längengradunterschied
	double	AW = 0;			// Abweitung
	double	WA = 0;			// Winkel alpha
	double	WI = 360;			// verwendeter Winkelbreich
	double	ZR = 0;			// Zielrichtung
	double	Dummy = 0;

	int main ()
	{	
	  BU = BA - BZ;			// Unterschied Breitengrad berechnen
	  return BU;
	  MB = BA - BU/2;			// Mittelbreite berechnen
	  return MB;			
	}
Dieser Anfang vom Programm lässt sich fehlerfrei übersetzen, sagt wohl noch nicht viel.
Jetzt Habe ich mit dem Tracen mal versucht meine errechneten Werte im RAM-Bereich zu finden, was nicht gelang.
Nun meine Fragen:
Wie finde ich meine Werte z.B. im RAM wieder?
Rechnet mir das Programm überhaupt die Werte aus, müsste für BU 3,637 sein ....finde den Wert bloss nicht?
Wäre nett, wenn jemand mal einen Blick aufs "Progrämmchen" werfen könnte.

Achja bin schon im Rentenalter und habe sowas wie hier nie gelernt!

Gruss aus Bayern

Gerd

/* edit by Xin: Codeflags eingefügt */

nufan
Wiki-Moderator
Beiträge: 2558
Registriert: Sa Jul 05, 2008 3:21 pm

Re: Ein Versuch in C

Beitrag von nufan » Do Jun 04, 2009 6:18 pm

Hallo und willkommen :)

Bitte pack deinen Code doch in

Code: Alles auswählen

Code-Tags
dadurch ist er leichter zu lesen. Einfach den Text markieren und auf "Code" klicken.

Vorweg eine Frage: Soll dein Programm komplett in C geschrieben werden? Willst du es irgendwie mit Assembler kombinieren?
GerdV hat geschrieben:

Code: Alles auswählen

double BA = 5936.841; // Startposition Breitengrad N
double LA = 515.258; // Startposition Längengrad E
double BZ = 5933.204; // Zielposition Breitengrad N
double LZ = 512.688; // Zielposition Längengrad E

double BU = 0; // Breigengradunterschied
double MB = 0; // Mittelbreite
double LU = 0; // Längengradunterschied
double AW = 0; // Abweitung
double WA = 0; // Winkel alpha
double WI = 360; // verwendeter Winkelbreich
double ZR = 0; // Zielrichtung
double Dummy = 0;
Das sind aber ziemlich viele Variablen... noch dazu alle global. Du solltest sie dort deklarieren wo du sie benötigst, also z.B. in main. Weiters kannst du dir das "double" ab der 2. Variable sparen und Variablen mit einem , trennen. Also z.B.

Code: Alles auswählen

double LA = 515.258, BZ = 5933.204, LZ = 512.688;
Am Ende muss wieder ein ; stehen.
GerdV hat geschrieben:

Code: Alles auswählen

int main ()
{
BU = BA - BZ; // Unterschied Breitengrad berechnen
return BU;
MB = BA - BU/2; // Mittelbreite berechnen
return MB;
}
Also den gewünschten Wert von "MB" wirst du auf jeden Fall nicht bekommen. Die Funktion bricht nach dem "return" ab. Die Funktion sollte "double" zurückgeben, da schließlich "BU" und "MB" ebenfalls vom Typ "double" sind. Wobei mir nicht ganz klar wohin du diesen Wert übergeben willst.
GerdV hat geschrieben:Wie finde ich meine Werte z.B. im RAM wieder?
Du könntest den Adressoperator & oder Zeiger verwenden. Du weißt dann genau an welcher Stelle im RAM dein Wert liegt. Für Zeiger musst du Speicherplatz reservieren. Hier ein kurzes Beispiel zu beiden Methoden:

Code: Alles auswählen

#include <stdio.h>
#include <stdlib.h>

int main ()
{

  double *pointer, variable;    // Zeiger und normale Variable deklarieren
  
  pointer = (double *) malloc (sizeof (double));     // Speicher für Zeiger reservieren
  
  printf ("Adresse des Wertes: %p\n", pointer);    // Wert des Zeigers ausgeben
  
  printf ("Adresse der Variable: %p\n", &variable);  // Adresse der Variable ausgeben

  return 0;

}
Auch wenn du auf deinem U-Boot kein printf funktioniert sollte dir das helfen zu verstehen was ich meine. Wie das mit der Speicherreservierung aussieht kann ich nicht sagen.

Hoffe ich konnte dir weiterhelfen :)


EDIT: Kleine Umformulierung
Zuletzt geändert von nufan am Do Jun 04, 2009 6:27 pm, insgesamt 2-mal geändert.

Benutzeravatar
cloidnerux
Moderator
Beiträge: 3123
Registriert: Fr Sep 26, 2008 4:37 pm
Wohnort: Ram (Gibts wirklich)

Re: Ein Versuch in C

Beitrag von cloidnerux » Do Jun 04, 2009 6:24 pm

Wie ich sehe wilst du einen µC Programmieren, da musst du andere befehle nutzen.
Wenn du auf irgedneinen Port einen Wert ausgeben wilst, dann musst du das über das entsprechende define machen(PD0...)
Zudem solltest du zumindest diene mian-schleife so umändern:

Code: Alles auswählen

int main()
{
   //Berechungnen ohne return
   while(1);
   return 0;
}
Das while(1); wird benötigt, damit sich das Porgramm nicht sofort beendet.
Redundanz macht wiederholen unnötig.
quod erat expectandum

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8859
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: Ein Versuch in C

Beitrag von Xin » Do Jun 04, 2009 6:46 pm

GerdV hat geschrieben:erstmal ein Dankeschön an diese Plattform, hat mir zu den ersten Gehversuchen in C sehr geholfen.]
Ich programmiere ansich meine Microcontroller in Assembler auf dem AVR-Studio, was jetzt aber nicht möglich ist.
Muss für mein Hobbyprojekt (Modell-U-Boot) einige complexe Rechenfunktionen durchführen und zwar mit den Funktionen
double cos(x) und atan(x) um damit meinen Kurs zu berechnen.
Willkommen an Bord ;)
GerdV hat geschrieben:Die Anfänge von meinem Programm sehen so aus:

Code: Alles auswählen

	int main ()
	{	
	  BU = BA - BZ;			// Unterschied Breitengrad berechnen
	  return BU;
	  MB = BA - BU/2;			// Mittelbreite berechnen
	  return MB;			
	}
Du verlässt die Funktion main (also das Programm) an der Stelle 'return BU'.
GerdV hat geschrieben:Dieser Anfang vom Programm lässt sich fehlerfrei übersetzen, sagt wohl noch nicht viel.
Jetzt Habe ich mit dem Tracen mal versucht meine errechneten Werte im RAM-Bereich zu finden, was nicht gelang.
Nun meine Fragen:
Wie finde ich meine Werte z.B. im RAM wieder?
Rechnet mir das Programm überhaupt die Werte aus, müsste für BU 3,637 sein ....finde den Wert bloss nicht?
Wäre nett, wenn jemand mal einen Blick aufs "Progrämmchen" werfen könnte.
Ich weiß jetzt nicht, wo Du das Programm aufrufst. Mit einem (aktuellen) OS kommst Du an die Werte nach dem Verlassen des Programms in der Regel nicht mehr ran.
Da Du in Assembler programmierst, wirst Du vermutlich Funktionen aus Assembler rufen wollen. Hierfür wirst Du gemeinsame verwendete Objekte kompilieren müssen und anschließend Dein Assemblerprogramm mit den von Dir geschriebenen C-Funktionen verbinden (linken).

Versuch Dich erstmal mit printf, so dass Du die Werte auf eine Text-Konsole ausgeben kannst, damit Du C lernen kannst.

Code: Alles auswählen

#include <stdio.h>

int main( void )
{
  double d = 3.1415;

  printf( "Wert: %f\n", d );

  return 0; // Fehlercode: alles ok.
}
Dein Fahrplan wäre vermutlich einzelne Funktionen zu schreiben (die Du von main aus testen könntest). Diese Funktionen wirst Du dann vermutlich aus Assembler heraus aufrufen.
GerdV hat geschrieben:Achja bin schon im Rentenalter und habe sowas wie hier nie gelernt!
Lebenslanges Lernen... wäre ja auch schade, wenn man als Rentner vor dem Fernseher versauert, oder!?

Das Forum ist für Programmiereinsteiger. Das sind meist Jugendliche - aber es gibt keine Alterbeschränkung.
Ich bin sicher, auch wir können von Deinen Assemblerkenntnissen noch profitieren.
Willkommen im Forum.
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

Benutzeravatar
Kerli
Beiträge: 1456
Registriert: So Jul 06, 2008 10:17 am
Wohnort: Österreich
Kontaktdaten:

Re: Ein Versuch in C

Beitrag von Kerli » Do Jun 04, 2009 6:50 pm

Von mir auch noch ein Willkommen...
GerdV hat geschrieben:Jetzt Habe ich mit dem Tracen mal versucht meine errechneten Werte im RAM-Bereich zu finden, was nicht gelang.
Wozu musst du denn wissen wo die Werte im RAM liegen? Ich würde die Werte eher in den Variablen halten und die Ports deines Controllers steuern. Den richtigen Header hast du wie ich gesehen habe eh schon eingebaut.

Hier noch ein einfaches Programm:

Code: Alles auswählen

#include <avr/io.h>

int main(void)
{
  DDRB = 0xff; // Port B als Ausgang schalten
  PORTB = 0;   // Port B zurücksetzen
  while(1)
  {
    ++PORTB; // Port B hinaufzählen
  }

  return 0;
}
Für dein U-Boot musst du dann natürlich die Ports etwas sinnvoller ansteuern ;) Zum Ansteuern musst du dann nur mehr die jeweiligen Bits setzen wobei jeder Port als 8-Bit Variable angesprochen wird. Also zb:

Code: Alles auswählen

PORTB |= (1 << PB3); // Pin 3 an Port B aktivieren
PORTB &= ~(1 << PB3); // Pin 3 an Port B wieder deaktivieren
Übrigens bin ich da nebenbei noch auf eine ganz interessantes Tutorial gestoßen: http://www.mikrocontroller.net/articles ... C-Tutorial
"Make it idiot-proof and someone will invent an even better idiot." (programmers wisdom)

OpenGL Tutorials und vieles mehr rund ums Programmieren: http://www.tomprogs.at

GerdV
Beiträge: 4
Registriert: Mi Jun 03, 2009 2:30 pm
Wohnort: Aham

Re: Ein Versuch in C

Beitrag von GerdV » Do Jun 04, 2009 8:46 pm

Guten Abend,

vielen Dank für die Antworten, werde morgen damit mal testen.

Nun muss ich doch erstmal von Anfang an mein Vorhaben schildern.
Ich schreibe das C-Programm mittels AVR-Studio (Firmensoftware von ATMEL) und den dort integrierten GCC-Compiler.
Mit dem AVR-Studio kann ich das Programm ohne jegliche Hardware simulieren und später von dort als Hex-File in den entsprechenden Microcontroller brennen, ebenso liegt mir das Assemblerlistung vom C-Programm vor.
Dort im Simulator habe ich auf sämtliche Speicherarten, Ports und Spezialregister Zugriff und kann die während der Simulation auslesen.
In Assembler ist das einfach, da arbeite ich Zeile für Zeile ab und sehe sofort die Veränderung während in C einfach eine Funktion in main angearbeitet wird und nicht jeder einzelne Assemblerbefehl.
Ich teste so meine entwickelte Hard- und Software bis zu 90% ohne jemals die Hardware benutzt zu haben, Endtest findet dann natürlich mit der entsprechenden Hardware statt.
Nach der Funktionsabarbeitung kann ich dann statisch auf sämtliche Speicher und Register zugreifen und diese auslesen, was mir aber im Moment nichts bringt weil dort nichts Brauchbares zu finden ist.
Auf dem PC-Bildschirm kann ich nichts ausgeben da der Compiler bzw. das gesamte OS sich nur auf den Microcontroller bezieht und ich auch bislang noch keine Hardware für die Kurssteuerung habe also printf kann ich nicht verwenden und später auch kein "Datensichtgerät" im Boot vorhanden ist.

So nun zum eigentlichen Vorhaben:
Ja, es soll später alles in C programmiert werden, wenigstens dieses Modul und wenn ich alles soweit kapiert habe dann wohl später alles in C denn Assember ist schon eine ewige Tipperei.
Es werden bei dem Modul keine Ports angesprochen sondern später nur über einen Portpin das PWM-Signal an das Ruderservo ausgegeben.
Die Signale kommen vom GPS-Modul (RS232) und einem Kompassmodul (I2C) werden dann zu einem "Ziel-Kurs" verarbeitet der mittels Funk (RS232) als Ziel übertragen wurde.

Mein Stückchen Programm soll nichts weiter machen als die Werte von den Variablen abholen und dann wieder an die entsprechenden Variablen zurückgeben.
Beispiel BU = BA - BZ sollen aus Variablen AZ und BZ die Differenz errechnen und diese dann an die Variable BU zurückgeben.
Ich möchte die einzelnen Schritte und deren Werte einzeln im RAM haben, denn dann kann ich besser nach Programmfehlern suchen ...so habe ich das bislang immer gemacht.
Später kann man natürlich die Zwischenschritte weglassen und einen verkürzten Code schreiben ...wenn ich das mal beherrsche!

Schöne Abend noch

Gerd

Benutzeravatar
Dirty Oerti
Beiträge: 2229
Registriert: Di Jul 08, 2008 5:05 pm
Wohnort: Thurndorf / Würzburg

Re: Ein Versuch in C

Beitrag von Dirty Oerti » Fr Jun 05, 2009 9:31 am

Hm, ich persönlich habe keine Ahnung vom Microkontrollerprogrammieren, aber ein Computer ist ja ein riesen Microcontroller (^^), vielleicht hilft dir das :) :

Du hast irgendwelchen Assemblercode.
Nun legst du dir eine neue Datei an.
Sagen wir: kurs.c

Code: Alles auswählen

double berechne_kurs ( double var1, double var2)
{
   return var1 - var2;
}
Das kannnst du nun kompilieren. Wenn du es mit deinem Assemblercode zusammenlinkst, dann kannst du die Funktion auch aus deinem Assemblercode heraus aufrufen. Du musst dabei nur beachten, wie die Funktion aufgerufen wird:

Code: Alles auswählen


[EXTERN berechne_kurs] ;;evtl ein Unterstrich

 ;;;....

  push var2
  push var1 ;;;hier übergibst du die Parameter, umgekehrte Reihenfolge wie im C-Code!
  call berechne_kurs ;;;;evtl noch ein Unterstrich davor..
 ;; jetzt ist iwo der Return Wert, entweder auf dem Stack oder in einem Register, dass weiß ich leider nicht mehr.
Bei Fragen einfach an daniel[ät]proggen[Punkt]org
Ich helfe gerne! :)
----------
Wenn du ein Licht am Ende des Tunnels siehst, freu dich nicht zu früh! Es könnte ein Zug sein, der auf dich zukommt!
----
It said: "Install Win95 or better ..." So I installed Linux.

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8859
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: Ein Versuch in C

Beitrag von Xin » Fr Jun 05, 2009 10:14 am

Dirty Oerti hat geschrieben: ;; jetzt ist iwo der Return Wert, entweder auf dem Stack oder in einem Register, dass weiß ich leider nicht mehr.[/code]
Soweit alles richtig.

Ich glaube, das Ergebnis liegt mit auf dem Stack, ich weiß es auch nicht mehr genau.
Vielleicht können wir in Zusammenarbeit mit Gerd da ein Kurztutorial draus machen, wie man aus verschiedenen Programmiersprachen ein Programm zusammenlinken kann?!
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

GerdV
Beiträge: 4
Registriert: Mi Jun 03, 2009 2:30 pm
Wohnort: Aham

Re: Ein Versuch in C

Beitrag von GerdV » Fr Jun 05, 2009 4:53 pm

Hallo Xin,
Xin hat geschrieben:Vielleicht können wir in Zusammenarbeit mit Gerd da ein Kurztutorial draus machen, wie man aus verschiedenen Programmiersprachen ein Programm zusammenlinken kann?!
Ich schreibe kein kombiniertes Programm, also kein C- und Assemblerprogramm, sondern ein reines C-Programm.
Grund dafür ist, dass ich den Pythagoras bemühen muss um meinen Kurs zu bestimmen.
Ich brauche die Funktionen cos(x) und arctan(x) dafür, das ist der einzige Grund für mich im Moment um C zu lernen bzw. damit zu Programmieren. Mir haben bislang für meine Projekte immer Assembler und zum Austesten bzw. zum Steuern meiner Entwicklungen PureBasic gereicht, Purebasic auch nur soweit wie ich es für meine Anwendungen brauchte.

Meine Frage zielte bislang immmer darauf hin, ob das Ergenis meiner Rechenfunktion, bislang ja nur die Subtraktion, fest im RAM erhalten bleibt und ob das so mit den Werten als globale Variablen funktioniert.
Die Frage ist bislang mir noch nicht verständlich beantwortet worden.
Ob man die Variable double zu Testzwecken auf ein Port ausgeben kann welches nur 8-Bit-Breite hat ist wohl eher unwahrscheinlich.
Diese Lösung fällt folglich komplett flach ebenso die 16-Bit-Register im Microcontroller, bleibt nur noch als Lösung das RAM übrig.

Ich rede zwar hier von Assembler, meine aber damit nicht den Source-Code!
Um das mal etwas klarer darzustellen hier mal ein Auszug aus dem übersetzten Teil des vorhandenen C-Source-Code welches nur aus der einer einzigen Subtraktion in main besteht:
Das Ganze Progrämmchen hat eine Länge von über 2kB, in Assembler würden es keine 100 Byte sein!

Code: Alles auswählen

000000ae <main>:
	double	B_Unterschied = 0;					// Breigengradunterschied
	
	
	int main (void)
	{	
	B_Unterschied = A_Breite - Z_Breite;				// Unterschied Breitengrad berechnen
  ae:	60 91 14 01 	lds	r22, 0x0114
  b2:	70 91 15 01 	lds	r23, 0x0115
  b6:	80 91 16 01 	lds	r24, 0x0116
  ba:	90 91 17 01 	lds	r25, 0x0117
  be:	20 91 18 01 	lds	r18, 0x0118
  c2:	30 91 19 01 	lds	r19, 0x0119
  c6:	40 91 1a 01 	lds	r20, 0x011A
  ca:	50 91 1b 01 	lds	r21, 0x011B
  ce:	0e 94 be 01 	call	0x37c	; 0x37c <__subsf3>
  d2:	60 93 44 01 	sts	0x0144, r22
  d6:	70 93 45 01 	sts	0x0145, r23
  da:	80 93 46 01 	sts	0x0146, r24
  de:	90 93 47 01 	sts	0x0147, r25
  e2:	ff cf       		rjmp	.-2      	; 0xe2 <main+0x34>
Rechter Teil das Assemblerlisting und links der Hex-Code bzw. das ist der Maschinencode.

Diese Subtraktion B_Unterschied = A_Breite - Z_Breite;
lädt aus dem RAM ab 0x0114 per lds (Speicher direkt auslesen) ihre Rechenwerte vom RAM in entsprechende Register,
springt dann per call in eine Subtraktionsroutine,
liefert dann über die Register r22-25 per sts (speicher direkt ins RAM) einen Wert ab, vermutlich bzw. muss so sein, das Ergebnis der Subtraktion vom Typ double.
Auf diese Speicherstellen habe ich per AVR-Studio direkten Zugriff und kann die Werte während der Simulation auslesen.
Es steht nichts lesbares drin ...wenigstens nicht für mich im Moment, auch kein entsprechender Binärcode, ASCII schon garnicht.
Ich weiss nicht wie im Maschinencode eine Variable double abgespeichert bzw. aufgelöst wird.

Meine einzige Frage zum meinem C-Code ist nun:
Werden für die Rechenoperation die globalen Variablen mit ihren Werten verwendet, so wie ich sie deklariert habe?
Wird das Ergebnis auch in der entsprechenden globalen Variable gespeichert?
Bleiben die Variablen A_Breite und Z_Breite mit ihren Werten erhalten?

Ich habe zwar 2 Bücher für C hier, aber über solche Probleme lassen sich keines der Bücher aus.
Auch nicht das Buch Microcomputertechnik mit Controllern der Atmel AVR-RISC-Famlie - Programmierung in Assembler und C-
In dem Buch wird auch fast nur über Registeranwendungen geschrieben, die mich aber für dieses Problem nicht interessieren sondern reine Mathematik in C.

prinf funktioniert bei mir nicht (schon probiert), also kann ich es nicht auf dem Bildschirm ausgeben und kontrollieren.
Register und Ports fallen auch flach, denn die errechnete Zahl kann ja ewig lang sein.
In meinem Beispiel benutzt der Compiler laut Assemblerlisting wohl 4 Byte für je einen double-Wert mit Komma.

Gruss

Gerd

Benutzeravatar
Kerli
Beiträge: 1456
Registriert: So Jul 06, 2008 10:17 am
Wohnort: Österreich
Kontaktdaten:

Re: Ein Versuch in C

Beitrag von Kerli » Fr Jun 05, 2009 7:54 pm

GerdV hat geschrieben:Meine Frage zielte bislang immmer darauf hin, ob das Ergenis meiner Rechenfunktion, bislang ja nur die Subtraktion, fest im RAM erhalten bleibt und ob das so mit den Werten als globale Variablen funktioniert.
Die Frage ist bislang mir noch nicht verständlich beantwortet worden.
Ja, solange das RAM unter Strom steht und man die Werte nicht überschreibt bleiben sie erhalten. Auch das mit den globalen Variablen funktioniert genauso. Alle globalen Variablen kannst du in Funktionen in der selben Datei, die nach der Deklaration der Variable kommen verwenden.
GerdV hat geschrieben:Es steht nichts lesbares drin ...wenigstens nicht für mich im Moment, auch kein entsprechender Binärcode, ASCII schon garnicht.
Ich weiss nicht wie im Maschinencode eine Variable double abgespeichert bzw. aufgelöst wird.
Du musst beachten das 'double'-Zahlen normalerweise 8 Byte groß sind und als Gleitkommazahl gespeichert werden. Gleitkommazahlen sind speziell kodiert und bestehen aus einem Vorzeichenbit gefolgt von 11 Bit für den Exponenten und weiteren 52 Bits für die Mantisse. Genauere Informationen darüber findest du zb. auf Wikipedia

Zum Auffinden im Speicher wäre es vielleicht etwas einfacher wenn du zb 'int' verwenden würdest, da dort eine einfache binäre Kodierung bzw. das Zweier-Komplement verwendet wird.
"Make it idiot-proof and someone will invent an even better idiot." (programmers wisdom)

OpenGL Tutorials und vieles mehr rund ums Programmieren: http://www.tomprogs.at

Antworten