Hallo in die Runde,
ich habe hier gerade ein Problem mit einem Atmega88, das ich mir nicht
erklären kann.
Ich übergebe einer Funktion einen Funktionspointer, der eigentlich
ausgewertet werden soll. Stattdessen wird aber sein Wert innerhalb der
Funktion verändert.
Der Code (aufs relevante reduziert):
1
...
2
uint8_tte=timerexist(led_on);
3
if(!te)...
4
...
5
uint8_ttimerexist(constvoid(*f)(void*)){
6
uint8_tcurrent=t_first;
7
while(current!=T_LAST){
8
if(&(tasks[current].func.f)==&f)
9
return1;
10
...
Beim Blick in den Assembler ist denn auch schnell klar, wo es kaputt
geht:
1
uint8_t te = timerexist(led_on);
2
00000A36 LDI R24,0x95 Load immediate
3
00000A37 LDI R25,0x07 Load immediate
4
00000A38 RCALL PC-0x0122 Relative call subroutine
5
if(!te){
6
00000A39 TST R24 Test for Zero or Minus
7
...
8
uint8_t timerexist(const void (*f)(void*)) {
9
00000916 PUSH R28 Push register on stack
10
00000917 PUSH R29 Push register on stack
11
00000918 RCALL PC+0x0001 Relative call subroutine
12
00000919 IN R28,0x3D In from I/O location
13
0000091A IN R29,0x3E In from I/O location
14
uint8_t current = t_first;
15
0000091B LDS R24,0x0165 Load direct from data space
16
while(current != T_LAST) {
17
0000091D CPI R24,0xFF Compare with immediate
18
0000091E BREQ PC+0x2D Branch if equal
19
if(&(tasks[current].func.f) == &f)
20
0000091F LDI R25,0x00 Load immediate
21
00000920 MOVW R20,R24 Copy register pair
Die Adresse von led_on ist in R25 R24 0x0795. In timerexist wird dann
erst R24 mit t_first überschrieben und R25 auf 0x00 gesetzt.
Aber warum? Sollte der Compiler nicht wissen, dass in R24 und R25 noch
Daten sind, die benötigt werden?
Im Debugger wird f weiterhin auf function (word address)
*{registers}@R25 R24 abgebildet, und man kann beim Durchsteppen
mitverfolgen, wie der Wert kaputt geht. (von 0x0795 über 0x07ab zu
0x00ab)
Kompiliert wurde mit
Das Problem liegt ziemlich sicher in den Pünktchen oder in = &f anstatt
in =f. Du willst ziemlich sicher nicht die Adresse der lokalen
Variablen f zuweisen.
Und beachte Warnungen!
Da in deinem Code keine Zuweisung auf f stattfindet, vermute ich den
Fehler ganz woanders. Möglicherweise hast Du einen Stack überlauf. Stack
überlauf ist beinahe immer die Ursache für unerwartete (vor allem nicht
programmierte) Schreibzugriffe auf Variablen.
Dass der Compiler R25 überschreibt, muss kein Fehler sein. Wenn f später
nochmal gebraucht wird, kann er ja erneut einen LDI R25,0x07 ausführen.
Deaktiviere mal den Optimizer mit -O0, dann erhälst Du einen weniger
veränderten Code der eher dem C Quelltext entspricht. Ich wette, dass
dein Problem dann immer noch besteht, und dann wird eventuell
offensichtlicher, das die Fehlerursache ein Stack Überlauf ist (oder
auch nicht).
Ich habe mir angewöhnt, Code stets mit der Option -O0 zu debuggen. Es
sei denn, das Problem tritt nur bei einer bestimmten Optimierungs-Stufe
auf.
Johann L. schrieb:> Ok, ist ein Vergleich. Für den gilt aber das gleiche in grün.
Sehe ich auch so. f ist schon ein Pointer. Davon noch einmal die Adresse
zu nehmen macht an dieser Stelle so keinen Sinn. Kann aber auch daran
liegen, dass zu wenig Kontext vorhanden ist.
G T M. schrieb:> Aber warum? Sollte der Compiler nicht wissen, dass in R24 und R25 noch> Daten sind, die benötigt werden?
Wie schon bemerkt wurde, verwendest du die Daten im Parameter nicht,
sondern nimmst nur die Adresse (zumindest angenommen, den Rest der
Funktion sehen wir ja nicht). Der Compiler weiß also, dass die Daten
tatsächlich nicht benötigt werden.
Außerdem ist es etwas sinnlos, einen void Rückgabewert const zu machen.
Der Funktionspointer Parameter sollte vermutlich "void (*const
f)(void*)" heißen.
Erstmal Danke für die zahlreichen Antworten!
Johann L. schrieb:> Und beachte Warnungen!
Keine Warnungen vorhanden.
Johann L. schrieb:> Soll wohl das gemeint sein:> if (tasks[current].func.f == f)Rene H. schrieb:> Ziemlich sicher sogar. Stellt sich nur die Frage ob das den Fehler> behebt.
Hmm.. problematisch, weil der Compiler dann die Adresse hardcodiert und
gleichzeitig wegoptimiert?
Wenn ich das & weglasse, bekommt der Debugger jedenfalls mit, was los
ist:
Rene H. schrieb:> @G T M> Hast du schon mal ohne -O3 kompiliert?
Der sagt dann nämlich bei -O>0 zu f:
"Optimized away"
Andreas B. schrieb:> Wie schon bemerkt wurde, verwendest du die Daten im Parameter nicht,> sondern nimmst nur die Adresse (zumindest angenommen, den Rest der> Funktion sehen wir ja nicht). Der Compiler weiß also, dass die Daten> tatsächlich nicht benötigt werden.
Ja, Dein Post hat sich gerade mit meinem Tippen und ausprobieren
überschnitten.
Aber auch wenn ich nicht die Adresse verwende, sondern die Pointer
direkt vergleiche, wird das Ganze wegoptimiert, obwohl es für den
Vergleich benötigt wird.
> Außerdem ist es etwas sinnlos, einen void Rückgabewert const zu machen.> Der Funktionspointer Parameter sollte vermutlich "void (*const> f)(void*)" heißen.
Stimmt natürlich. Muss bei der Fehlersuche verrutscht sein. Ändert aber
nichts am Ergebnis.
Die gesamte funktion sieht (jetzt) so aus:
1
uint8_ttimerexist(void(*constf)(void*)){
2
uint8_tcurrent=t_first;
3
while(current!=T_LAST){
4
if(tasks[current].func.f==f)
5
return1;
6
current=tasks[current].next;
7
}
8
return0;
9
}
und f wird außer bei -O0 wegoptimiert.
Wenn current != T_LAST ist, läuft die Schleife auch. Zum Vergleich (der
nicht wegoptimiert wird) herangezogen werden die Register, in denen f
vor dem Sprung in timerexist zu finden war.
Viele Grüße!