www.mikrocontroller.net

Forum: Compiler & IDEs Was macht der Compiler mit meinen paar Zeilen C-Code?


Autor: Sam .. (sam1994)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi

Ich hab mich mal vor kurzem ein bisschen in Asm eingelesen. Als ich aber 
dann mal ein Programm kompiliert habe und auf die verdammte Größe von 72 
Bytes starrte, fragte ich mich was für einen Mist der Compiler da macht? 
Schreibt der da Standardmäßig irgendweche Routinen rein?

Das war der Code:
#include <avr/io.h>

int main()
{
    DDRD = 1 << 6; //PD6 als Ausgang
    DDRB = 0xFF;   //Alle PB Ports als Ausgänge
    
    PORTD = 1 << 6;
    PORTB = 1 << 3;
    
    while(1);
}

Aus dem wurde dann:
; Disassembly of main.hex (avr-gcc style)

.text
main:
cpi     r19, 0x1a       ; 26
cpi     r19, 0x00       ; 0
cpi     r19, 0x00       ; 0
cpi     r19, 0x00       ; 0
cpi     r19, 0x10       ; 16
sbci    r19, 0x32       ; 50
cpi     r19, 0x10       ; 16
sbci    r19, 0x37       ; 55
cpi     r19, 0x10       ; 16
sbci    r19, 0x36       ; 54
cpi     r19, 0x10       ; 16
sbci    r19, 0x35       ; 53
cpi     r19, 0x10       ; 16
sbci    r19, 0x34       ; 52
cpi     r19, 0x10       ; 16
sbci    r19, 0x33       ; 51
cpi     r19, 0x10       ; 16
sbci    r19, 0x32       ; 50
cpi     r19, 0x10       ; 16
sbci    r19, 0x31       ; 49
cpi     r19, 0x50       ; 80
sbc     r3, r18
cpi     r19, 0x1a       ; 26
cpi     r19, 0x00       ; 0
cpi     r19, 0x10       ; 16
cpi     r19, 0x00       ; 0
cpi     r19, 0x10       ; 16
sbci    r19, 0x30       ; 48
cpi     r19, 0x00       ; 0
sbci    r20, 0x36       ; 54
cpi     r19, 0x00       ; 0
sbci    r20, 0x35       ; 53
cpi     r19, 0x00       ; 0
sbci    r20, 0x34       ; 52
cpi     r19, 0x00       ; 0
sbci    r20, 0x33       ; 51
cpi     r19, 0x00       ; 0
sbci    r20, 0x32       ; 50
cpi     r19, 0x00       ; 0
sbci    r20, 0x31       ; 49
cpi     r19, 0x00       ; 0
sbci    r19, 0x39       ; 57
cpi     r19, 0x70       ; 112
sbc     r4, r19
cpi     r19, 0x1a       ; 26
cpi     r19, 0x00       ; 0
cpi     r19, 0x20       ; 32
cpi     r19, 0x00       ; 0
cpi     r19, 0x00       ; 0
sbci    r19, 0x38       ; 56
cpi     r19, 0x00       ; 0
sbci    r19, 0x37       ; 55
cpi     r19, 0x00       ; 0
sbci    r19, 0x36       ; 54
cpi     r19, 0x10       ; 16
cpi     r19, 0x21       ; 33
cpi     r19, 0x14       ; 20
sbci    r20, 0x26       ; 38
sbci    r20, 0x35       ; 53
sbci    r20, 0x56       ; 86
sbci    r20, 0x34       ; 52
sbci    r20, 0x24       ; 36
cpi     r20, 0x06       ; 6
sbci    r19, 0x42       ; 66
cpi     r19, 0x40       ; 64
sbc     r4, r22
cpi     r19, 0x1a       ; 26
cpi     r19, 0x00       ; 0
cpi     r19, 0x30       ; 48
cpi     r19, 0x00       ; 0
cpi     r19, 0x00       ; 0
sbci    r19, 0x39       ; 57
sbci    r19, 0x50       ; 80
sbci    r19, 0x36       ; 54
cpi     r20, 0x86       ; 134
sbci    r19, 0x50       ; 80
cpi     r19, 0x84       ; 132
sbci    r19, 0x21       ; 33
cpi     r20, 0x92       ; 146
sbci    r20, 0x56       ; 86
cpi     r20, 0x96       ; 150
sbci    r19, 0x27       ; 39
cpi     r20, 0x82       ; 130
sbci    r19, 0x22       ; 34
cpi     r20, 0x82       ; 130
sbci    r19, 0x58       ; 88
cpi     r19, 0x10       ; 16
sbc     r4, r20
cpi     r19, 0x0a       ; 10
cpi     r19, 0x08       ; 8
cpi     r19, 0x40       ; 64
cpi     r19, 0x00       ; 0
cpi     r19, 0x80       ; 128
sbci    r19, 0x28       ; 40
sbci    r20, 0x62       ; 98
sbci    r20, 0x36       ; 54
sbci    r20, 0x66       ; 102
cpi     r19, 0x98       ; 152
sbci    r19, 0x64       ; 100
sbci    r20, 0x36       ; 54
cpi     r20, 0x46       ; 70
sbc     r4, r20
cpi     r19, 0x0a       ; 10
cpi     r19, 0x00       ; 0
cpi     r19, 0x00       ; 0
cpi     r19, 0x00       ; 0
sbci    r19, 0x61       ; 97
sbc     r4, r22

Kann mir jemand mal erklären was das soll? Der macht ja dauernd 
irgendwelche vergleiche, und subtrahiert irgendwas.

Autor: Ich (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn ich mich jetz nicht täusche, fehlt hier die while-Schleife.

Autor: Peter Diener (pdiener) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wie hast du denn das asm-file erzeugt (gcc Aufruf mit Parametern)? 
Normalerweise macht der keinen solchen Unsinn.

Ich sehe garnicht, wo das passieren soll, was im Quelltext steht.

Grüße,

Peter

Autor: Rufus Τ. Firefly (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter Diener schrieb:
> Wie hast du denn das asm-file erzeugt (gcc Aufruf mit Parametern)?
> Normalerweise macht der keinen solchen Unsinn.

Das ist kein gcc-Output, sondern der eines Disassemblers.

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Als ich aber
>dann mal ein Programm kompiliert habe und auf die verdammte Größe von 72
>Bytes starrte,

Ach mein Gottchen, das ist aber echt viel!
Es gibt noch ein Leben vor main() ;)

Autor: Ansgar K. (paulderbademeister)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
1. C braucht immer etwas mehr Platz als man aufgrund des Codes denken 
würde, da z.B. vor dem Start der main noch Variablen initialisiert 
werden, alle Interrupts mit Dummysprüngen belegt werden und ähnliches...

2. Das Listing passt überhaupt nicht zu deinem C-Code - falls das 
Programm läuft würde ich sagen, dass der Disassembler Murks produziert. 
Z.B. sehe ich da keinen einzigen Speicherbefehl, der etwas auf die Ports 
gibt.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also bei mir erzeugt er dieses Listing (ohne Prolog):
int main()
{
    DDRD = 1 << 6; //PD6 als Ausgang
  34:  80 e4         ldi  r24, 0x40  ; 64
  36:  81 bb         out  0x11, r24  ; 17
    DDRB = 0xFF;   //Alle PB Ports als Ausgänge
  38:  9f ef         ldi  r25, 0xFF  ; 255
  3a:  97 bb         out  0x17, r25  ; 23
    
    PORTD = 1 << 6;
  3c:  82 bb         out  0x12, r24  ; 18
    PORTB = 1 << 3;
  3e:  88 e0         ldi  r24, 0x08  ; 8
  40:  88 bb         out  0x18, r24  ; 24
  42:  ff cf         rjmp  .-2        ; 0x42 <__SREG__+0x3>


Peter

Autor: Sam .. (sam1994)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
achso, jetzt weiß ich meinen Fehler. Ich hab das HexFile genommen und 
das nimmt das Programm nicht an.

Entschuldigt bitte meine Unwissenheit.

Hier ist das ganze mit der bin-Datei:
; Disassembly of main.bin (avr-gcc style)

.text
main:

; Referenced from offset 0x32 by rjmp
Label1:
rjmp    Label2
rjmp    Label3
rjmp    Label3
rjmp    Label3
rjmp    Label3
rjmp    Label3
rjmp    Label3
rjmp    Label3
rjmp    Label3
rjmp    Label3
rjmp    Label3
rjmp    Label3
rjmp    Label3
rjmp    Label3
rjmp    Label3
rjmp    Label3
rjmp    Label3
rjmp    Label3
rjmp    Label3

; Referenced from offset 0x00 by rjmp
Label2:
clr     r1
out     0x3f, r1        ; 63
ldi     r28, 0xdf       ; 223
out     0x3d, r28       ; 61
rcall   Function1
rjmp    Label5

; Referenced from offset 0x04 by rjmp
; Referenced from offset 0x06 by rjmp
; Referenced from offset 0x08 by rjmp
; Referenced from offset 0x0a by rjmp
; Referenced from offset 0x0c by rjmp
; Referenced from offset 0x0e by rjmp
; Referenced from offset 0x10 by rjmp
; Referenced from offset 0x12 by rjmp
; Referenced from offset 0x02 by rjmp
; Referenced from offset 0x16 by rjmp
; Referenced from offset 0x18 by rjmp
; Referenced from offset 0x1a by rjmp
; Referenced from offset 0x1c by rjmp
; Referenced from offset 0x1e by rjmp
; Referenced from offset 0x20 by rjmp
; Referenced from offset 0x22 by rjmp
; Referenced from offset 0x24 by rjmp
; Referenced from offset 0x14 by rjmp
Label3:
rjmp    Label1

; Referenced from offset 0x2e by rcall
Function1:
ldi     r24, 0x40       ; 64
out     0x11, r24       ; 17
ser     r25
out     0x17, r25       ; 23
out     0x12, r24       ; 18
ldi     r24, 0x08       ; 8
out     0x18, r24       ; 24

; Referenced from offset 0x42 by rjmp
Label4:
rjmp    Label4

; Referenced from offset 0x30 by rjmp
Label5:
cli

; Referenced from offset 0x46 by rjmp
Label6:
rjmp    Label6

Die vielen rjmp macht c Standardmäßig rein, oder wie? ISt das nicht 
unnötig?
Und warum deaktiviert der globale Interrupts wenn ich es nichtmal im 
C-Code geschrieben habe. Erfindet der das einfach dazu, damit die 
schleife wirklich unendlich ist?

Autor: Michael H. (michael_h45)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Samuel K. schrieb:
> achso, jetzt weiß ich meinen Fehler. Ich hab das HexFile genommen und
> das nimmt das Programm nicht an.
Der hatte wohl eine Binärdatei erwartet und kein Intel-Hex.
http://hex2bin.sourceforge.net/

Autor: Markus Engel (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die Jumps gehören zur Interrupt-Vektor-Tabelle. Die liegt am Anfang vom 
Flash und darin steht, bei welchem Interrupt er wohin springen soll. Die 
sollte auch im Assembler-Code drinstehen, den du selber schreibst ;) .
Der erste Sprung zum Label2 ist der Reset-Vektor. Der wird nach dem 
Strom-An ausgeführt und springt zu deinem Code. Alle anderen springen 
über den Umweg Label3 zum Anfang zurück, machen also einen Reset, wenn 
versehentlich ein solcher, von dir nicht verwendeter Interrupt ausgelöst 
wird.
Das mit dem cli bastelt der Compiler dazu, richtig. Störts dich?

Autor: Ansgar K. (paulderbademeister)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Samuel K. schrieb:
> Und warum deaktiviert der globale Interrupts wenn ich es nichtmal im
> C-Code geschrieben habe. Erfindet der das einfach dazu, damit die
> schleife wirklich unendlich ist?

Das ist eine zusätzliche Schleife, die der Compiler hinzufügt, um den µC 
davon abzuhalten weiterzumachen, falls aus irgendeinem Grund aus der 
main zurückgesprungen wird. Ansonsten könnte er beispielsweise 
probieren, die Anfangswerte von Variablen (die weiter hinten im Flash 
liegen) als ausführbaren Code zu interpretieren und Mist produzieren. Da 
wenn diese Stelle erreicht wird schon irgendetwas schiefgelaufen ist, 
werden auch Interrupts abgeschaltet und der Controller macht erst nach 
einem Reset weiter.

Autor: Simon Huwyler (simi)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Der C-Compiler bastelt überhaupt nichts, was Du nicht explizit 
schreibst. Wäre ja auch merkwürdig und würde mich auch irgendwie jucken.

ABER:

Wie oben schon ironisch erwähnt: Es gibt ein Leben vor main(). Dieses 
Leben heisst z.B. crt0. oder startup. Das ist ein Object-File, welches 
vom Linker mit in Dein Binary reingelinkt wird. Der Sinn ist wie folgt:

Der C-Compiler erzeugt Dir aus Deinem C-File Befehl für Befehl 
Maschinencode. Nicht mehr und nicht weniger. Und den schmeisst er in 
Dein main.o

Nehmen wir mal an, Du würdest NUR dieses main.o in Dein bin-File 
schreiben. Dann wäre also der erste Befehl von main() auf Adresse 0 
(nehme ich mal an, kommt auf das Link-Script an). Ok, der Prozi wird da 
anfangen zu robotten.

Aber was ist z.B. mit einer globalen Variable, die Du auf einen Wert 
initialisiert hast?

z.B. int x = 5; (im global scope)?

Jetzt müsste der C-compiler eigentlich Code "erfinden", der irgendwoher 
eine Fünf nimmt und sie in eine RAM-Zelle schreibt. Wohin soll der Code? 
in main.o? Nö, sicher nicht, ist ja nicht im Scope von main(). Wohin 
also?

Anwort: Nirgendwohin. Der Compiler teilt dem Linker bloss mit:
Ich möchte, dass Du im ROM eine Speicherzelle mit einer 5 beschreibst. 
Und ich möchte, dass Du mir eine Speicherzelle im RAM reserviertst.

Und jetzt geht der Compiler einfach frech davon aus, dass die 5 ins Ram 
gespeichert wird, bevor main() aufgerufen wird.
WIE das geschieht, ist dem C-Compiler scheissegal. Und genau dafür 
braucht es z.B. so ein crt0 oder startup Object, welches dieses 
Initialisieren erledigt.

Und dann gibt's halt noch viele andere Dinge, die erledigt werden 
müssen, und die den C-Compiler überhaupt nichts interessieren. Wie eben 
z.B. Interrupts deaktivieren. Interrupt-Vektoren abspitzen. Oszillatoren 
konfigurieren etcetc....

Warum interessiert all das den C-Compiler nicht? Weil er keine Ahnung 
haben darf, wie das geschieht. Er soll ja prozessorunabhängig sein.

crt0 ist also nichts anderes als eine Routine - resp. ein Object-File, 
geschrieben in Assembler oder wiederum in (sehr limitiertem) C. Und DICH 
braucht dieses crt0 überhaupt nicht zu kümmern. Denn wie der C-Compiler 
willst Du einfach davon ausgehen dürfen, dass alles so eingerichtet ist, 
wie C es verlangt - vor Aufruf von main().
Und wer nun schreibt dieses crt0? Üblicherweise die Typen, die den Prozi 
machen und/oder die Typen, die den Compiler rausbringen.


Gruäss
Simon

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.