#include"eeprom.h" // Eigene EEPROM-Headerdatei einbinden
2
3
uint8_tee_mem[EESIZE]EEMEM=
4
{
5
[EE_DUMMY]=0x00,
6
[EE_VALUE1]=0x05,
7
[EE_WORD1L]=0x01,
8
[EE_WORD1H]=0x00,
9
[EE_VALUE2]=0xFF
10
};
Warum steht z.B. EE_DUMMY in eckigen Klammern bzw. welche Bedeutung hat
das? Ich als Anfänger kenne die eckigen Klammern in C bisher nur als
Index bei Arrays, was hier aber wohl nicht der Fall ist.
>#include"eeprom.h"// Eigene EEPROM-Headerdatei einbinden
2
>
3
>uint8_tee_mem[EESIZE]EEMEM=
4
>{
5
>[EE_DUMMY]=0x00,
6
>[EE_VALUE1]=0x05,
7
>[EE_WORD1L]=0x01,
8
>[EE_WORD1H]=0x00,
9
>[EE_VALUE2]=0xFF
10
>};
11
>
>> Warum steht z.B. EE_DUMMY in eckigen Klammern bzw. welche Bedeutung hat> das?
Scroll ein wenig nach oben im Tutorial. Da gibt es ein paar #define
dafür.
1
#define EE_DUMMY 0x000 // Dummyelement (Adresse 0 sollte nicht genutzt werden)
2
#define EE_VALUE1 0x001 // Eine Bytevariable
3
#define EE_WORD1L 0x002 // Eine Wordvariable (Lowbyte)
4
#define EE_WORD1H 0x003 // Eine Wordvariable (Highbyte)
5
#define EE_VALUE2 0x004 // Eine weitere Bytevariable
Letzendlich steht da
1
uint8_tee_mem[EESIZE]EEMEM=
2
{
3
[0x000]=0x00,
4
[0x001]=0x05,
5
[0x002]=0x01,
6
[0x003]=0x00,
7
[0x004]=0xFF
8
};
Das ganze ist die Initialisierung eines Arrays und jetzt sollte es nicht
mehr schwer zu erraten sein, was die Bedeutung ist.
Vielen Dank, wieder was dazugelernt. Diese Art von Arrayinitialisierung
hatte ich vorher noch nirgends gesehen. Selbst im berühmt/berüchtigten
K&R kann ich mich nicht dran entsinnen. Ich kannte bisher nur die
"normale" Initialisierung in geschweiften Klammern, wo sich die
Reihenfolge der Elemente automatisch ergibt.
Tobias schrieb:
> Vielen Dank, wieder was dazugelernt. Diese Art von Arrayinitialisierung> hatte ich vorher noch nirgends gesehen. Selbst im berühmt/berüchtigten> K&R kann ich mich nicht dran entsinnen. Ich kannte bisher nur die> "normale" Initialisierung in geschweiften Klammern, wo sich die> Reihenfolge der Elemente automatisch ergibt.
Ich bin mir nicht mal sicher, ob diese Initialisierung standardkonform
ist, oder ob das wieder mal eine Spezialität des gcc darstellt.
Es ist GNU-C.
Die Definition von EESIZE ist übrigens überflüssig bzw. wird besser
gemacht in der Form
1
#include<avr/io.h>
2
3
#define EESIZE (1+E2END)
denn in den I/O-Headern wird die letzte EEPROM-Adresse als E2END
definiert.
Die vorgeschlagene Lösung feste Adressen durch Anlegen eines Arrays
maximaler Größe zu definieren, gefällt mir übrigens überhaupt nicht.
Das Layout muss komplett handgefrickelt werden. Ausserdem müssen alle
Elemente vom gleichen Typ sein.
Der einzige Grund, feste Adressen zu vergeben, sind Sachen wie
Seriennummern oder Checksummen. Für alles andere ist die absolute
Adresse wurscht.
Daher wäre es besser, die üblichen Nutzdaten in einer Struktur zu
organisieren. Dies hat den weiteren Vorteil, daß man ein Abbild der
Struktur im RAM anlegen und problemlos darauf zugreifen kann. Die
Struktur wird im Startup einmal geladen und nur rückgeschrieben, wenn
sich was geändert hat (und Änderungen an der Konfiguration nicht
verworfen werden durch ein "Cancel", sondern wirklich dauerhaft ins
EPROM sollen).
Falls der seltene Fall vorliegt, daß man wirklich explizite Adressen
braucht, dann lässt man .eeprom zB an Adresse 3 beginnen und behandelt
die eine 16-Bit-Variable von Hand:
Kann man nicht einfach in einer .c-Datei des Projektes sowas wie
1
...
2
#define EEPROM_ADDRESS_1 33
3
#define EEPROM_ADDRESS_2 34
4
...
5
eeprom_write_byte((uint8_t*)EEPROM_ADDRESS_1,'T')
6
eeprom_write_byte((uint8_t*)EEPROM_ADDRESS_2,6)
7
...
schreiben? Es sollte dann doch eine .eep-Datei erstellt werden, die so
mit exakt dem Inhalt im EEPROM landet, ohne das eigentliche Programm im
Flash auch nur irgendwie zu belasten?
Nachtrag: Es müßte natürlich gar keine Datei im Projekt sein. Es kann ja
irgendein Programm sein, was einem praktisch diesen .eep-Teil erzeugt,
den man seinem Projekt hinzufügen kann (ruder zurück ...)
> schreiben? Es sollte dann doch eine .eep-Datei erstellt werden, die so> mit exakt dem Inhalt im EEPROM landet, ohne das eigentliche Programm im> Flash auch nur irgendwie zu belasten?
Das Programm wird durch eine handhabbare Datenorganisation dirch
Strukturen icht belastet. Das ist Fingerübung für'n Compiler.
Adressen hart zu vergeben mag verlockend aussehen und schnell zu
Ergebnissen führen, aber es ist fehlerträchtig und nervig das händisch
zu verwalten.
Johann
Johann L. schrieb:
> Die Definition von EESIZE ist übrigens überflüssig bzw. wird besser> gemacht in der Form>
1
>#include<avr/io.h>
2
>
3
>#defineEESIZE(1+E2END)
4
>
>> denn in den I/O-Headern wird die letzte EEPROM-Adresse als E2END> definiert.
Guter Einwand.
Werd das mal ins Turotial mit einbauen
> Die vorgeschlagene Lösung feste Adressen durch Anlegen eines Arrays> maximaler Größe zu definieren, gefällt mir übrigens überhaupt nicht.
Da bist du nicht alleine :-)
> Das Layout muss komplett handgefrickelt werden. Ausserdem müssen alle> Elemente vom gleichen Typ sein.
Nicht unbedingt.
Als ich das erste mal mit dem EEPROM gearbeitet habe, hab ich das so
gemacht
1
#define EE_DUMMY 0x000 // Dummyelement (Adresse 0 sollte nicht genutzt werden)
2
#define EE_VALUE1 0x001 // Eine Bytevariable
3
#define EE_VALUE2 EE_VALUE1 + sizeof( unsigned char ) // unsigned char, weil EE_VALUE1 als Byte anzusehen ist
4
#define EE_VALUE3 EE_VALUE2 + sizeof( double ) // weil EE_VALUE2 als double anzusehen ist
Schön ist das nicht. Übersichtlich ist das auch nicht und ja: es ist
fehleranfällig und mann muss nach 2 Wochen höllisch aufpassen, dass bei
jedem #define immer der sizeof des vorhergehenden Elements steht.
Ob ich jemanden diese Spielerei empfehlen würde: Definitiv nicht.
> Daher wäre es besser, die üblichen Nutzdaten in einer Struktur zu> organisieren.
So habe ich das dann auch in weiteren Programmen gelöst. Diese Variante
ist viel besser!
Karl heinz Buchegger schrieb:
> Johann L. schrieb:>> Die vorgeschlagene Lösung feste Adressen durch Anlegen eines Arrays>> maximaler Größe zu definieren, gefällt mir übrigens überhaupt nicht.>> Da bist du nicht alleine :-)>>> Das Layout muss komplett handgefrickelt werden. Ausserdem müssen alle>> Elemente vom gleichen Typ sein.>> Nicht unbedingt.> Als ich das erste mal mit dem EEPROM gearbeitet habe, hab ich das so> gemacht>> [...]>> Schön ist das nicht. Übersichtlich ist das auch nicht und ja: es ist> fehleranfällig und mann muss nach 2 Wochen höllisch aufpassen, dass bei> jedem #define immer der sizeof des vorhergehenden Elements steht.>> Ob ich jemanden diese Spielerei empfehlen würde: Definitiv nicht.
Ich frag mich immer, wer auf die Idee kommt, solche "Design Pattern" in
ein Tutorial reinzuschreiben.
Jeder kann das gerne so machen, aber in ein Tutorial nach dem sich
viele Einsteiger richten und dort Rat suchen, gehört das definitiv nicht
rein!
Das ist meine bescheidene Meinung dazu.
Wenn Anfänger damit nicht zurechtkommen oder was nicht verstehen -- was
keineswegs unwahrscheinlich ist -- und daraufhin im Forum nachfragen,
bekommen sie erstmal eins auf die Mütze wegen ihres Codes ;-).
Absulute Adressen vergeben braucht ein Einsteiger schlichtweg nicht.
Wenn er irgendwo ne CRC oder SERNUM reinhaben will, ist er definitiv
kein Anfänger.
Zum Anzeigen/Ändern von Variableninhalten sind absolute Adressen voll
daneben. Würdest du RAM-Daten an absolute Adressen forcieren, nur um sie
in nem Debugger anzuzeigen?
Nein, natürlich nicht.
In jedem Debugger können Variablen und Objekte/Komposite/Arrays
angezeigt werden, deren Adresse sowieso. Falls ein Debugger nicht
schaffen sollte ein Objekt im EEPROM darzustellen, nimmt man die Adresse
des Objekts, castes die (im Debugger natürlich) auf den Typ des Objekts
und zeigt dann die Daten an.
Im Tutorial gibt es eineige stellen wo zweifelhafte Konstrukte verwendet
oder sogar angepriesen werden. Leider.
Johann
Ich hatte demnächst vor, ein CAN-Netz mit ATMega88PA (evtl. auch 168;
mal sehen, wieviel Flash ich verbrate zzgl. Reserve) aufzubauen. Da das
gute Stück ja auch schon 512 byte EEPROM hat, der meistens ungenutzt
ist, hatte ich folgende Vorstellung: Im Flash haben alle Nodes das
gleiche Programm, was die Bootloader-Updates (die zweifellos kommen
werden, und sei es nur als Bugfix oder Verbesserung) und das
Programmieren der "Standardsoftware" stark vereinfacht. Die
nodespezifischen Daten, wie z.B. ID, Masken, Filter,
Kalibrierkonstanten/Reihenwiderstandswerte für KTY-Sensoren etc.) wollte
ich ins EEPROM packen und dann nach dem jedem Reset quasi beim Booten
einlesen. Also unterscheiden sich die Nodes nur durch die .eep-Datei. So
wollte ich auch noch etwas Flash sparen. Aber das ganze klappt natürlich
nur, wenn von festen Adressen aus dem EEPROM gelesen wird. Oder gibt es
da womöglich bessere Methoden als die festen Adressen?
In Prinzip kann man doch auf den ganzen Zauber verzichten.
Wenn man alle EEPRROM Variablen in einem File zusammenfasst, dann wird
der Compiler die Reihenfolge im Speicher immer nach der Reihenfolge der
Definitionen im Quellcode erzeugen.
Man darf dann halt nie die Reihenfolge ändern, sondern neue Variablen
immer am Ende anfügen, dann bleiben die bisherigen an der selben, alten,
festen Adresse.
Klaus Falser schrieb:
> In Prinzip kann man doch auf den ganzen Zauber verzichten.>> Wenn man alle EEPRROM Variablen in einem File zusammenfasst, dann wird> der Compiler die Reihenfolge im Speicher immer nach der Reihenfolge der> Definitionen im Quellcode erzeugen.> Man darf dann halt nie die Reihenfolge ändern, sondern neue Variablen> immer am Ende anfügen, dann bleiben die bisherigen an der selben, alten,> festen Adresse.
Für eine professionelle Lösung würde ich was nicht machen und schon
garnicht empfehlen das Layout von der Reihenfolge der Definitionen oder
von der Linkreihenfolge (Anordnung im Makefile...) abhängig zu machen.
Man dat hier unterschiedliche Anwendungen (Firmware-Versionen) die von
dem gleichen Linkzusammenhang bzw. Adresslage ausgehen.
Die Adresse kann man also vergeben wie oben SERNUM_ADDR oder man legt --
je nach Gusto -- das Ding in eine eigene (Input-)Section, bildet die per
ld-Skript auf eine Output-Section, deren Adresse man dann festlegt, ab.
Johann
> das Layout von der Reihenfolge der Definitionen oder> von der Linkreihenfolge (Anordnung im Makefile...) abhängig zu machen.
Deshalb habe ich auch geschrieben, dass man alle EEPROM Variablen in
EINEM Source-File zusammenfasst. Das ist für kleine Projekte, wie sie
sich bei einem AVR ergeben, durchaus noch übersichtlich.
Der Compiler legt die Reihenfolge im Speicher nach der Reihenfolge der
Definitionen fest, und der Linker macht gar nichts mehr, weil nur eine
Section mit EEPROM Variablen existiert. Die Reihenfolge im Makefile ist
somit komplett irrelevant.
>> Der Compiler legt die Reihenfolge im Speicher nach der Reihenfolge der>> Definitionen fest, und der Linker macht gar nichts mehr, weil nur eine>> Section mit EEPROM Variablen existiert.
Kann man sich darauf auch in Zukunft halbwegs sicher verlassen oder kann
das bei einer der nächsten AVR-GCC-Versionen wieder ganz anders
aussehen, weil "das ja ganz bestimmt nichts ist, womit irgendjemand
plant". Die Frage aber wirklich rein auf AVR bezogen, wobei der GCC
natürlich so ziemlich alles bedient und auf das "Abfallprodukt" AVR bei
solchen Entscheidungen wohl wirklich keine Rücksicht genommen werden
dürfte.
>> man legt -- je nach Gusto -- das Ding in eine eigene (Input-)Section,>> bildet die per ld-Skript auf eine Output-Section, deren Adresse man>> festlegt, ab.
Gesundheit! Nun aber ernsthaft: Kannst Du mir ein paar Google-Stichworte
nennen, wie ich das Thema mal etwas nachlesen kann? Als ich damals das
Lernen beim AVR (mit Assembler natürlich, aber nur das Nötigste) anfing,
gab es irgendwie Schlüsselwörter, mit denen man etwas in bestimmte
Sections/Codesegmente schreiben konnte, aber nun nutze ich eigentlich
nur noch (ganz fröhlich) C. Ein Link wäre natürlich das Allerbeste.
Lutz schrieb:
>>> Der Compiler legt die Reihenfolge im Speicher nach der Reihenfolge der>>> Definitionen fest, und der Linker macht gar nichts mehr, weil nur eine>>> Section mit EEPROM Variablen existiert.> Kann man sich darauf auch in Zukunft halbwegs sicher verlassen oder kann> das bei einer der nächsten AVR-GCC-Versionen wieder ganz anders> aussehen, weil "das ja ganz bestimmt nichts ist, womit irgendjemand> plant".
Zunächst mal legt GCC die Reihenfolge im Speicher überhaupt nicht fest.
Das macht der Linker/Lokator und nicht der Compiler. Zumindest läuft das
bei den GNU-Tools so. Möglich daß das bei anderen Compilern die keine
Objekte erzeugen anders läuft, zB tcc.
> Die Frage aber wirklich rein auf AVR bezogen, wobei der GCC> natürlich so ziemlich alles bedient und auf das "Abfallprodukt" AVR bei> solchen Entscheidungen wohl wirklich keine Rücksicht genommen werden> dürfte.
Das ist eine GCC-Angelegenheit, keine Sache des Backends. GCC kann
Symboldefinitionen/Funktionen umsortieren, zB mit -funit-at-a-time
(Standard bei optimize >= -O1). Dann hilft wohl -fno-toplevel-reorder,
aber das sollte man aus dem einfachen Grund nicht verwenden, weil es
schlichtweg unnötig ist und das was zu tun ist mit Bordmitteln klar und
unmissverständlich ausgedrückt werden kann.
>>> man legt -- je nach Gusto -- das Ding in eine eigene (Input-)Section,>>> bildet die per ld-Skript auf eine Output-Section, deren Adresse man>>> festlegt, ab.> Gesundheit! Nun aber ernsthaft:
Das ist mein Ernst :-)
Für komplexere µC braucht's eben ld-Skripte und ich wüsste jetzt keinen
Kunden (nicht AVR sondern ein 32-Bitter) der nicht seine eigenen
Linkerskripte hat. Der Vorteil davon ist, daß an einem zentralen Ort die
Adresslage angegeben wird. Ausserdem ist wünschenswert, die Adresslage
möglichst spät zu machen. Wird die Adresslage im Compiler per (foo_t*)
0x1234 oder im Assembler festgelegt, muss für jede Adresslage neu
generiert werden.
Bei Verwendung von ld-Skript kann man aber Objekte/Libs nehmen und
braucht nur nen Linklauf zu machen und so zB für unterschiedliche
RAM-Konfigurationen die Anwendung bauen ohne neu zu übersetzen.
(Passiert zB mit dem Code unter der Motorhaube deines PKW so). Neu zu
übersetzen ist nicht nur ne Zeitfrage sondern auch ne Kostenfrage. Wenn
das Zeug neu zertifiziert werden muss (TÜV, SIL, ...) wird sowas richtig
teuer.
> Als ich damals das> Lernen beim AVR (mit Assembler natürlich, aber nur das Nötigste) anfing,> gab es irgendwie Schlüsselwörter, mit denen man etwas in bestimmte> Sections/Codesegmente schreiben konnte, aber nun nutze ich eigentlich> nur noch (ganz fröhlich) C.
In C geht das eben nicht -- es sei denn über harte Adressvergabe und
Cast.
Es wird also realisiert über Nicht-Standard-Erweiterungen, zB
__attribute__((section)) in gcc bzw. mehr oder weniger gleichbedeutend
damit Section-Pragmas. Dazu gehören auch PROGMEM, EEMEM, PSTR, ... die
die Codeablage regeln (.eeprom, .pgmspace, .bootloader, ...)
> Ein Link wäre natürlich das Allerbeste.http://sourceware.org/binutils/docs-2.19/ld/Scripts.html
Johann
> Zunächst mal legt GCC die Reihenfolge im Speicher überhaupt nicht fest.> Das macht der Linker/Lokator und nicht der Compiler. Zumindest läuft das> bei den GNU-Tools so. Möglich daß das bei anderen Compilern die keine> Objekte erzeugen anders läuft, zB tcc.
Das sehe ich nicht so.
Der Compiler erzeugt verschiedene memory sections für code,
initialisierte Daten, EEPROM usw.
Das Layout innerhalb dieser Sections, also z.B. die Reihenfolge der
Variablen wird vom Compiler festgelegt.
Der Linker ordnet dann die Sections nach type und fasst diese zusammen,
kann und darf aber nicht den Inhalt (Layout) innerhalb der Sections
ändern.
Dieses Zusammenfassen ist IMHO auch nicht willkürlich, sondern ist von
der Reihenfolge bestimmt, mit der die einzelnen Objects an den Linker
übergeben werden.
Wenn also im Projekt nur eine Section für EEPROM Daten erzeugt wird,
weil nur ein einziges File EEPROM Variablen deklariert, dann ist die
Reihenfolge der EEPROM Variablen komplett vom Compiler bestimmt.
Ob jetzt der Compiler 100% verpflichtet ist, die Variablen im Speicher
nach der Reihenfolge der Definitionen anzuordnen, wissen die Compiler
Gurus, ich bin jedenfalls der Überzeugung, dass er es macht.
Es gibt meiner Meinung ja auch keinen Grund dies nicht zu tun. Der
Compiler arbeitet das Quellfile sequentiell ab (OK, er macht mehrere
Passes), und reserviert Speicher im den ensprechenden Sections
sequentiell mit jeder Variable die er trifft.
Klaus Falser schrieb:
>> Zunächst mal legt GCC die Reihenfolge im Speicher überhaupt nicht fest.>> Das macht der Linker/Lokator und nicht der Compiler. Zumindest läuft das>> bei den GNU-Tools so. Möglich daß das bei anderen Compilern die keine>> Objekte erzeugen anders läuft, zB tcc.>> Das sehe ich nicht so.> Der Compiler erzeugt verschiedene memory sections für code,> initialisierte Daten, EEPROM usw.> Das Layout innerhalb dieser Sections, also z.B. die Reihenfolge der> Variablen wird vom Compiler festgelegt.
Die Reihenfolge, in der der Compiler die Definitionen ausgibt, ist nicht
festgelegt.
> Ob jetzt der Compiler 100% verpflichtet ist, die Variablen im Speicher> nach der Reihenfolge der Definitionen anzuordnen, wissen die Compiler> Gurus, ich bin jedenfalls der Überzeugung, dass er es macht.
Dieses Wissen ist auch Normalsterblichen zugänglich -- sofern sie
gewillt sind die Dokumentation zu lesen, zB die der gcc-Schalter wie
-funit-at-a-time.
> Es gibt meiner Meinung ja auch keinen Grund dies nicht zu tun.
Der Compiler kann die Symbole sammeln und erst am Ende des Compilelaufs
ausgeben -- oder eben nicht wenn die nicht referenziert werden
(-fwhole-program). Die Ausgabe kann aus einer Hashtabelle erfolgen, wird
also idR unsortiert sein.
Und wenn der Compiler die Symbole so ausgibt wie sie in der Quelle
stehen, dann ist das immer noch unspezifiziert . Sich darauf zu
verlassen ist Hack und ich hatte schon mehr als ein "Bugreport" von
Kunden genau wegen solchem Rumgefrickel (oder "Compilerfehler" wie das
i=i++ nicht funktioniert oder Inline Assembler falchen Code generiert):
Alles geht jahrelang prima und plötzlich, just vor Produktfertigstellung
fliegt den Jungs alles um die Ohren. Natürlich im Feld.
Johann
Klaus Falser schrieb:
> Ob jetzt der Compiler 100% verpflichtet ist, die Variablen im Speicher> nach der Reihenfolge der Definitionen anzuordnen, wissen die Compiler> Gurus, ich bin jedenfalls der Überzeugung, dass er es macht.
Meines Wissens ist das nicht vorgschreiben.
Lediglich wenn man die Vars in einer struct sammelt, muss die
Reihenfolge der Strukturmember erhalten bleiben.
Aber freie Variablen darf der Compile im Speicher verteilen, wie es ihm
am besten passt. Das kann auch Sinn machen, wenn der Compiler Speicher
sparen muss und gleichzeitig Alignment Restriktionen einhalten will.
Sagen wir der Einfachheit halber, dass man auf einen Word nur dann
zugreifen kann, wenn er im Speicher auf einer geraden Adresse liegt.
1
unsignedchari;
2
intj;
3
unsignedchark;
4
intl;
benutzt der Compiler die Reihenfolge direkt so wie sie im Code angegeben
ist, müsste er zwischen dem unsigned char und dem int jeweils ein
Padding Byte einbauen: in Summe werden daher 8 Byte verbraucht
dreht er die Reihenfolge im Speicher aber um
1
unsignedchari;
2
unsignedchark;
3
intj;
4
intl;
kommt er mit 6 Bytes Speicherverbrauch über die Runden.
Beim AVR spielt das natürlich keine Rolle, aber es geht ja ums
alllgemeinere Prinzip. Und bei solchen Sachen ist der C-Standard
praktisch immer so formuliert, dass man die Details dem Compiler
überlässt und ihn möglichst wenig einschränkt oder auf etwas festnagelt.
Ich benutze überhaupt keine EEPROM-Variablen.
Ich lege alle Variablen, die gesichert werden sollen, als Struktur im
SRAM an.
Und bei Bedarf lese ich diese vom EEPROM ein bzw. schreibe sie in den
EEPROM.
Und dieser Funktion übergebe ich dann ganz einfach die
EEPROM-Startadresse als int.
Eine default-Initialiserung dieser Struktur im SRAM bei leerem EEPROM
erfolgt dann ganz normal vom Compiler.
Ein leerer EEPROM kann z.B. durch eine CRC oder durch den Inhalt 0xFF
festgestellt werden.
Peter
Gut, ich geb's zu. Ihr habt recht, man kann sich auf den Compiler nicht
100% verlassen.
Es wird zwar zu 99 % funktionieren, aber richtig garantiert ist es doch
nicht.
Es wäre für mich aber kein so großes Problem auch im professionellen
Bereich, weil man beim testen einer neuen Version sofort sehen sollte,
ob das Speicherlayout und die Werte von früher korrekt übernommen werden
oder nicht.
Wer sowas erst im Field sieht, ist sowieso unprofessionell bei seinem
Testablauf.
Klaus Falser schrieb:
> Es wäre für mich aber kein so großes Problem auch im professionellen> Bereich, weil man beim testen einer neuen Version sofort sehen sollte,
jaja, Engineering und die Konjunktive... ;-)