C tut weh!

Am 10.10.2019 um 12:42 schrieb Wolfgang Allinger:
So here is the pain: Hier ist der Schmerz:

(*(void(*)())0)()

ok, ein böses Beispiel, wie übel C sein kann.
[...]
Hint:
In Forth 2 Worte (auch interaktiv) und in Z80 Assembler 2 Befehle.
Beides schon oft gebraucht :p

Und nun zum Vergleich:

double det = p*p/4.0 - q;
if (det >= 0.0) {
double sol1 = -p/2 - sqrt(det);
double sol2 = -p/2 + sqrt(det);
printf("solutions = %f, %f\n", sol1, sol2);
} else {
printf("no solution\n");
}

Was braucht man im richten Leben öfter, das Lösen von Rechen- oder
Logikaufgaben jedweder Art oder den Sprung an den Resetvektor? Wieviele
Befehle sind eine Rechenaufgabe in Z80-Assembler? Wie lesbar ist die
Rechenaufgabe in Forth? Wieviel Transistoren braucht die Rechenaufgabe
als HW-Schaltung?


Stefan
 
Gerhard Hoffmann wrote:
Am 10.10.19 um 12:42 schrieb Wolfgang Allinger:

Hint:
In Forth 2 Worte (auch interaktiv) und in Z80 Assembler 2 Befehle.
Beides schon oft gebraucht :p

RST 0 ist ja wohl nur 1 Byte und nicht 2 Befehle.

Selbst JP 0000 ist nur 1 Befehl.

Ja, aber drei Bytes. Das geht an die Substanz.
Wenn ich wissen will, ob das Programm von einem normal
denkenden Menschen geschrieben ist, dann durchsuche ich
den Hexcode nach '3e 00' und 'fe 00'.

MfG
hjs
 
Am 10.10.19 um 12:42 schrieb Wolfgang Allinger:

Hint:
In Forth 2 Worte (auch interaktiv) und in Z80 Assembler 2 Befehle.
Beides schon oft gebraucht :p

RST 0 ist ja wohl nur 1 Byte und nicht 2 Befehle.

Selbst JP 0000 ist nur 1 Befehl.

/Gerhard
 
Johannes Bauer wrote:
On 10.10.19 16:50, Hans-Juergen Schneider wrote:
Johannes Bauer wrote:

On 10.10.19 12:42, Wolfgang Allinger wrote:
C_hurts sounds in German like: Tooths hurts ( C_tut_weh :)

in Z80 Assembler ja auch.

Aus reiner Neugier täte mich mal interessieren, wieso
das irgendwie Ăźbel sein sollte.

Es scheint ßbel, wenn einem ein Allingersches Realitätsverzerrungsfeld
die Birne vernebelt.

Mal abgesehen davon: Was sollte beim Z80 nicht richtig sein?

MfG
hjs
 
On 10.10.19 18:47, Hans-Juergen Schneider wrote:

On 10.10.19 12:42, Wolfgang Allinger wrote:
C_hurts sounds in German like: Tooths hurts ( C_tut_weh :)

in Z80 Assembler ja auch.

Aus reiner Neugier täte mich mal interessieren, wieso
das irgendwie ßbel sein sollte.

Es scheint ßbel, wenn einem ein Allingersches Realitätsverzerrungsfeld
die Birne vernebelt.

Mal abgesehen davon: Was sollte beim Z80 nicht richtig sein?

Hmmm ich verstehe deine Frage nicht ganz, ich glaube wir reden
aneinander vorbei.

Ich nehme an, es ist Konsens, dass es gut und sogar notwendig ist, in
Programmen SprĂźnge auslĂśsen zu kĂśnnen. Bei einer Hochsprache kann man
sich vielleicht darĂźber streiten, ob das Abstraktionsniveau so ein
Hardwaredetail exponieren sollte oder nicht. C macht das, andere
Programmiersprachen nicht. Beides ist okay.

Ich glaube niemand hat bestritten, dass es vĂśllig normal ist, dass man
in Z80-Assembler (oder jedem beliebigen nicht-esoterischen ASM-Dialekt)
einen absoluten Sprung auslĂśsen kann.

Gruß,
Johannes

--
"Performance ist nicht das Problem, es läuft ja nachher beides auf der
selben Hardware." -- Hans-Peter Diettrich in d.s.e.
 
On 10/10/2019 14:54, Johannes Bauer wrote:
On 10.10.19 14:40, Helmut Schellong wrote:

Also sozusagen ein Reboot "auf manchen Maschinen"?

Undefiniertes Verhalten. Alles kann, nichts muss.

Nein, die Konstruktion wird oft sinnvoll angewandt.

Doch, und zwar KRISTALL klar:

"§6.5.3.2 Address and indirection operators" sagt:

"If an invalid value has been assigned to the pointer, the behavior of
the unary * operator is undefined."

Und dann noch eine Fußnote:

"Among the invalid values for dereferencing a pointer by the unary *
operator are a null pointer, [...]"

Da gibt es Ăźberhaupt keinen Zweifel, einen Nullpointer in C zu
dereferenzieren ist definitiv undefiniertes Verhalten.

Ich hatte den Wert der Konstante 0 kritiklos vom OP Ăźbernommen,
in der Annahme, daß da implementations-definiertes Verhalten
in einer freestanding_implementation wirkt.

An integer may be converted to any pointer type.
Except as previously specified, the result is implementation-defined,
might not be correctly aligned, might not point to an entity of the
referenced type, and might be a trap representation.67)

Undefiniertes Verhalten erlaubt sogar ein Verhalten, das so erscheint,
als sei es definiertes Verhalten.
Der Standard hat es halt nicht definiert - aber die Implementation.

Das Dereferenzieren ist undefiniertes Verhalten, auch wenn es
Implementierungsabhängig ßblicherweise das macht, was du beschreibst.

Eben.

Häufiger kommt so etwas vor:
# define NEsp0ARR(S,A)  (sizeof(((S*)0)->A)/sizeof(*((S*)0)->A))

sizeof() ist ohnehin implementation defined, ganz unabhängig was man
reinsteckt.

Das kann so nicht gesagt werden!
Der WERT, den sizeof liefert, ist natĂźrlich implementations-definiert.
Aber alles Sonstige nicht.


--
Mit freundlichen Grüßen
Helmut Schellong var@schellong.biz
www.schellong.de www.schellong.com www.schellong.biz
http://www.schellong.de/c.htm
 
On 10/10/2019 15:31, Heinz Saathoff wrote:
Johannes Bauer schrieb:


Häufiger kommt so etwas vor:
# define NEsp0ARR(S,A)  (sizeof(((S*)0)->A)/sizeof(*((S*)0)->A))

sizeof() ist ohnehin implementation defined, ganz unabhängig was man
reinsteckt.

Wobei sizeof vom Compiler ausgewertet wird und nicht zur Laufzeit.
Deshalb wird da auch nichts wirklich dereferenziert.

Bei VLA wird zur Laufzeit berechnet.
Der letzte Satz stimmt.


--
Mit freundlichen Grüßen
Helmut Schellong var@schellong.biz
www.schellong.de www.schellong.com www.schellong.biz
http://www.schellong.de/c.htm
 
On 10/10/2019 16:56, Hans-Peter Diettrich wrote:
Am 10.10.2019 um 14:40 schrieb Helmut Schellong:

Nein, die Konstruktion wird oft sinnvoll angewandt.

    (* (void(*)())0 )()

Die Konstante 0 wird gecastet in einen Funktionspointer
auf eine Funktion, die nichts retourniert.
Dann wird dieser Funktionspointer explizit dereferenziert
und die Funktion mit Adresse 0 aufgerufen.

Geht das nicht einfacher mit
  goto 0;
???

Der Operand zu goto muß ein konstanter Label-Name sein.


--
Mit freundlichen Grüßen
Helmut Schellong var@schellong.biz
www.schellong.de www.schellong.com www.schellong.biz
http://www.schellong.de/c.htm
 
On 10.10.19 20:26, Helmut Schellong wrote:

Ich hatte den Wert der Konstante 0 kritiklos vom OP Ăźbernommen,
in der Annahme, daß da implementations-definiertes Verhalten
in einer freestanding_implementation wirkt.

An integer may be converted to any pointer type.
Except as previously specified, the result is implementation-defined,
might not be correctly aligned, might not point to an entity of the
referenced type, and might be a trap representation.67)

Die 0 ist hier aber ein Spezialfall, da (void*)0 eben die null pointer
contant ist. §6.3.2.3.3:

"An integer constant expression with the value 0, or such an expression
cast to type void *, is called a null pointer constant."

Undefiniertes Verhalten erlaubt sogar ein Verhalten, das so erscheint,
als sei es definiertes Verhalten.
Der Standard hat es halt nicht definiert - aber die Implementation.

Naja, der C-Standard differenziert schon genau zwischen undefined
behavior und implementation-defined behavior. Und einen NULL-pointer zu
dereferenzieren ist ersteres.

Aber klar, letztlich wird es zu Code compiliert und der wird in aller
Regel konsistent irgendwas tun. Nur was, das ist halt nicht definiert
und der C-Standard erlaubt prinzipiell alles: Abort z.B..

sizeof() ist ohnehin implementation defined, ganz unabhängig was man
reinsteckt.

Das kann so nicht gesagt werden!
Der WERT, den sizeof liefert, ist natĂźrlich implementations-definiert.
Aber alles Sonstige nicht.

Hmja, da war ich nicht klar genug in meiner Aussage. Was ich meinte war
§6.5.3.4.4:

"The value of the result is implementation-defined, and its type (an
unsigned integer type) is size_t".

FĂźr dein vorliegendes Beispiel ist das aber eh zweitranging, weil
§6.5.3.4.2 ja schon sagt

"If the type of the operand is a variable length array type, the operand
is evaluated; otherwise, the operand is not evaluated and the result is
an integer constant."

Viele Grüße,
Johannes

--
"Performance ist nicht das Problem, es läuft ja nachher beides auf der
selben Hardware." -- Hans-Peter Diettrich in d.s.e.
 
Johannes Bauer wrote:
On 10.10.19 18:47, Hans-Juergen Schneider wrote:

On 10.10.19 12:42, Wolfgang Allinger wrote:
C_hurts sounds in German like: Tooths hurts ( C_tut_weh :)

in Z80 Assembler ja auch.

Aus reiner Neugier täte mich mal interessieren, wieso
das irgendwie ßbel sein sollte.

Es scheint ßbel, wenn einem ein Allingersches Realitätsverzerrungsfeld
die Birne vernebelt.

Mal abgesehen davon: Was sollte beim Z80 nicht richtig sein?

Hmmm ich verstehe deine Frage nicht ganz, ich glaube wir reden
aneinander vorbei.

Ich nehme an, es ist Konsens, dass es gut und sogar notwendig ist, in
Programmen SprĂźnge auslĂśsen zu kĂśnnen.

Ja, ich hatte gedacht, dass es besondesrs böse sein könnte,
ausgerechnet eine Sprung nach 0x0000 auszuführen.

MfG
hjs
 
On 10.10.19 12:42, Wolfgang Allinger wrote:
C_hurts sounds in German like: Tooths hurts ( C_tut_weh :)

So here is the pain: Hier ist der Schmerz:

(*(void(*)())0)()

ok, ein bĂśses Beispiel, wie Ăźbel C sein kann.

Ich mag es, wenn ich sowas machen kann. Auch wenn das ein Beispiel dafĂźr
ist, wie man es nicht macht. Denn 10 Jahre später wird das Programm auf
einen andern Controller portiert, dessen Reset-Adresse nicht 0 ist...

Natürlich kann man sich in C leicht in den Fuß schiessen. In Java nicht
ganz so einfach, aber da ist dann gleich das ganze Bein weg ;-)

....

> In Forth 2 Worte (auch interaktiv)

Was? Sprung nach NULL, Reset-Simulation, auf welcher Plattform?

> und in Z80 Assembler 2 Befehle.

Da ist der Prozessor ja bekannt ;-)

Gruß,
Falk
--
Microsoft ist aus einer Kooperation der Borg und der Ferengi
entstanden.
Leider arbeiten die Ferengi in der Entwicklungsabteilung und die Borg im
Marketing
 
On 10/10/2019 20:53, Johannes Bauer wrote:
On 10.10.19 20:26, Helmut Schellong wrote:

Undefiniertes Verhalten erlaubt sogar ein Verhalten, das so erscheint,
als sei es definiertes Verhalten.
Der Standard hat es halt nicht definiert - aber die Implementation.

Naja, der C-Standard differenziert schon genau zwischen undefined
behavior und implementation-defined behavior. Und einen NULL-pointer zu
dereferenzieren ist ersteres.

Ich kenne Implementationen, die viele Fälle, fßr die der Standard
UB nennt, definieren, also das UB des Standards entfernen.

Der Standard verlangt von Implementationen fast nur, daß ein
'strikt konformes' Programm entsprechend funktionieren muß.
DarĂźber hinaus darf eine Implementation beliebig viel definieren.
Die Implementation muß ihre Erweiterungen in einem Dokument beschreiben.

Beispielsweise darf der Ausdruck ((void*)0) jeden beliebigen
Wert haben (im Adressenkontext).
Der REALE Nullpointer darf folglich z.B. den Wert 0xCB haben.
Je nach Typ dĂźrfen es verschiedene Werte sein.


--
Mit freundlichen Grüßen
Helmut Schellong var@schellong.biz
www.schellong.de www.schellong.com www.schellong.biz
http://www.schellong.de/c.htm
 
On 10 Oct 19 at group /de/sci/electronics in article EvbIW$mjQoB@allinger-307049.user.uni-berlin
<all2001@spambog.com> (Wolfgang Allinger) wrote:

C_hurts sounds in German like: Tooths hurts ( C_tut_weh :)

So here is the pain: Hier ist der Schmerz:

(*(void(*)())0)()

ok, ein böses Beispiel, wie übel C sein kann.

Ich weiss was es macht :) Wissen die C-progger das auch?
Sogar sinnvoll auf vielen CPUs.

Hint:
In Forth 2 Worte (auch interaktiv) und in Z80 Assembler 2 Befehle.
Beides schon oft gebraucht :p

In HW eine Taste und/oder ein WW-Pfosten Pärchen.
Auch oft gebraucht :)

Nun auf, ich warte :eek:

THX für die rege Beteiligung :)

ja es ist ein Sprung an die Adresse 0. Ist bei vielen CPUs der RESET
Vektor. (8080 ... Z80... etc.)

Also in FORTH:
0 EXECUTE

In Z80:
LD HL,0
JP (HL)

Ja ich weiss, dass auch ein JP 0 geht, entspricht eben einem GOTO 0, bzw.
RST 0 geht auch. Aber die machen nicht exact das gleiche, wie der C-
Konstrukt.

k.a. ob C GOTO hat, vermutlich nicht, denn obiger C-Konstrukt stammt aus
einem Programm, was unter bestimmten Umständen einen RESET auslösen soll.
(nein ich habs nicht gefunden, wurde mir vor Äonen angetragen. War aber
keine DummenFa)

BTW Dumm: unser (vor)lauter Dummbatz Johannes Bauer auf dem Mt. Stupid(1)
rumkrakelend, sollte sich noch hinter die Ohren schreiben, dass man in
jedem uC nach dem Reset alle HW Register auf den gleichen Stand wie nach
einem echten RESET setzt. Besonders auch die undefinierten
Registerzustände (auch das gibt es!) in einen ordentlichen Zustand bringt
und erst dann mit seiner Applikation anfängt und danach alles auf den
Zustand setzt, wie es die Applikation erfordert. Das sorgt dafür, dass
alle externen Signale immer in der gleichen Reihenfolge hochkommen und
keine lustige Spikes irgendeinen externen Baustein durcheinanderbringen.

Das erspart mächtig Ärger, ist zwar etwas Arbeit, aber das lohnt sich.

BTW2 taugen in fast allen uC die HW Reset nichts, daher hab ich IMMER
einen externen RST Baustein in meinen Entwicklungen eingebaut, nachdem ich
es auf die harte Tour lernen musste. IIRC wars ein 80531 und auch der
ADuC812 hatte einen grauenhaften HW RST. Schande über deren Entwickler.

BTW3 der eine oder andere uC macht auch beim runtertorkeln der Spannung
noch lustige Dinge. 8031 schreiben dabei noch gerne Murks in ein externes
Flash, meist an die Adresse 0 bzw. Block 0. Auch das verhindert eine ext.
RST Baustein, der die RST Leitung auf GND zieht.

Ich weiss nicht, ob die internen RST besser geworden sind, da ich das
nicht mehr ausprobueren wollte. s.o. IMMER ext. RST Baustein.

Ich weiss nicht mehr, welchen Baustein ich genommen habe.
Habe alle AKten vor der Auswanderung verkauft.

Ich hab keine Lust, auf meiner uralt Plattensammlung noch was zu suchen,
ob da noch Schaltpläne und Stücklisten zu finden sind.

IIRC war das irgend ein 3pin SMD aus der Schweiz. Funzte auch einwandfrei
bei Schleichspannungsbetrieb :)

(1) Gruss von Dunning-Kruger















Saludos (an alle Vernünftigen, Rest sh. sig)
Wolfgang

--
Ich bin in Paraguay lebender Trollallergiker :) reply Adresse gesetzt!
Ich diskutiere zukünftig weniger mit Idioten, denn sie ziehen mich auf
ihr Niveau herunter und schlagen mich dort mit ihrer Erfahrung! :p
(lt. alter usenet Weisheit) iPod, iPhone, iPad, iTunes, iRak, iDiot
 
Falk Willberg schrieb:

On 10.10.19 12:42, Wolfgang Allinger wrote:
C_hurts sounds in German like: Tooths hurts ( C_tut_weh :)

So here is the pain: Hier ist der Schmerz:

(*(void(*)())0)()

ok, ein böses Beispiel, wie übel C sein kann.

Ich mag es, wenn ich sowas machen kann. Auch wenn das ein Beispiel dafür
ist, wie man es nicht macht. Denn 10 Jahre später wird das Programm auf
einen andern Controller portiert, dessen Reset-Adresse nicht 0 ist...

Das Problem stellt sich so meist nicht. Wenn man für einen konkreten
Controller programmiert, dann gibt es notwendigerweise
Controller-spezififischen Code, der so nicht auf anderen Controllern
laufen wird und den man immer anpassen muß. Die Lösung ist, diesen
Code möglichst abgeschottet in separaten Übersetzungseinheiten /
Librarys zu verpacken und ein sauberes Interface zu definieren.
Im spezifischen Code darf dann auch nicht portabel programmiert
werden.


- Heinz
 
On 11.10.19 03:34, Wolfgang Allinger wrote:

rumkrakelend, sollte sich noch hinter die Ohren schreiben, dass man in
jedem uC nach dem Reset alle HW Register auf den gleichen Stand wie nach
einem echten RESET setzt. Besonders auch die undefinierten
Registerzustände (auch das gibt es!) in einen ordentlichen Zustand bringt

Musst du dich denn wirklich jedes Mal zum Brot machen indem du deutlich
machst, dass deine Mikrocontroller-Kenntnisse auf dem Stand von Anno
1980 stehen geblieben sind?

Auf deinen Spielzeugcontrollerchen und trivialen Demoapplikationen mag
das so gehen. Die verzeihen dir ja sogar deinen grotesken Codepfusch und
die Raceconditions. Meistens geht das schon gut, da ist noch nie was
passiert, gell.

Controller von heute haben buchstäblich *tausende* von Registern, bei
denen man sich darauf verlassen kĂśnnen muss, dass sie mit einem Reset
ordentlich initialisiert werden, sonst ist die Hardware Murks. Ganz
abgesehen davon, dass es Register gibt, die sich nur in eine Richtung
initialisieren lassen (z.B. MPU). Da ist es schlicht nicht mĂśglich, die
zu verbiegen, nachdem sie einmal gesetzt wurden. Vermutlich hast du mit
solcher Peripherie aber noch nie gearbeitet, das wĂźrde zumindest deine
Ahnungslosigkeit erklären.

Lass es doch einfach sein, Wolfgang. Es ist einfach nur peinlich was du
vom Stapel lässt und wirklich gute 40 Jahre von der heutigen Realität
entfernt. Deine Tipps von gestern sind heute nur noch fĂźr die Tonne.

Immer gern,
Dein Johannes

--
"Performance ist nicht das Problem, es läuft ja nachher beides auf der
selben Hardware." -- Hans-Peter Diettrich in d.s.e.
 
Am 10.10.2019 um 19:28 schrieb Hans-Juergen Schneider:
Wenn ich wissen will, ob das Programm von einem normal
denkenden Menschen geschrieben ist, dann durchsuche ich
den Hexcode nach '3e 00' und 'fe 00'.

Um es genau solchen Reverse Engineering Leuten schwer zu machen, sollte
jeder halbwegs clevere Programmierer mit wechselnden Sprungtechniken
arbeiten und ausnutzen, was die Trickkiste so hergibt. :-D

W.
 
"Stefan Reuther" <stefan.news@arcor.de> schrieb im Newsbeitrag
news:qnntn5.4bs.1@stefan.msgid.phost.de...

Was braucht man im richten Leben öfter, das Lösen von Rechen- oder
Logikaufgaben jedweder Art oder den Sprung an den Resetvektor?

Was ist das für eine bescheuerte Frage ?

Wenn eine Programmiersprache etwas nicht bietet was man braucht,
und man es auch nicht nachrüsten kann,
dann ist sie für manche Jobs einfach unbrauchbar.

Wer lässt sich schon auf halb-durchdachtes ein mit dem er eines
Tages in einer Sackgasse steht aus der es kein heraus gibt ?

Da nimmt man doch lieber gleich eine vernünftige Basis die von
vielen anderen erprobt wurde, sich also i der Praxis bewiesen hat.
--
MaWin, Manfred Winterhoff, mawin at gmx dot net
Homepage http://flexiblebird.bplaced.net/
dse-FAQ: http://dse-faq.elektronik-kompendium.de/
 
Am 11.10.2019 um 11:34 schrieb MaWin:

Da nimmt man doch lieber gleich eine vernünftige Basis die von
vielen anderen erprobt wurde, sich also i der Praxis bewiesen hat.

Faustkeile!
 
On 10/11/2019 09:29, Johannes Bauer wrote:
On 11.10.19 03:34, Wolfgang Allinger wrote:

rumkrakelend, sollte sich noch hinter die Ohren schreiben, dass man in
jedem uC nach dem Reset alle HW Register auf den gleichen Stand wie nach
einem echten RESET setzt. Besonders auch die undefinierten
Registerzustände (auch das gibt es!) in einen ordentlichen Zustand bringt

Musst du dich denn wirklich jedes Mal zum Brot machen indem du deutlich
machst, dass deine Mikrocontroller-Kenntnisse auf dem Stand von Anno
1980 stehen geblieben sind?
[...]

Auch bei aktuellen uC funktioniert nicht immer alles makellos.

Bei uns wurde z.B. ein zusätzlicher externer WatchDog eingefßhrt.
Der interne war eben nicht ganz einwandfrei.
RST wird von einem BrownOut-IC betätigt.


--
Mit freundlichen Grüßen
Helmut Schellong var@schellong.biz
www.schellong.de www.schellong.com www.schellong.biz
http://www.schellong.de/c.htm
 
On 11.10.19 13:34, Helmut Schellong wrote:

Auch bei aktuellen uC funktioniert nicht immer alles makellos.

Bei uns wurde z.B. ein zusätzlicher externer WatchDog eingefßhrt> Der interne war eben nicht ganz einwandfrei.
RST wird von einem BrownOut-IC betätigt.

Hardware kann natĂźrlich immer Errata haben, aber ein nicht richtig
funktionierender WDT ist schon *richtig* Ăźbel. Das ist ja die *eine*
Komponente, die alles noch irgendwie retten soll, wenn in dem Rest Murks
passiert. Was war das denn fĂźr ein SoC?

Wenn ich mich nicht darauf verlassen kann, was mir im Datenblatt
garantiert wird (z.B. eben definierte Werte der Peripherie-Register und
dessen Zustand) dann habe ich da vermutlich noch ganz andere Probleme.

Es ist trotzdem schlicht auf einem modernen System unrealistisch,
lediglich in Software einen Reset ausfĂźhren zu wollen: Zum einen, dass
man mĂśglicherweise gar nicht die Privilegien dazu hat und an den
Reset-Vektor springen kann, wie man will und mag -- nur halt wegen
fehlender Berechtigungen das System nicht resetten darf. Zum anderen,
weil da im Hintergrund, parallel, dann z.B. noch irgendwo ein DMA wild
rumwurstelt oder der Interrupt-Controller "undefiniertet" konfiguriert ist.

Das macht echt einfach keinen Sinn und ist genau der Grund, wieso man
Ăźblicherweise eben einen echten (Software) Reset auslĂśsen kann.

Gruß,
Johannes

--
"Performance ist nicht das Problem, es läuft ja nachher beides auf der
selben Hardware." -- Hans-Peter Diettrich in d.s.e.
 

Welcome to EDABoard.com

Sponsor

Back
Top