Hallo,
habe stundenlang nach einem Fehler in meinem Programm gesucht und glaube
ihn jetzt im Compiler gefunden zu haben.
Ich habe eine Funktion definiert:
void sun( int year, int month, int day, double longitude, double
latitude, int rise, int localOffset, zeit_t* zeit );
rufe ich sie zweimal hintereinander auf, nur mit 0 bzw 1 für den
Parameter rise, zeigt sich, dass der zweite Aufruf nicht das gewünschte
Ergebnis bringt.
Im Listing sehe ich, dass das Unterprogramm die Register r2-r17 und
r28-r29 auf den Stack rettet.
Die Parameterübergabe erfolgt aber auch über r18 und r19.
Die werden nicht gerettet, der Compiler scheint das aber zu erwarten,
denn beim zweiten Aufruf glaubt er, das die Parameter noch alle in ihren
Registern stehen, nur die geänderten Register und r20-r25 (nicht
gerettet) werden neu gesetzt.
R18 und r19 werden im Unterprogramm verändert!
Deshalb stimmt der Parameter Longitude beim zweiten Aufruf nicht!
Habe mir erstmal damit geholfen, nicht mehr so viele Parameter zu
übergeben...
Kennt jemand das Problem?
Im Anhang mein stark gekürztes Programm (tut nix sinnvolles), das
trotzdem den Fehler zeigt.
Bevor jemand fragt: es soll mal eine Uhr werden, die Sonnenauf- und
untergangszeiten anzeigt. Nach Umstieg auf einen megaAVR wird wohl auch
noch der Mond drankommen.
schon mal danke für die zahlreichen Antworten
Uwe
http://www.uwe-nagel.de.vu
Ich glaub da war vor kurzem in der GCC-Mailinglist der selbe Bugreport.
Ich weiß leider nicht ob das schon gefixt ist. Versuch mal die
aktuelleste avr-gcc version.
Nö, r18 + r19 gehören zu den `clobber' Registern, die muß eine
gerufene Funktion nicht retten. Der Bugreport in der avr-gcc
Liste bezog sich darauf, daß eine 32-Bit-Zahl die Register
r16..r19 belegt hat. Der Compiler hat daraus fälschlicherweise
abgeleitet (r18/r19-mäßig), daß diese Register nicht gerettet
werden müssen, und hat dabei mit dem Funktionsaufruf r16/r17
zerstört.
Nö, habe mir nochmal den generierten Assemblercode angesehen,
das könnte in der Tat dieses Problem sein...
Ich habe bisher noch nie einen Funktionsaufruf mit einer derart hohen
Zahl von Argumenten gesehen. Warscheinlich die Compilerbauer auch nicht.
Aber dann sollte der Compiler wenigstens eine Fehlermeldung ausgeben.
Der üblichere und Ressourcen schonendere Weg ist die Übergabe als
Pointer auf ein Array oder eine Struktur.
Sowas lohnt sich schon oft bei mehr als 3 Argumenten.
Außerdem würde ich einen 8-Bitter nie unnötiger Weise mit "int" quälen,
z.B. bei Monat (1..12).
Auch zur besseren Verständlichkeit nehme ich für Variablen, wo negative
Werte unsinnig sind, immer "unsigned", z.B. bei Monat (+1..+12). Sowas
schont auch die Ressourcen.
Auf der Atmel-Seite gibt es eine sehr informative Applikationsnote mit
C-Programmiertips. Sollte sich jeder MC-Programmierer mal reinziehen.
Peter
Warum sollte die Übergabe als struct oder array ressourcenschonender
sein? Die einzelnen Parameter können in Registern übergeben
werden, so daß erstmal keine Speicherzugriffe nötig sind. Gut,
das wird durch die in den Stack zu rettenden Register wieder
teilweise aufgehoben, aber ich würde ohne eingehende Analyse
nicht die eine oder andere Variante per se als ressourcenschonender
bezeichnen.
Die Compilerbauer haben durchaus auch schon so viele Argumente
gesehen, das Problem entsteht nur dann, wenn ein 32-bit
Argument sich über die Grenze r17/r18 erstreckt.
Ich sehe übrigens in der aktuellen Compilerversion (GCC aus dem
CVS, gcc-3_3-branch) von gestern kein Problem mehr. Der Aufrufer
(main()) lädt jedesmal r16...r19 komplett, der Aufgerufene (sun())
sichert von r2 bis r17 und r28/r29 komplett.
@Joerg,
"Warum sollte die Übergabe als struct oder array ressourcenschonender
sein?"
Der Aufrufer muß sie ja erst vom RAM in Register packen, d.h. Kopien
anlegen. Der Aufgerufene will aber mit den Registern auch arbeiten, wird
also erstmal den Großteil wieder im RAM sichern.
Bedeutet also doppelt RAM, doppelt Code.
Der AVR kann doch gut mit Zeigern+Displacement umgehen, d.h er holt sich
die Variablen bequem aus dem RAM, aber erst genau dann, wenn er sie auch
benötigt.
Ist aber meine rein persönliche Erfahrung, daß C-Compiler sehr gut auf
die Arbeit mit Zeigern hin optimiert sind. D.h. ab >=4 Argumenten ergibt
sich mit Zeigern oftmals ein kleinerer und schnellerer Code.
Peter
Ich persönlich mag es gern, wenn man zusammengehörende Variablen (z.B.
Tag, Monat, Jahr) dann auch in Strukturen zusammenfaßt.
Das Programm wird dadurch besser lesbar.
Peter