AVR - Geschwindigkeit von Operationen und Variablentypen

Im Unterforum Microcontroller - Beschreibung: Hardware - Software - Ideen - Projekte

Elektronik Forum Nicht eingeloggt       Einloggen       Registrieren




[Registrieren]      --     [FAQ]      --     [ Einen Link auf Ihrer Homepage zum Forum]      --     [ Themen kostenlos per RSS in ihre Homepage einbauen]      --     [Einloggen]

Suchen


Serverzeit: 19 4 2024  07:28:13      TV   VCR Aufnahme   TFT   CRT-Monitor   Netzteile   LED-FAQ   Osziloskop-Schirmbilder            


Elektronik- und Elektroforum Forum Index   >>   Microcontroller        Microcontroller : Hardware - Software - Ideen - Projekte


Autor
AVR - Geschwindigkeit von Operationen und Variablentypen

    







BID = 689650

Teletrabi

Schreibmaschine



Beiträge: 2317
Wohnort: Auf Anfrage...
 

  



Moin,

wenn perl es nich im anderen thread haben will, dann halt nochmal neu:

nochmal ein paar Fragen.
Braucht der AtMega zum Subtrahieren (bzw. simples Dekrementieren) deutlich länger als für Additionen? Oder für Schiebeoperationen nach links statt rechts?
Hatte mir was zum Einlesen von Daten aus einem PISO-Schieberegister gestrickt. Zunächst mit einer Schleifenvariable initialisiert mit j= 7 und j-- am Schleifenende, während hereinkommende Bits nach einer Maskierung mit (1<<j) das Eingangsbyte von links nach rechts füllen. Dabei reagiert der Controller jedoch subjektiv deutlich langsamer auf externe Ereignisse. Gut, zunächst lag es an einem Gewohnheits-j++ , sodass die Schelifendurchläufe in die falsche Richtung gezählt wurden, aber auch korrigiert erscheint mir die Version langsamer zu laufen als eine inkrementierende Schleifenvariable und Start- Maskierung mit 0b10000000, welche dann um j nach rechts geschoben wird?!


Was hat es mit Variablentypen wie "uint8_t" auf sich verglichen mit der 08/15-Variante "int"? Was ist der Vorteil der längeren Form, solange man im 8-bit-Bereich bleibt? Ist "int" auf 8 bit beschränkt oder einen anderen Standardwert oder entscheidet da der Compiler nach Gefühl, wieviel Platz er dafür vorsieht?

Woher stammt das Bit, mit dem bei Verschiebeoperationen die Randposition aufgefüllt wird? hatte eigentlich zunächst ein Füllen mit Nullen erwartet, das scheint aber nicht immer der Fall zu sein. wird da das carry-bit mit reingezogen? ist das 'nen zufallswert? (Sofern es sich nicht aus "int" = länger als 8 Bit erkärt)

[ Diese Nachricht wurde geändert von: Teletrabi am 12 Mai 2010 13:17 ]

BID = 689670

DonComi

Inventar



Beiträge: 8605
Wohnort: Amerika

 

  

Hallo,


Zitat :

Braucht der AtMega zum Subtrahieren (bzw. simples Dekrementieren) deutlich länger als für Additionen? Oder für Schiebeoperationen nach links statt rechts?

Mit avr-gcc <Standardparameter> -g übersetzen und mittels avr-objdump -S <fertiges Programm, z.b. main.o> das Disassembling anschauen.

Dekrementieren und Inkrementieren dauert je einen Taktzyklus (inc, dec),wobei oft auch subi genutzt wird. Mit subi kann man dekrementieren, inkrementieren, mehr als 1 addieren und subtrahieren. Der Trick ist, dass man einfach mit dem Komplement subtrahiert, wenn man addieren will
Schiebeoperationen um ein Bit nach links oder recht dauern ebenfalls einen Maschinenzyklus, also z.B. bei 1MHz nur eine Mikrosekunde. Suche im Disassembling nach "rol" bzw. "ror" bzw. "lsl" und "lsr".
Eventuell wirst du auch nur adc, rx, rx finden, denn das ist genau die Schiebeoperation n+n = 2n = n<<1.

Danach mal das ganze mit der Option "-Os" oder alternativ "-O2" kompilieren - das Programm wird deutlich kleiner und schneller.


Zitat :

während hereinkommende Bits nach einer Maskierung mit (1<<j)

Nicht gut. Das können AVRs nicht in Hardware. Die kennen nur rol, ror, lsl, lsr und die arithmetischen Version.
Für die fettgedruckte Variante wird eine Funktion aus der Libc eingebunden -> groß und lahm.
Versuche, das zu umgehen. Alternativ kannst du den Codeschnipsel mal posten, da findet sich immer eine Alternative.


Zitat :

Was hat es mit Variablentypen wie "uint8_t" auf sich verglichen mit der 08/15-Variante "int"

uint8_t ist eine Typdefinition aus inttypes.h und lautet in etwa:
typedef unsigned char uint8_t;

Durch diese Typdefinitionen werden die Datentypen schöner und eindeutiger. int ist nicht immer 16 oder 32 (oder 64) Bit lang. Das kommt auf die Maschine an. Per Definition muss es aber immer mindestens 16 Bit sein (es sei dann, man trickst(*)). Daher lieber den "nativen" Typen mit 8 Bit (uint8_t) oder unsigned char nehmen.


Zitat :

entscheidet da der Compiler nach Gefühl, wieviel Platz er dafür vorsieht?

Bei höherer Optimierung wird meist 8 Bit draus, solange der Compiler den Wertebereich statisch ermitteln kann - wenn nicht, 16 Bit.
Mit der Compileroption -mint8 (*das ist der Trick ) kannst du int auf 8 Bit festlegen, ist aber kein Standard.
Teste dies mit sizeof(int).


Zitat :

Woher stammt das Bit, mit dem bei Verschiebeoperationen die Randposition aufgefüllt wird? hatte eigentlich zunächst ein Füllen mit Nullen erwartet, das scheint aber nicht immer der Fall zu sein. wird da das carry-bit mit reingezogen? ist das 'nen zufallswert? (Sofern es sich nicht aus "int" = länger als 8 Bit erkärt)

Schaue bitte im Disassembling nach, was der Compiler daraus gemacht hat. Nutzt er adc rx, rx bzw. rol, rutscht das Carrybit ins LSB. Ich kenne es aber aus unzähligen Programmen, dass nur sehr selten vom Compiler Mist gebaut wird. Im Rest der Fälle (99% ) ist der Programmierer schuld.
Rol und ror sind vor allem gut, wenn man Bits in Datentypen wie uint16_t (unsigned short (int)) schieben will - das geht dann über das Carrybit.
Daher könnte dieser Fehler dann prinzipiell auch kommen; mehr als Raten bringt aber das Disassembling

---
Musste etwas editieren, da ich seit ca. 32 Stunden wach bin


_________________



[ Diese Nachricht wurde geändert von: DonComi am 12 Mai 2010 15:38 ]

BID = 689672

perl

Ehrenmitglied



Beiträge: 11110,1
Wohnort: Rheinbach


Zitat :
Braucht der AtMega zum Subtrahieren (bzw. simples Dekrementieren) deutlich länger als für Additionen? Oder für Schiebeoperationen nach links statt rechts?
Nein, aber es gibt schlechte Programmierpraktiken.

Zitat :
Einlesen von Daten aus einem PISO-Schieberegister gestrickt.
Meinst du jetzt Auslesen oder ein SIPO-SR?


Zitat :
während hereinkommende Bits nach einer Maskierung mit (1<<j) das Eingangsbyte von links nach rechts füllen.
Also doch wohl ein SIPO.
Und da haben wir auch schon die schlechte Programmierung: Zum Abspeichern eines jeden Bits wird zuerst eine neue Maske berechnet. Da das j eine Variable ist, die auch wirklich verändert wird, und keine Konstante, weiß der Compiler im vorhinein nicht, welchen Wert j bei jedem Schleifendurchlauf haben wird, und muss also Code generieren, der jedesmal die Maske erst in die gewünschte Position schiebt.
Einfacher und viel schneller ist es stets die gleiche Maske zu verwenden, also eine Konstante, und lediglich das Empfangsregister vorher oder nachher um 1 Bit zu rotieren.

Wer die ultimative Geschwindigkeit braucht, programmiert übrigens keine Schleifen, sondern schreibt platzfressenden Inline-Code.


Zitat :
Woher stammt das Bit, mit dem bei Verschiebeoperationen die Randposition aufgefüllt wird?
Das kommt darauf an, ob es sich um eine Berechnung handelt, die der Compiler vorab ausführt, oder ob das der Prozessor zur Laufzeit macht.
Beim Linksschieben eines 8Bit Registers (LSL), wird hardwaremäßig eine 0 in das Bit 0 gezogen, und das Bit 7 landet im Übertrag.
Wenn man das Linksschieben macht um eine längere Zahl mit 2 zu multiplizieren, wendet man anschliessend ROL (Rotate Left through Carry) an, bzw. der Compiler generiert entsprechenden Code. Dabei wandert ebenfalls das Bit 7 (jetzt aber mit dem Stellenwert 15 oder höher) ins Carry und das Bit 0 (jetzt Bit8 oder höher) wird mit dem alten Carry aufgefüllt.
Auf diese Weise kann man beliebig lange Zahlen durch LSL,ROL,ROL,ROL, ... mit 2 multiplizieren.

Beim Rechtsschieben, der Division durch 2, wendet man auf das höchste Byte zunächst ASR (Aritmetic Shift Right) an. Dabei landet ganz normal das Bit0 im Carry, und alle anderen auf der nächst niedrigen Bitposition, aber das Bit 7 bleibt erhalten. Z.B. wird aus 80h nun C0h. Auf diese Weise kann man auch negative Zahlen durch 2 dividieren. Anschliessend entsprechend dem normalen Verfahren ROR Instruktionen, bei denen das Cy in Bit 7 kommt und Bit 0 ins Cy.
Zum Rechtsschieben einer Zahl im 2er Komplement also ASR,ROR,ROR,ROR...
Wenn der Compiler weiß, dass das eine nicht negative Zahl ist (unsigned), dann ersetzt er das erste ASR durch ROR, wobei zuvor Carry auf 0 gesetzt wird (CLC).

P.S.:
Sehe gerade dass DonComi auch schon etwas dazu geschrieben hat. Hoffentlich ergänzen sich beide Beiträge anstatt zu verwirren.

BID = 689705

Teletrabi

Schreibmaschine



Beiträge: 2317
Wohnort: Auf Anfrage...

Moin,

nee nee, PISO meint schon PISO. Geht darum, in der irgendwann mal angedachten Verwendung, für die man sich da gerade reinfuchst bis zu 128 Taster (mit ein bisschen Reserve, zwingend verplant sind eher 40, optionales Gedöns bis etwa 80) auszulesen. Einlesen im sinne von in den controller hinein oder aus dem register seriell hinaus in den Controller.

Ähnliche Geschichte auf der Ausgangsseite, da kann man die 40 zwingenden Taster schonmal mit etwa 80 Ausgängen für Schaltfunktionen und etwa ebenso vielen unabhängig davon angeschlossenen Rückmelde-Leuchten übersetzen.

Also jeweils schöne SR-Kaskaden als Portexpander.

Zeitbedarf ist reichlich unkritisch, Reaktionszeit von einer Zehntel Sekunde wäre nett, bis ein oder zwei Sekunde noch hinnehmbar.


> Nicht gut. Das können AVRs nicht in Hardware. Die kennen nur rol,
> ror, lsl, lsr und die arithmetischen Version.
> Für die fettgedruckte Variante wird eine Funktion aus der Libc
> eingebunden -> groß und lahm.
> Versuche, das zu umgehen. Alternativ kannst du den Codeschnipsel
> mal posten, da findet sich immer eine Alternative.

Ja, perls Variante mit schieben des eingangsbyts dürfte da besser sein.


> Ich kenne es aber aus unzähligen Programmen, dass nur sehr selten vom
> Compiler Mist gebaut wird. Im Rest der Fälle (99% ) ist der
> Programmierer schuld.

jo, hatte nur andere Reaktion erwartet. Mit int als 16bit und dem carryflag beim schieben ist das beobachtete Verhalten schon doppelt erklärt.



Wie legt man denn am besten die 128 Taster-Zustände im Controller ab? Als Bitfeld? Kann man Bitfelder mit 'ner Variable als Laufindex ansprechen?
Bei Structures geht das nich ohne weiteres, oder? Da muss man mit der konkreten Zielbezeichnung auf den gewünschten Teil drauf zugreifen?

Besser mehrere Eingang-Abbilder (aktuelle und x vorhergehende Zustände) anlegen um Zustandsänderungen zu erkennen oder besser jeweils einem Taster ein Byte zuordnen, dass neben dem aktuellen die alten Zustände beinhaltet?

BID = 689766

DonComi

Inventar



Beiträge: 8605
Wohnort: Amerika

Moin,


Zitat :

Also jeweils schöne SR-Kaskaden als Portexpander.

Jau, mach ich auch häufig so. 4094 kann man ja beliebig kaskadieren und die Routinen dazu binde ich meist als Assemblercode ein - da kann man schon viel an Geschwindigkeit rausholen. Noch schneller geht es per SPI, ist ja nichts anderes als ein Schieberegister.


Zitat :

Zeitbedarf ist reichlich unkritisch, Reaktionszeit von einer Zehntel Sekunde wäre nett, bis ein oder zwei Sekunde noch hinnehmbar.

Kann man locker erreichen. Im Mikrosekundenbereich schafft man das sogar, also alles kein Problem .

Für PISO bietet sich auch sowas an: (MSB zuerst):

register uint8_t i = 8;
for( ; i; i-- )
{
 if( byte & 0x80 ) 
  set_bit(IO);
 else
  clear_bit(IO);
 byte <<= 1;
 /* Taktflanke erzeugen */
}


Man schiebt natürlich die Bits linksseitig ins Nirwana, aber wen interessiert das, wenn man das als Funktion implementiert, die eine Kopie eines Bytes bearbeitet.
Soll das LSB zuerst ausgegeben werden, schiebt man halt nach rechts und vergleicht mit 0x1.

In Assembler kann man meist noch nette Tricks anwenden, um sowas noch schneller zu machen, wobei es mittlerweile selbst bei sowas schwierig wird, den Compiler zu übertreffen. Da sind schon ziemlich intelligente Programme am synthetisieren.


Zitat :

Wie legt man denn am besten die 128 Taster-Zustände im Controller ab? Als Bitfeld? Kann man Bitfelder mit 'ner Variable als Laufindex ansprechen?

128 sind doch nett durch 8 teilbar -> nimm ein Feld/Array uint8_t der Größe 16Byte einmal für die aktuellen und einmal für die alten Werte.
Um Änderungen festzustellen reicht es ja, alle entsprechenden Masken einmal per XOR zu verknüpfen. Sind Änderungen festgestellt kann man noch den Wert des alten oder neuen hinzuziehen um Drücken oder Loslassen des Tasters zu bestimmen.

Sowas kann man gut mit laufendem Index realisieren.


Zitat :

Kann man Bitfelder mit 'ner Variable als Laufindex ansprechen?

Bitfelder gehören zwingend zu einer benutzerdefinierten Datenstruktur (struct). Man könnte es schon hinmurksen, dass man über 128 Bits, die in 16 Byte abgelegt sind, über Bitfelder zu iterieren, aber das kann man auch ohne Bitfelder und mit deutlich höherer Geschwindigkeit. Nur, dass du in der ersten Variante nicht ein Bit sondern gleich 8 Bit auf einmal maskierst -> viel schneller.


Zitat :

jeweils einem Taster ein Byte zuordnen, dass neben dem aktuellen die alten Zustände beinhaltet?

Ne, würd ich nicht machen. Der Aufwand des Speicherns ist höher. Mit den 128 Bits linear im Speicher geht das wesentlich flotter.

Um den Overhead abzuschätzen sollte man sich die Disassembling anschauen. Ich bin der Meinung, dass die Array-Variante die einzig sinnvolle ist (wegen diversen Gründen).

_________________

BID = 692569

Teletrabi

Schreibmaschine



Beiträge: 2317
Wohnort: Auf Anfrage...

Moin,

nochmal zum auffüllen beim schieben der Bits...
Hab mir hier mal was gestrickt, allerdings weiß ihc nich so recht, ob es da Probleme mit dem reinziehen vom Carry-Flag oder derlgeichen geben kann.




Code :


void _sr_read (char *ablage[_sr_in_length] )
//Zeiger auf Ablage-Array übernehmen
{

// --- PISOs mit Daten laden: ----
[...]

// --- Nutzdaten einlesen ---

char temp = 0;

for (char k = 0; k <= ((_sr_in_length - 1) /8); k++)
// Ablage-Array durchlaufen
{
temp = 0;
// Tempbyte rücksetzen

for (char l=7; 7>=0; l--)
// Bits von Tempbyte befüllen
{
temp = (temp << 1);
// Bits nach links verrücken
temp |= _sr_read_bit();
// Bit einlesen und rechts einfügen
}
*ablage[k] = temp;
// Inhalt von Temp an Position von Ablage-Zeiger schreiben

}

[...]

} // Ende _sr_read_input




Wenn ich erst das temp-Byte nach links schiebe, und dann oderverknüpfe mit der Rückgabe der _sr_read_bit-Funktion - kann dort beim Verschieben von rechts eine Eins in das temp-Byte rutschen und die Rückgabe von _sr_read_bit durch die VerORDERung übersteuern? Oder wird bei Linksshift rechts immer mit Nullen aufgefüllt?

Schiebe ich hingegen nach der Verknüpfung nach links, würde mir beim 8 Durchlauf ja alles eine Stelle zu weit nach links verrutschen.

BID = 692571

perl

Ehrenmitglied



Beiträge: 11110,1
Wohnort: Rheinbach


Zitat :
Wie legt man denn am besten die 128 Taster-Zustände im Controller ab?
Wahrscheinlich als Bitfeld um nicht zuviel Speicher zu verschwenden.
Du solltest nicht vergessen, dass bei einem Tastenfeld während der meisten Zeit überhaupt nichts passiert. Diesen Zustand kann mit minimalem Aufwand und sehr schnell verifizieren, indem man alte und neue Bitmap XORt. Dazu braucht du aber dann schon 256 Bit.
Wenn sich etwas ändert, kann man sich die Sache gründlicher anschauen und insbesondere die Timer für die Entprellung starten.
Da es nicht vorkommen wird, dass alle Tasten gleichzeitig gedrückt oder losgelassen werden, braucht man aber nicht für jedes Bit einen Timer vorzuhalten.

BID = 692584

Teletrabi

Schreibmaschine



Beiträge: 2317
Wohnort: Auf Anfrage...

Kann ich denn auf ein Bitfeld mit 'nem Laufindex zugreifen? Die Standardform sieht doch IIRC die Angabe er jeweiligen Bitvariablen vor, müsste man also für jedes Bit separat die Codezeile einhacken?

BID = 692674

DonComi

Inventar



Beiträge: 8605
Wohnort: Amerika

Also,

ein C-Bitfeld sieht irgendwie so aus (und kann nur zusammen mit einer Datenstruktur genutzt werden!):

struct xy =
{
  int bitfeld1:4;
  int bitfeld2:6;
  .
  .
  .
};



Das einzig nette an diesen Bitfeldern ist, dass man durchaus mehrere nicht zusammenhängende Daten in z.B. einer 32-Bit-Variablen ablegen, sie aber über symbolische Namen ordentlich ansprechen kann. Werden solche Felder aber nicht nativ (in Form von extra Hardware) vom Prozesor unterstützt, dann kann sowas massive Performanceeinbußen mitsichziehen. Auf AVRs alles andere als Empfehlenswert, zumindest nicht in der Form. Gibt Ausnahmen.

Perl meint mit Bitfeldern nicht direkt die Bitfelder aus C sondern vermutlich einen linearen Bereich im Speicher, wo die Tasterzustände abgelegt werden. Darüber kann man gut iterieren und das schrieb ich ja auch schon:
(wer lesen kann... )

Zitat :

128 sind doch nett durch 8 teilbar -> nimm ein Feld/Array uint8_t der Größe 16Byte einmal für die aktuellen und einmal für die alten Werte.
Um Änderungen festzustellen reicht es ja, alle entsprechenden Masken einmal per XOR zu verknüpfen. Sind Änderungen festgestellt kann man noch den Wert des alten oder neuen hinzuziehen um Drücken oder Loslassen des Tasters zu bestimmen.


Zu deinem Programmschnipsel:
Da sollte kein gesetztes LSB reinrutschen! Kannst du problemlos so machen .

Allerdings wird das da
  for (char l=7; 7>=0; l--)

wegoptimiert

_________________

BID = 692685

Teletrabi

Schreibmaschine



Beiträge: 2317
Wohnort: Auf Anfrage...

Okay, hatte das mit den Bitfeldern im struct{bla}-Sinne aufgefasst.

Da mir da auch noch keine Laufindex-Zugriffsvariante bekannt ist, erschienen sie mir ohnehin nicht so günstig. andererseits wollte ich sie an anderer Stelle nutzen. Daher nochmal ne frage zum Zigriff drauf - setzen/löschen dann einfach per struct_element.bitname = 1 bzw. 0? Oder muss man da noch irgendwie die 1 an die passende Bitposition rücken?
Kann man mit sowas wie struct_element >>=; die bits durchrücken oder steigt da der Compiler aus, weil eigentlich völlig unabhängige bits nur physisch zu einem byte zusammengepackt sind?



Liege ich in meinem Schnippsel richtig mit dem Wunsch, beim Funktionsaufruf einen Zeiger auf ein array von chars zu übernehmen? Oder ergibt das in der aufgeführten Form ein array von Zeigern auf chars? So ganz durchblicke ich da nich, wann es was ergibt bzw. wo das sternchen jeweils hingehört.

BID = 692822

DonComi

Inventar



Beiträge: 8605
Wohnort: Amerika

*ablage[k] = temp;      

Ist eher vewirrend und nicht nötig.
Faktisch entspricht das einem Zeiger auf einen Zeiger und das ist auf AVRs eh schon schlecht...

Ein einfacher Zeiger würde reichen. Du ließt dann immer acht Byte möglichst in eine Registervariable ein und immer, wenn ein gesamtes Byte gelesen wurde, kopierst du es mit *ziel = temp; in den Speicherbereich. Anschließend den Zeiger einmalig inkrementieren und erneut 8 Bits einlesen.
inline uint8_t
read_byte(void)
{
 register uint8_t
  buffer = 0,
  k = 0;

 for( k ... )
 { /* Bits eintakten */
 }

 return buffer;
}

void read(char *tg)
{
 register uint8_t k;

 for( k=0; k<16; k++ )
 {
  *tg = read_byte();
  tg++;
 }
}


So, oder so ähnlich könnte man das anstellen, wobei es natürlich andere Möglichkeiten gibt. Mit inline wird das ganze flotter, aber größer.

Dieser Code ist auf Geschwindigkeit ausgelegt. Sicherheit ist in C so eine Sache.
Ordentlicher wäre, dass man die Größe von tg angibt um nicht in illegale Speicherbereiche zu kritzeln und man würde einen Zeiger zurückgeben (NULL im Fehlerfall) sowie tg auf NULL prüfen.
Wie gesagt, das nur am Rande, fürs ordentliche Programmieren
Für Taster ist das völlig unwichtig.
Man könnte sogar so weit gehen, und gar keine Argumente (tg) übergeben sondern in fixe Bereich schreiben (schneller). Aber irgendwo muss man auch mal aufhören zu optimieren

Edit:

Zitat :

Kann man mit sowas wie struct_element >>=; die bits durchrücken oder steigt da der Compiler aus, weil eigentlich völlig unabhängige bits nur physisch zu einem byte zusammengepackt sind?

Wenn du die Bits über ihren Bezeichner rotieren lässt, bleiben sie im Rahmen des Bitfeldes und beeinflussen andere Bereiche nicht. ABER: C bietet genug (eklige) Möglichkeiten, das denoch zu machen.

Eine relativ saubere wäre z.B. das nutzen von unions. Man wähle Bitfelder mit einer Bitlänge von 1 und packt 8 in ein Byte, welches gleichzeitig eine union mit einem Byte bildet. Du kannst also auf die einzelnen Bits über deren Bezeichner zugreifen, aber alle Bits um z.B. eine Stelle nach links verrücken, indem du auf dem Bezeichners fürs Byte operierst.

Da lassen sich lustige Dinge veranstalten. Es gibt sogar entsprechende Wettbewerbe, wie man am geschicktesten mit solchen Verrenkungen umgeht.
Lieferst du sowas deinem Chef ab, wird der sich bedanken. Wenn andere sowas lesen und warten sollen, immer möglichst ordentlich und verständlich programmieren.



_________________


[ Diese Nachricht wurde geändert von: DonComi am 29 Mai 2010 22:25 ]


Zurück zur Seite 0 im Unterforum          Vorheriges Thema Nächstes Thema 


Zum Ersatzteileshop


Bezeichnungen von Produkten, Abbildungen und Logos , die in diesem Forum oder im Shop verwendet werden, sind Eigentum des entsprechenden Herstellers oder Besitzers. Diese dienen lediglich zur Identifikation!
Impressum       Datenschutz       Copyright © Baldur Brock Fernsehtechnik und Versand Ersatzteile in Heilbronn Deutschland       

gerechnet auf die letzten 30 Tage haben wir 24 Beiträge im Durchschnitt pro Tag       heute wurden bisher 0 Beiträge verfasst
© x sparkkelsputz        Besucher : 180924647   Heute : 1140    Gestern : 8415    Online : 495        19.4.2024    7:28
2 Besucher in den letzten 60 Sekunden        alle 30.00 Sekunden ein neuer Besucher ---- logout ----viewtopic ---- logout ----
xcvb ycvb
0.0806930065155