Forum: Compiler & IDEs While-Schleife in Assemblercode von GCC erzeugt.


von Philipp S. (philipp09)


Lesenswert?

Hallo zusammen,

momentan sitze über einem Assemblercode, den mir der GCC (Version 4.6.1) 
aus meinem C-Code generiert. Mich treibt es fast in den Wahnsinn, weil 
ich den Fehler nicht finden kann, wieso der Compiler einen Assemblercode 
generiert, der dazu führt, dass meine Funktion nicht ordnungsgemäß 
funktionieren kann. Die Lösung für das Problem habe ich zwar gefunden, 
es wäre aber für mich und möglicherweise auch für andere Nutzer 
interessant, die Gründe für ein solches Compilerverhalten zu kennen.

Mein Problem bezieht sich auf die "while-Schleife", die in Version 1 
nicht mehr im Assemblercode auftaucht, in Version 2 hingegen schon. Zum 
besseren Verständnis habe ich jeweils die komplette Funktion (Start 
einer AD-Wandlung und warten bis diese erfolgt ist) aus dem lss-File 
kopiert.

Zur Zeit wird von mir das Optimierungslevel -O1 verwendet.

1. Die "nicht funktionierende" Version:
1
uint16_t makeConversion(uint8_t channel)
2
{
3
  uint16_t adcResult;
4
5
  ADMUX &= 0xE0; /* Clear channel selection */
6
     298:  97 b1         in  r25, 0x07  ; 7
7
     29a:  90 7e         andi  r25, 0xE0  ; 224
8
     29c:  97 b9         out  0x07, r25  ; 7
9
  ADMUX |= channel; /* Set channel */
10
     29e:  97 b1         in  r25, 0x07  ; 7
11
     2a0:  89 2b         or  r24, r25
12
     2a2:  87 b9         out  0x07, r24  ; 7
13
14
  ADCSRA |= 0x40; /* Start conversion */
15
     2a4:  36 9a         sbi  0x06, 6  ; 6
16
    
17
  while((ADCSRA & 0x40) == 1)
18
     2a6:  86 b1         in  r24, 0x06  ; 6
19
  {
20
    
21
  }
22
  
23
  adcResult = ADC; /* Get result */
24
     2a8:  84 b1         in  r24, 0x04  ; 4
25
     2aa:  95 b1         in  r25, 0x05  ; 5
26
  
27
28
  return(adcResult);
29
}
30
     2ac:  08 95         ret

2. Die funktionierende Version.

1
uint16_t makeConversion(uint8_t channel)
2
{
3
  uint16_t adcResult;
4
5
  ADMUX &= 0xE0; /* Clear channel selection */
6
     298:  97 b1         in  r25, 0x07  ; 7
7
     29a:  90 7e         andi  r25, 0xE0  ; 224
8
     29c:  97 b9         out  0x07, r25  ; 7
9
  ADMUX |= channel; /* Set channel */
10
     29e:  97 b1         in  r25, 0x07  ; 7
11
     2a0:  89 2b         or  r24, r25
12
     2a2:  87 b9         out  0x07, r24  ; 7
13
14
  ADCSRA |= 0x40; /* Start conversion */
15
     2a4:  36 9a         sbi  0x06, 6  ; 6
16
    
17
  while((ADCSRA & 0x40) != 0)
18
     2a6:  36 99         sbic  0x06, 6  ; 6
19
     2a8:  fe cf         rjmp  .-4        ; 0x2a6 <thm5adc_makeConversion+0xe>
20
  {
21
    
22
  }
23
  
24
  adcResult = ADC; /* Get result */
25
     2aa:  84 b1         in  r24, 0x04  ; 4
26
     2ac:  95 b1         in  r25, 0x05  ; 5
27
  
28
29
  return(adcResult);
30
}
31
     2ae:  08 95         ret

Erläuterung zur Funktion:

Es wird eine AD-Wandlung angestoßen und in einer while-Schleife (zur 
Übersichtlichkeit noch ohne timeout) gewartet, bis diese abgeschlossen 
wurde. Laut Datenblatt wird das Bit ADSC immer dann von 1 auf 0 gesetzt, 
wenn eine Wandlung abgeschlossen wurde. Aus diesem Grund warte ich genau 
auf dieses Event in der while-Schleife. Es gibt natürlich zwei 
Möglichkeiten, wie ich auf den Statuswechsel warten kann.
Möglichkeit 1: Warten, solange ADSC auf 1 steht (siehe Version 1)
Möglichkeit 2: Warten, solange ADSC nicht auf 0 steht (siehe Version 2)

Prinzipiell sollte doch daraus derselbe Assemblercode (bis auf die 
Verwendung von SBIS anstelle von SBIC) entstehen. Wo liegt mein 
Denkfehler?

Viele Grüße,

Philipp

von Peter II (Gast)


Lesenswert?

wie soll denn diese bedingung jemals war werden

while((ADCSRA & 0x40) == 1)

dafür braucht man kein ASM code!

von spess53 (Gast)


Lesenswert?

Hi

>while((ADCSRA & 0x40) == 1)

Das kann nie 1 werden.

MfG Spess

von Philipp S. (philipp09)


Lesenswert?

Hallo Spess, hallo Peter,

ihr habt völlig recht.

Hatte absolut Tomaten auf den Augen. Aber manchmal sieht man eben vor 
lauter Bäumen den Wald nicht mehr.

Vielen Dank an euch!

von Thomas E. (thomase)


Lesenswert?

Philipp S. schrieb:
> Hatte absolut Tomaten auf den Augen. Aber manchmal sieht man eben vor
> lauter Bäumen den Wald nicht mehr.

Deswegen schreibt man sowas ja auch nicht.
> while((ADCSRA & 0x40) == 1)
> while((ADCSRA & 0x40) != 0)

Sondern: while(ADCSRA & 0x40)

mfg.

von Dussel (Gast)


Lesenswert?

Wobei die letzten beiden gleich sind und das gleiche tun.

von Philipp S. (philipp09)


Lesenswert?

Hallo Thomas,

da hast du völlig recht. Allerdings füge ich einen Timeout in die 
While-Schleife ein. Und weil daher ein weiteres Abbruchkriterium 
dazukommt, dachte ich, dass es übersichtlicher gewesen wäre.


Um diejenigen, die zufällig auf diesen Beitrag stoßen, nicht im Unklaren 
über den von mir gemachten Fehler zu lassen:
1
while((ADCSRA & 0x40) == 1); //Wird NIEMALS erfüllt
2
3
while((ADCSRA & 0x40) == 0x40); //Würde funktionieren besser ist aber die Lösung von Thomas Eckmann

von Peter D. (peda)


Lesenswert?

So gehts auch:
1
while(!!(ADCSRA & 0x40) == 1)

Das !! macht aus Wahr eine 1.

Peter

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Peter Dannegger schrieb:
> So gehts auch:

Macht man aber nur, wenn man einen Beitrag beim IOCCC einreichen
möchte. :-)

von Peter D. (peda)


Lesenswert?

Jörg Wunsch schrieb:
> Macht man aber nur, wenn man einen Beitrag beim IOCCC einreichen
> möchte. :-)

Nö.

Es kann sein, daß das 0x40 nicht direkt im Text steht, sondern ein 
Define irgendwo ist.
Und nicht ein While den Ausdruck auswertet, sondern er als Returnwert 
von einer anderen Funktion irgendwo auswertet wird.
Dann ist das !! schon sinnvoll, um immer den Wert TRUE zu erzwingen, 
egal welche Bitposition geprüft wird.
1
#define BLA 0x40
2
// nen Haufen Code ...
3
uint8_t testbla()
4
{
5
// Code ...
6
  return !!(blablub & BLA);
7
}
8
// nen Haufen Code ...
9
  var += testbla();

Peter

von Karl H. (kbuchegg)


Lesenswert?

Philipp S. schrieb:

> Um diejenigen, die zufällig auf diesen Beitrag stoßen, nicht im Unklaren
> über den von mir gemachten Fehler zu lassen:

Man kann deinen Fehler so zusammenfassen:

       ungleich 0
  und  gleich 1
sind nicht dasselbe

Wie man sich leicht klar machen kann. 2 ist zwar ungleich 0 (diese 
Fragstellung würde man als mit wahr beantworten), aber 2 ist nicht 
gleich 1 (diese Fragestellung würde man also mit falsch beantworten).

In C ist weniger oft mehr. Da im while bzw. im if die Bedingung 
'ungleich 0' schon implizit mit drinnen steckt(*), lässt man als 
Programmierer den am besten weg (wenn es nicht um eine extreme Betonung 
dieses Sachverhalts geht). Alles was man weglassen kann, kann auch nicht 
zu einem Tippfehler führen.

(*) dies ist so zu verstehen, dass while (oder auch if) sich nicht dafür 
interessieren, dass in ihren Klammern ein logischer Ausdruck mit einem 
boolschen Ergebnis steckt. Sie nehmen einfach den angegebenen Ausdruck, 
werten ihn aus und bewerten dieses Ergebnis nach den Regeln der Sprache 
für logische Ausdrücke.

D.h. bei
   while( x )
geht es nicht um den konkreten Aufbau dieses x. Das muss kein Ausdruck 
sein, in dem ein Vergleichsoperator vorkommt. Das ist in C nicht 
gefordert (in anderen Sprachen ist das anders!). Aus Sicht des while ist 
das einfach nur ein arithmetischer Ausdruck, dessen Ergebnis auf "0 oder 
nicht 0" untersucht wird. Ist er nicht 0, dann wird die Schleife 
(erneut) betreten.

In diesem Sinne ist
  while( x != 0 )
eigentlich doppelt gemoppelt. Denn das Ergebnis des Ausdrucks
   x != 0
ist seinerseits wieder eine Zahl: 0 wenn der Vergleich nicht zutrifft 
und 1 wenn der Vergleich zutrifft. Wenn das while daher das Ergebnis 
dieses Vergleichs prüft, dann stellt man fest, dass dieses Ergebnis 
immer mit dem Ergebnis des explizit hingeschriebenen Vergleiches 
übereinstimmt. D.h. diese Schreibweise mit dem expliziten != Vergleich 
ist in allen Belangen identisch zu

   while( x )

Nun kann es natürlich sein, dass man als Programmierer hier eine 
spezielle Betonung auf den Umstand des 'ungleich 0' legen will und dann 
schreibt man den Vergleich auch explizit an. Aber im Grunde ist er nicht 
notwendig, so wie bei

   i = 1 * k;

die Multiplikation mit 1 nicht notwendig ist. Und die schreibt man ja 
auch nicht explizit an, es sei denn man will da eine Betonung drauf 
legen.


Also keine Scheu

  if( PINC & ( 1 << PB4 ) )

ist perfektes C, an dem es erst mal nichts auszusetzen gibt. Es ist 
genauso logisch einwandfrei, wie

   if( isCursorVisible() )
     ...

einwandfrei ist. Denn in dieser Situation würde wohl kaum jemand

   if( isCursorVisible() != FALSE )

schreiben. Ganz im Gegenteil, würde wohl hier der zusätzliche Vergleich 
die meisten Programmierer stutzig machen und sie würden sich fragen, 
warum der da steht und ob es da wohl irgendwelche Sonderfälle gibt, die 
sie nur einfach nicht durchschauen.

In diesem Sinn:
* Vergleichsoperatoren sind in C auch nichts anderes als arithmetisch 
logische Operatoren. Sie bewerten ihr linkes Argument, bewerten ihr 
rechtes Argument, führen die Aktion durch und haben ein numerisches 
Ergebnis. Konzeptionell identisch zu den Operatoren +, -, *, /, |, .... 
und wie sie sonst alle heißen.

* In C ist weniger oft mehr.

von Thomas E. (thomase)


Lesenswert?

Philipp S. schrieb:
> Und weil daher ein weiteres Abbruchkriterium
> dazukommt, dachte ich, dass es übersichtlicher gewesen wäre.
Finde ich nicht.

while((ADCSRA & 0x40) & !timeout);

Peter Dannegger schrieb:
> Das !! macht aus Wahr eine 1.
Nö.
Wahr, also true, ist immer 1. Das !! macht aus !0 eine 1.

Karl Heinz Buchegger schrieb:
> ist perfektes C, an dem es erst mal nichts auszusetzen gibt.
MISRAtene Normungspopanze regen sich da aber schon drüber auf.

mfg.

von Karl H. (kbuchegg)


Lesenswert?

Thomas Eckmann schrieb:

>> ist perfektes C, an dem es erst mal nichts auszusetzen gibt.
> MISRAtene Normungspopanze regen sich da aber schon drüber auf.

:-)
Zum Thema MISRA: Blödeln wir rum oder programmieren wir?

(Du kennst sicherlich meine Einstellung zu MISRA. :-) )

von Thomas E. (thomase)


Lesenswert?

Karl Heinz Buchegger schrieb:
> (Du kennst sicherlich meine Einstellung zu MISRA. :-) )
Sonst hätte ich das auch nicht geschrieben.

mfg.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Thomas Eckmann schrieb:
> while((ADCSRA & 0x40) & !timeout);

Oi oi oi. :-)

Peter Dannegger schrieb:
> Dann ist das !! schon sinnvoll, um immer den Wert TRUE zu erzwingen,
> egal welche Bitposition geprüft wird.

Da finde ich aber ((ADCSRA & 0x40) != 0) allemal besser verständlich.
Das erzwingt auch den Wert 1, unabhängig von der Bitposition, und
man kann es lesen, ohne erst drüber nachdenken zu müssen. ;-)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.