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:
1
#include<avr/io.h>
2
3
intmain()
4
{
5
DDRD=1<<6;//PD6 als Ausgang
6
DDRB=0xFF;//Alle PB Ports als Ausgänge
7
8
PORTD=1<<6;
9
PORTB=1<<3;
10
11
while(1);
12
}
Aus dem wurde dann:
1
; Disassembly of main.hex (avr-gcc style)
2
3
.text
4
main:
5
cpi r19, 0x1a ; 26
6
cpi r19, 0x00 ; 0
7
cpi r19, 0x00 ; 0
8
cpi r19, 0x00 ; 0
9
cpi r19, 0x10 ; 16
10
sbci r19, 0x32 ; 50
11
cpi r19, 0x10 ; 16
12
sbci r19, 0x37 ; 55
13
cpi r19, 0x10 ; 16
14
sbci r19, 0x36 ; 54
15
cpi r19, 0x10 ; 16
16
sbci r19, 0x35 ; 53
17
cpi r19, 0x10 ; 16
18
sbci r19, 0x34 ; 52
19
cpi r19, 0x10 ; 16
20
sbci r19, 0x33 ; 51
21
cpi r19, 0x10 ; 16
22
sbci r19, 0x32 ; 50
23
cpi r19, 0x10 ; 16
24
sbci r19, 0x31 ; 49
25
cpi r19, 0x50 ; 80
26
sbc r3, r18
27
cpi r19, 0x1a ; 26
28
cpi r19, 0x00 ; 0
29
cpi r19, 0x10 ; 16
30
cpi r19, 0x00 ; 0
31
cpi r19, 0x10 ; 16
32
sbci r19, 0x30 ; 48
33
cpi r19, 0x00 ; 0
34
sbci r20, 0x36 ; 54
35
cpi r19, 0x00 ; 0
36
sbci r20, 0x35 ; 53
37
cpi r19, 0x00 ; 0
38
sbci r20, 0x34 ; 52
39
cpi r19, 0x00 ; 0
40
sbci r20, 0x33 ; 51
41
cpi r19, 0x00 ; 0
42
sbci r20, 0x32 ; 50
43
cpi r19, 0x00 ; 0
44
sbci r20, 0x31 ; 49
45
cpi r19, 0x00 ; 0
46
sbci r19, 0x39 ; 57
47
cpi r19, 0x70 ; 112
48
sbc r4, r19
49
cpi r19, 0x1a ; 26
50
cpi r19, 0x00 ; 0
51
cpi r19, 0x20 ; 32
52
cpi r19, 0x00 ; 0
53
cpi r19, 0x00 ; 0
54
sbci r19, 0x38 ; 56
55
cpi r19, 0x00 ; 0
56
sbci r19, 0x37 ; 55
57
cpi r19, 0x00 ; 0
58
sbci r19, 0x36 ; 54
59
cpi r19, 0x10 ; 16
60
cpi r19, 0x21 ; 33
61
cpi r19, 0x14 ; 20
62
sbci r20, 0x26 ; 38
63
sbci r20, 0x35 ; 53
64
sbci r20, 0x56 ; 86
65
sbci r20, 0x34 ; 52
66
sbci r20, 0x24 ; 36
67
cpi r20, 0x06 ; 6
68
sbci r19, 0x42 ; 66
69
cpi r19, 0x40 ; 64
70
sbc r4, r22
71
cpi r19, 0x1a ; 26
72
cpi r19, 0x00 ; 0
73
cpi r19, 0x30 ; 48
74
cpi r19, 0x00 ; 0
75
cpi r19, 0x00 ; 0
76
sbci r19, 0x39 ; 57
77
sbci r19, 0x50 ; 80
78
sbci r19, 0x36 ; 54
79
cpi r20, 0x86 ; 134
80
sbci r19, 0x50 ; 80
81
cpi r19, 0x84 ; 132
82
sbci r19, 0x21 ; 33
83
cpi r20, 0x92 ; 146
84
sbci r20, 0x56 ; 86
85
cpi r20, 0x96 ; 150
86
sbci r19, 0x27 ; 39
87
cpi r20, 0x82 ; 130
88
sbci r19, 0x22 ; 34
89
cpi r20, 0x82 ; 130
90
sbci r19, 0x58 ; 88
91
cpi r19, 0x10 ; 16
92
sbc r4, r20
93
cpi r19, 0x0a ; 10
94
cpi r19, 0x08 ; 8
95
cpi r19, 0x40 ; 64
96
cpi r19, 0x00 ; 0
97
cpi r19, 0x80 ; 128
98
sbci r19, 0x28 ; 40
99
sbci r20, 0x62 ; 98
100
sbci r20, 0x36 ; 54
101
sbci r20, 0x66 ; 102
102
cpi r19, 0x98 ; 152
103
sbci r19, 0x64 ; 100
104
sbci r20, 0x36 ; 54
105
cpi r20, 0x46 ; 70
106
sbc r4, r20
107
cpi r19, 0x0a ; 10
108
cpi r19, 0x00 ; 0
109
cpi r19, 0x00 ; 0
110
cpi r19, 0x00 ; 0
111
sbci r19, 0x61 ; 97
112
sbc r4, r22
Kann mir jemand mal erklären was das soll? Der macht ja dauernd
irgendwelche vergleiche, und subtrahiert irgendwas.
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
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.
>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() ;)
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.
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:
1
; Disassembly of main.bin (avr-gcc style)
2
3
.text
4
main:
5
6
; Referenced from offset 0x32 by rjmp
7
Label1:
8
rjmp Label2
9
rjmp Label3
10
rjmp Label3
11
rjmp Label3
12
rjmp Label3
13
rjmp Label3
14
rjmp Label3
15
rjmp Label3
16
rjmp Label3
17
rjmp Label3
18
rjmp Label3
19
rjmp Label3
20
rjmp Label3
21
rjmp Label3
22
rjmp Label3
23
rjmp Label3
24
rjmp Label3
25
rjmp Label3
26
rjmp Label3
27
28
; Referenced from offset 0x00 by rjmp
29
Label2:
30
clr r1
31
out 0x3f, r1 ; 63
32
ldi r28, 0xdf ; 223
33
out 0x3d, r28 ; 61
34
rcall Function1
35
rjmp Label5
36
37
; Referenced from offset 0x04 by rjmp
38
; Referenced from offset 0x06 by rjmp
39
; Referenced from offset 0x08 by rjmp
40
; Referenced from offset 0x0a by rjmp
41
; Referenced from offset 0x0c by rjmp
42
; Referenced from offset 0x0e by rjmp
43
; Referenced from offset 0x10 by rjmp
44
; Referenced from offset 0x12 by rjmp
45
; Referenced from offset 0x02 by rjmp
46
; Referenced from offset 0x16 by rjmp
47
; Referenced from offset 0x18 by rjmp
48
; Referenced from offset 0x1a by rjmp
49
; Referenced from offset 0x1c by rjmp
50
; Referenced from offset 0x1e by rjmp
51
; Referenced from offset 0x20 by rjmp
52
; Referenced from offset 0x22 by rjmp
53
; Referenced from offset 0x24 by rjmp
54
; Referenced from offset 0x14 by rjmp
55
Label3:
56
rjmp Label1
57
58
; Referenced from offset 0x2e by rcall
59
Function1:
60
ldi r24, 0x40 ; 64
61
out 0x11, r24 ; 17
62
ser r25
63
out 0x17, r25 ; 23
64
out 0x12, r24 ; 18
65
ldi r24, 0x08 ; 8
66
out 0x18, r24 ; 24
67
68
; Referenced from offset 0x42 by rjmp
69
Label4:
70
rjmp Label4
71
72
; Referenced from offset 0x30 by rjmp
73
Label5:
74
cli
75
76
; Referenced from offset 0x46 by rjmp
77
Label6:
78
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?
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/
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?
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.
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