Forum: Compiler & IDEs switch case optimierung


von Peter Z. (Gast)


Lesenswert?

Hi,
ich habe ein ATtiny10-C-programm mit einer switch case Anweisung 
geschrieben.
Der Compiler hat es wundervoll mit einem berechneten Sprung optimiert, 
die einzelnen Sprungziele liegen jeweils 6 Adressen auseinander.
Aber was ich nicht verstehe: wie macht er die Multiplikation mit 6?
Hier ein Auszug aus der iss-Datei:
1
//*********************************************************************
2
void light_wave(uint8_t Color,uint8_t position)
3
{
4
  Color = 4;
5
  switch(position)
6
  7a:  e6 2f         mov  r30, r22
7
  7c:  f0 e0         ldi  r31, 0x00  ; 0
8
  7e:  e1 31         cpi  r30, 0x11  ; 17
9
  80:  f1 07         cpc  r31, r17
10
  82:  b0 f5         brcc  .+108      ; 0xf0 <light_wave+0x76>
11
  84:  e5 5f         subi  r30, 0xF5  ; 245
12
  86:  ff 4f         sbci  r31, 0xFF  ; 255
13
  88:  09 94         ijmp
14
  {
15
    case  0 : Send_Color(Color,0x01);
16
  8a:  61 e0         ldi  r22, 0x01  ; 1
17
  8c:  84 e0         ldi  r24, 0x04  ; 4
18
  8e:  eb df         rcall  .-42       ; 0x66 <Send_Color>
19
    case  1 : Send_Color(Color,0x02);
20
  90:  62 e0         ldi  r22, 0x02  ; 2
21
  92:  84 e0         ldi  r24, 0x04  ; 4
22
  94:  e8 df         rcall  .-48       ; 0x66 <Send_Color>
23
    case  2 : Send_Color(Color,0x04);
24
  96:  64 e0         ldi  r22, 0x04  ; 4
25
  98:  84 e0         ldi  r24, 0x04  ; 4
26
  9a:  e5 df         rcall  .-54       ; 0x66 <Send_Color>
27
    case  3 : Send_Color(Color,0x08);
28
  9c:  68 e0         ldi  r22, 0x08  ; 8
29
  9e:  84 e0         ldi  r24, 0x04  ; 4
30
  a0:  e2 df         rcall  .-60       ; 0x66 <Send_Color>
31
    case  4 : Send_Color(Color,0x10);
32
  a2:  60 e1         ldi  r22, 0x10  ; 16
33
  a4:  84 e0         ldi  r24, 0x04  ; 4
34
  a6:  df df         rcall  .-66       ; 0x66 <Send_Color>
35
    case  5 : Send_Color(Color,0x20);
36
  a8:  60 e2         ldi  r22, 0x20  ; 32
37
  aa:  84 e0         ldi  r24, 0x04  ; 4
38
  ac:  dc df         rcall  .-72       ; 0x66 <Send_Color>
39
    case  6 : Send_Color(Color,0x40);
40
  ae:  60 e4         ldi  r22, 0x40  ; 64
41
  b0:  84 e0         ldi  r24, 0x04  ; 4
42
  b2:  d9 df         rcall  .-78       ; 0x66 <Send_Color>
43
    case  7 : Send_Color(Color,0x80);
44
  b4:  60 e8         ldi  r22, 0x80  ; 128
45
  b6:  84 e0         ldi  r24, 0x04  ; 4
46
  b8:  d6 df         rcall  .-84       ; 0x66 <Send_Color>
47
    case  8 : Send_Color(Color,0xFF);
48
  ba:  6f ef         ldi  r22, 0xFF  ; 255
49
  bc:  84 e0         ldi  r24, 0x04  ; 4
50
  be:  d3 df         rcall  .-90       ; 0x66 <Send_Color>
51
    case  9 : Send_Color(Color,0x80);
52
  c0:  60 e8         ldi  r22, 0x80  ; 128
53
  c2:  84 e0         ldi  r24, 0x04  ; 4
54
  c4:  d0 df         rcall  .-96       ; 0x66 <Send_Color>
55
    case 10 : Send_Color(Color,0x40);
56
  c6:  60 e4         ldi  r22, 0x40  ; 64
57
  c8:  84 e0         ldi  r24, 0x04  ; 4
58
  ca:  cd df         rcall  .-102      ; 0x66 <Send_Color>
59
    case 11 : Send_Color(Color,0x20);
60
  cc:  60 e2         ldi  r22, 0x20  ; 32
61
  ce:  84 e0         ldi  r24, 0x04  ; 4
62
  d0:  ca df         rcall  .-108      ; 0x66 <Send_Color>
63
    case 12 : Send_Color(Color,0x10);
64
  d2:  60 e1         ldi  r22, 0x10  ; 16
65
  d4:  84 e0         ldi  r24, 0x04  ; 4
66
  d6:  c7 df         rcall  .-114      ; 0x66 <Send_Color>
67
    case 13 : Send_Color(Color,0x08);
68
  d8:  68 e0         ldi  r22, 0x08  ; 8
69
  da:  84 e0         ldi  r24, 0x04  ; 4
70
  dc:  c4 df         rcall  .-120      ; 0x66 <Send_Color>
71
    case 14 : Send_Color(Color,0x04);
72
  de:  64 e0         ldi  r22, 0x04  ; 4
73
  e0:  84 e0         ldi  r24, 0x04  ; 4
74
  e2:  c1 df         rcall  .-126      ; 0x66 <Send_Color>
75
    case 15 : Send_Color(Color,0x02);
76
  e4:  62 e0         ldi  r22, 0x02  ; 2
77
  e6:  84 e0         ldi  r24, 0x04  ; 4
78
  e8:  be df         rcall  .-132      ; 0x66 <Send_Color>
79
    case 16 : Send_Color(Color,0x01);
80
  ea:  61 e0         ldi  r22, 0x01  ; 1
81
  ec:  84 e0         ldi  r24, 0x04  ; 4
82
  ee:  bb df         rcall  .-138      ; 0x66 <Send_Color>
83
    default : Send_Color(0x00,0x00);
84
  f0:  60 e0         ldi  r22, 0x00  ; 0
85
  f2:  80 e0         ldi  r24, 0x00  ; 0
86
  f4:  b8 df         rcall  .-144      ; 0x66 <Send_Color>
87
  f6:  08 95         ret

von Michael A. (micha54)


Lesenswert?

Hallo,

also der ijmp springt wohl auf eine Tabelle bei ca. 0x500. Wegen der 
Word-Adressierung des PC spricht er dort die Bytes immer paarweise an, 
was für ein rjmp ausreicht. Leider felt der Bereich im Listing.

Mir fehlt auch im Select noch der Sprung aus den Cases hinter dem rcall 
wieder hinaus auf das Ende des Selects.
Kann es sein, daß Du keine breaks im Sourceode eingebaut hast ? Dann 
durchläuft case 0 also alle cases bis zum Default sequentiell durch ? 
Soll das so sein ?

Gruß,
Michael

: Bearbeitet durch User
von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Peter Zz schrieb:
> Der Compiler hat es wundervoll mit einem berechneten Sprung optimiert,
> die einzelnen Sprungziele liegen jeweils 6 Adressen auseinander.

Wie Michael schon sagte: Es wird kein Sprung berechnet, sondern auf eine 
Tabelle zugegriffen,

> Aber was ich nicht verstehe: wie macht er die Multiplikation mit 6?

Der AVR macht sie jedenfalls nicht, er greift nur auf eine Tabelle zu. 
Du kannst Dir vielleicht auch vorstellen, dass die einzelnen Befehle 
nach einem case durchaus verschiedene Längen haben können. Da hilft 
keine Multiplikation mit 6 ;-)

Gruß,

Frank

: Bearbeitet durch Moderator
von Peter II (Gast)


Lesenswert?

Wenn ich mal davon ausgehen, das die break vergessen wurden. Dann ist 
die Lösung ob doch sehr umständlich.
1
uint8_t Colors[] = { 0x01, 0x02, ... 0x01 };
2
3
void light_wave(uint8_t Color,uint8_t position)
4
{
5
   Send_Color(Color,Colors[Position]);
6
}

man müsste noch das mit dem Default einbauen.

von Peter Z. (Gast)


Angehängte Dateien:

Lesenswert?

Michael Appelt schrieb:
> also der ijmp springt wohl auf eine Tabelle bei ca. 0x500. Wegen der
> Word-Adressierung des PC spricht er dort die Bytes immer paarweise an,
> was für ein rjmp ausreicht. Leider felt der Bereich im Listing.

was du schreibst scheint mir plausibel. Leider ist die Tabelle im 
.iss-File nicht enthalten. Wo könnte sie sein? Habe die Hex-Datei 
angehängt, da müsste die Tabelle ja drin sein?!


> Kann es sein, daß Du keine breaks im Sourceode eingebaut hast ? Dann
> durchläuft case 0 also alle cases bis zum Default sequentiell durch ?
> Soll das so sein ?

Ja, soll so sein!

von Logel (Gast)


Lesenswert?

Hex-Datei? Es ist besser du postest das komplette Listing.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Hier ist sie, die lange gesuchte Sprungtabelle:
1
   :      :              :        :                 :
2
  16:   39 c0           rjmp    .+114           ;  0x8a
3
  18:   3b c0           rjmp    .+118           ;  0x90
4
  1a:   3d c0           rjmp    .+122           ;  0x96
5
  1c:   3f c0           rjmp    .+126           ;  0x9c
6
  1e:   41 c0           rjmp    .+130           ;  0xa2
7
  20:   43 c0           rjmp    .+134           ;  0xa8
8
  22:   45 c0           rjmp    .+138           ;  0xae
9
  24:   47 c0           rjmp    .+142           ;  0xb4
10
  26:   49 c0           rjmp    .+146           ;  0xba
11
  28:   4b c0           rjmp    .+150           ;  0xc0
12
  2a:   4d c0           rjmp    .+154           ;  0xc6
13
  2c:   4f c0           rjmp    .+158           ;  0xcc
14
  2e:   51 c0           rjmp    .+162           ;  0xd2
15
  30:   53 c0           rjmp    .+166           ;  0xd8
16
  32:   55 c0           rjmp    .+170           ;  0xde
17
  34:   57 c0           rjmp    .+174           ;  0xe4
18
  36:   59 c0           rjmp    .+178           ;  0xea
19
   :      :              :        :                 :
20
  7a:   e6 2f           mov     r30, r22
21
  7c:   f0 e0           ldi     r31, 0x00       ; 0
22
  7e:   e1 31           cpi     r30, 0x11       ; 17
23
  80:   f1 07           cpc     r31, r17
24
  82:   b0 f5           brcc    .+108           ;  0xf0
25
  84:   e5 5f           subi    r30, 0xF5       ; 245
26
  86:   ff 4f           sbci    r31, 0xFF       ; 255
27
  88:   09 94           ijmp
28
  8a:   61 e0           ldi     r22, 0x01       ; 1
29
  8c:   84 e0           ldi     r24, 0x04       ; 4
30
  8e:   eb df           rcall   .-42            ;  0x66
31
  90:   62 e0           ldi     r22, 0x02       ; 2
32
  92:   84 e0           ldi     r24, 0x04       ; 4
33
  94:   e8 df           rcall   .-48            ;  0x66
34
  96:   64 e0           ldi     r22, 0x04       ; 4
35
  98:   84 e0           ldi     r24, 0x04       ; 4
36
  9a:   e5 df           rcall   .-54            ;  0x66
37
  9c:   68 e0           ldi     r22, 0x08       ; 8
38
  9e:   84 e0           ldi     r24, 0x04       ; 4
39
   :      :              :        :                 :

Die 16-Bit-Subtraktion vor dem IJMP addiert 11 zur Variable position.
Damit springt der IJMP zur Wortadresse

  0x000b + position

bzw. zur Byteadresse

  0x0016 + 2*position

Und genau bei Adresse 0x0016 beginnt die Serie der JRMPs zu den
einzelnen Case-Zweigen.

von Peter Z. (Gast)


Lesenswert?

Super!
Wie hast du das rausgekriegt?
Hast du da einen Disassembler? Welchen?


Yalu X. schrieb:
> Damit springt der IJMP zur Wortadresse
>
>   0x000b + position
>
> bzw. zur Byteadresse
>
>   0x0016 + 2*position

Wie macht er die Multiplikation mit 2 ?

von (prx) A. K. (prx)


Lesenswert?

Überhaupt nicht. Die Befehle sind wortadressiert.

von Peter Z. (Gast)


Lesenswert?

A. K. schrieb:
> Überhaupt nicht. Die Befehle sind wortadressiert.

Oh! Wirklich? Wo steht das im Datenblatt?
Das erklärt einiges!

von Michael A. (micha54)


Lesenswert?

Peter Zz schrieb:
> Super!
> Wie hast du das rausgekriegt?
> Hast du da einen Disassembler? Welchen?

Hallo,

ich denke, wir benutzen alle myBrain.exe. Ist leider kopiergeschützt.
Aber wenn Du lange genug trainierst bekommst Du auch eins.

Bis dahin findest Du den Assemblercode z.B. im Studio im Simulator.

Mea culpa, hab Hex gerechnet und Dezimal gedacht.

Gruß,
Michael

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Peter Zz schrieb:
> Hast du da einen Disassembler? Welchen?

Es gibt 1000 Möglichkeiten, an den Code zu kommen.  Vermutlich sogar 
noch mehr...

http://rn-wissen.de/wiki/index.php/Assembler-Dump_erstellen_mit_avr-gcc

von Yalu X. (yalu) (Moderator)


Lesenswert?

Peter Zz schrieb:
> Hast du da einen Disassembler?

Ja, du wahrscheinlich auch ;-)

> Welchen?

Das Ding heißt avr-objdump und ist Bestandteil des GNU-Binutil-Pakets,
das überlicherweise zusammen mit dem AVR-GCC installiert wird.

Der Aufruf:
1
avr-objdump -m avr -D ATtiny10.hex

Etwas arg viel anderes macht aber das Makefile bei der Generierung des
lss-Files auch nicht. Schau dir das lss-File also noch einmal an. Die
Sprungtabelle sollte auch dort an der Adresse 0x16, also gleich nach den
Interruptvektoren beginnen.

von Eric (Gast)


Lesenswert?

Michael Appelt schrieb:

> ich denke, wir benutzen alle myBrain.exe.

Tsk, .exe? wie altmodisch!
Sollst doch längst auf MyBrain.app umgestiegen sein!?

von Peter Z. (Gast)


Lesenswert?

Johann L. schrieb:
> Es gibt 1000 Möglichkeiten, an den Code zu kommen.  Vermutlich sogar
> noch mehr...

Johann L. schrieb:
> Es gibt 1000 Möglichkeiten, an den Code zu kommen.  Vermutlich sogar
> noch mehr...
>
> http://rn-wissen.de/wiki/index.php/Assembler-Dump_erstellen_mit_avr-gcc

GCC erzeugt seine Ausgabe als Assembler-Datei; allerdings wird diese 
Assemblerdatei nur temporär angelegt und automatisch wieder gelöscht, 
nachdem der Assembler diese Datei gelesen und verarbeitet hat.

Um ein Löschen dieser Datei zu unterbinden, dient ein Aufruf folgender 
Form:

Kommando:

> gcc -c -fverbose-asm -save-temps ...

Äh, wo gebe ich dieses Kommando unter Atmel Studio 6.1 ein?

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.