Forum: Mikrocontroller und Digitale Elektronik Disassembler Anzeige bei AVR Studio


von Tommy (Gast)


Lesenswert?

Hallo zusammen,
ich habe ein Verständnisproblem und hoffe hier Hilfe zu erhalten.
Wenn ich ein Probgramm in Assembler schreibe und gucke mir den Code mit 
der Disassembler Funktion unter AVR Studio an, dann sieht das wunderbar 
aus.
(Ganz einfaches Programm!)
Wenn ich es aber in C schreibe (gleicher Sinn) und mir dann den 
Assembler code ansehe fängt der schon gar nicht bei Adresse Null an!
Das sieht irgendwie total durcheinander aus. Kann das vielleicht mit der 
Optimierung zu tuen haben? Wobei die auf -Os steht.
Hab mal die ersten Zeilen eingefügt

----UNKNOWN_FILE-------------------------------------------------------- 
-----
0: File not found
+00000000:   940C0038    JMP       0x00000038     Jump

Kann mir jemand was dazu sagen?
Vielen Dank im voraus.

von Alter Hase (Gast)


Lesenswert?

Vermutlich verwendest Du keine Interrupts, dann beginnt
Dein Code wahrscheinlich bei 0x0000. Der Compiler setzt bei Adresse
0x0000 einen Sprung der über die Interrupt-Sprungtabelle hinwegführt.

(Mache bei Asm-Programmen aus Prinzip schon immer so...)
Bei unbenutzen Interrupts steht dann halt ein Reti.

von Tommy (Gast)


Lesenswert?

Habe ein ganz einfaches Programm geschrieben
1
.include "m162def.inc"
2
.def temp = r16
3
 
4
         ldi temp, HIGH(RAMEND)            
5
         out SPH, temp
6
         ldi temp, LOW(RAMEND)             
7
         out SPL, temp
8
   ldi temp, 0xff
9
         out DDRB, temp
10
   ldi R17, 0b10101010
11
         rcall sub1                     
12
         out PORTB, R17
13
loop:    rjmp loop
14
sub1:                                      
15
         push R17                                     
16
         ldi R17, 0                              
17
         pop R17        
18
   ret
Nur für verständnis.
Es stimmt es sind keine Interrupts vorhanden. Heißt das, dass in C der 
Compiler gleich einen Offset für eventuelle Interrupts einfügt?

von Hc Z. (mizch)


Lesenswert?

Keinen Offset, sondern eine Sprungtabelle, also einen Block von Code, 
der aus jmps besteht, für jeden Interruptvektor einen.  Auch danach 
beginnt nicht Dein Programm, sondern es folgen die Flash-Konstanten 
(Dinge, die im Programm mit PROGMEM festgelegt wurden).  Dann kommt der 
C-Startup-Code, und erst danach folgt Dein Programm.

von Tommy (Gast)


Lesenswert?

Okay!
Hab jetzt mal in C
das hier geschrieben:
1
#include<avr/io.h>
2
3
int main()
4
{  
5
  DDRB=0xff;
6
  PORTB=0x55;
7
  
8
  while(1)
9
  {
10
11
  }
12
}
Im Disassembler sieht das so aus:
0: File not found
+00000000:   940C0038    JMP       0x00000038     Jump
+00000002:   940C0055    JMP       0x00000055     Jump
+00000004:   940C0055    JMP       0x00000055     Jump
+00000006:   940C0055    JMP       0x00000055     Jump
+00000008:   940C0055    JMP       0x00000055     Jump
+0000000A:   940C0055    JMP       0x00000055     Jump
+0000000C:   940C0055    JMP       0x00000055     Jump
+0000000E:   940C0055    JMP       0x00000055     Jump
+00000010:   940C0055    JMP       0x00000055     Jump
+00000012:   940C0055    JMP       0x00000055     Jump
+00000014:   940C0055    JMP       0x00000055     Jump
+00000016:   940C0000    JMP       0x00000000     Jump
+00000018:   940C0055    JMP       0x00000055     Jump
+0000001A:   940C0055    JMP       0x00000055     Jump
+0000001C:   940C0055    JMP       0x00000055     Jump
+0000001E:   940C0055    JMP       0x00000055     Jump
+00000020:   940C0055    JMP       0x00000055     Jump
+00000022:   940C0055    JMP       0x00000055     Jump
+00000024:   940C0055    JMP       0x00000055     Jump
+00000026:   940C0055    JMP       0x00000055     Jump
+00000028:   940C0055    JMP       0x00000055     Jump
+0000002A:   940C0055    JMP       0x00000055     Jump
+0000002C:   940C0055    JMP       0x00000055     Jump
+0000002E:   940C0055    JMP       0x00000055     Jump
+00000030:   940C0055    JMP       0x00000055     Jump
+00000032:   940C0055    JMP       0x00000055     Jump
+00000034:   940C0055    JMP       0x00000055     Jump
+00000036:   940C0055    JMP       0x00000055     Jump
+00000038:   2411        CLR       R1             Clear Register
+00000039:   BE1F        OUT       0x3F,R1        Out to I/O location
+0000003A:   EFCF        SER       R28            Set Register
+0000003B:   E0D4        LDI       R29,0x04       Load immediate
+0000003C:   BFDE        OUT       0x3E,R29       Out to I/O location
+0000003D:   BFCD        OUT       0x3D,R28       Out to I/O location
+0000003E:   E011        LDI       R17,0x01       Load immediate
+0000003F:   E0A0        LDI       R26,0x00       Load immediate
+00000040:   E0B1        LDI       R27,0x01       Load immediate
+00000041:   EBEC        LDI       R30,0xBC       Load immediate
+00000042:   E0F0        LDI       R31,0x00       Load immediate
+00000043:   C002        RJMP      PC+0x0003      Relative jump
+00000044:   9005        LPM       R0,Z+          Load program memory 
and postincrement
+00000045:   920D        ST        X+,R0          Store indirect and 
postincrement
+00000046:   30A0        CPI       R26,0x00       Compare with immediate
+00000047:   07B1        CPC       R27,R17        Compare with carry
+00000048:   F7D9        BRNE      PC-0x04        Branch if not equal
+00000049:   E011        LDI       R17,0x01       Load immediate
+0000004A:   E0A0        LDI       R26,0x00       Load immediate
+0000004B:   E0B1        LDI       R27,0x01       Load immediate
+0000004C:   C001        RJMP      PC+0x0002      Relative jump
+0000004D:   921D        ST        X+,R1          Store indirect and 
postincrement
+0000004E:   30A0        CPI       R26,0x00       Compare with immediate
+0000004F:   07B1        CPC       R27,R17        Compare with carry
+00000050:   F7E1        BRNE      PC-0x03        Branch if not equal
+00000051:   940E0057    CALL      0x00000057     Call subroutine
+00000053:   940C005C    JMP       0x0000005C     Jump
+00000055:   940C0000    JMP       0x00000000     Jump
@00000057: main
---- 2010-05-26_jetzt_c.c 
------------------------------------------------------------------------ 
-
4:        {
+00000057:   EF8F        SER       R24            Set Register
+00000058:   BB87        OUT       0x17,R24       Out to I/O location
6:          PORTB=0x55;
+00000059:   E585        LDI       R24,0x55       Load immediate
+0000005A:   BB88        OUT       0x18,R24       Out to I/O location
+0000005B:   CFFF        RJMP      PC-0x0000      Relative jump
6:          PORTB=0x55;
+0000005C:   94F8        CLI                      Global Interrupt 
Disable
+0000005D:   CFFF        RJMP      PC-0x0000      Relative jump

Aber er scheint ja etliche Male am Anfang immer zur gleichen Stelle zu 
springen! Warum macht er das denn?

von Hc Z. (mizch)


Lesenswert?

> Aber er scheint ja etliche Male am Anfang immer zur gleichen Stelle zu
> springen! Warum macht er das denn?

Interrupts, die im Programm nicht belegt sind, werden mit einem Sprung 
zu einem Default-Handler vorbelegt, um nicht in der Wüste zu landen. 
Der Default-Handler macht nichts als einen Soft-Reset (Sprung zu dem 
Vektor auf Adresse 0 -- den Default-Handler hast Du bei Dir auf Adresse 
0x55).

von MarioT (Gast)


Lesenswert?

Tommy schrieb:
> Aber er scheint ja etliche Male am Anfang immer zur gleichen Stelle zu
> springen! Warum macht er das denn?

Er überspringt die Interupt Tabelle.

von Tommy (Gast)


Lesenswert?

Okay ich glaube so langsam schnall ichs!
Die Interrupteinspring Tabelle steht ja am Anfang des Flash und wird im 
C Programm übersprungen. Das ist doch soweit richtig. Aber was ich jetzt 
nicht verstehe, ist das in der Assemblerversion das Programm keine 
Adressen am Anfang überspringt. Also doch nicht geschnalt. Oder ist das 
so, dass die Assembler Version gar nicht die Interrupt Tabelle einfügt, 
wenn sie nicht genutzt wird. Kann man sich das so vorstellen? Wenn das 
so ist, wäre für kleine Programme Assembler immer platzsparender als C 
oder?

von Hc Z. (mizch)


Lesenswert?

Nein.  In der Assembler-Version musst Du die Interrupttabelle 
schreiben (so Du eine haben willst), denn die Laufzeitumgebung bestimmst 
alleine Du.  In Assembler wird kein vorgefertigter Code hinzugefügt, 
solange Du es nicht befiehlst.

von Tommy (Gast)


Lesenswert?

Ah okay! Aber wenn ich einen bestimmten Interrupt bentzen will, dann 
muss ich darauf achten, wo er im Flash hingeschrieben werden muss, da 
sie an fest definierten Adressen stehen müssen.

von Kluchscheißernder N. (kluchscheisser)


Lesenswert?

Tommy schrieb:
> Ah okay! Aber wenn ich einen bestimmten Interrupt bentzen will, dann
> muss ich darauf achten, wo er im Flash hingeschrieben werden muss,

Richtig

> da
> sie an fest definierten Adressen stehen müssen.

Genau. Und diese Adressen sind quasi eine Vereinbarung zwischen 
Controller-Hersteller und Programmierer (oder Compilerbauer). Genaue 
Informationen dazu findest Du im Datenblatt des betreffenden 
Controllers, denn jeder AVR ist anders mit (interruptauslösenden) 
Features ausgestattet, braucht also eine andere Sprungtabelle.

MfG, Consulter

von Tommy (Gast)


Lesenswert?

Und ich hätte noch eine Frage:
Mein Default Handler springt ja an Adresse 0x0000000.
Ist das dann nicht eine Endlosschleife?

+00000000:   940C0038    JMP       0x00000038     Jump
+00000002:   940C0055    JMP       0x00000055     Jump
+00000004:   940C0055    JMP       0x00000055     Jump

Kommt er zur Adresse +00000002 springt der zur Adresse 0x00000055 die 
ihm sagt spring nach Adresse +000000000. Fängt das dann nicht wieder von 
vorne an?

von Hc Z. (mizch)


Lesenswert?

Er geht ja von 0 nicht auf 2, sondern setzt bei 0x38 fort.

von Tommy (Gast)


Lesenswert?

Vielen Dank an alle!
Jetzt hab ichs geschnallt!
Wirklich letzte Frage:
Dann kann ich doch anhand der Junps am Anfang feststellen, wie viele 
Interrupts der MC hat, oder? Ich weiß zwar nicht was für welche aber die 
Anzahl weiß ich.

von MarioT (Gast)


Lesenswert?

Hc Zimmerer schrieb:
> Oder ist das
> so, dass die Assembler Version gar nicht die Interrupt Tabelle einfügt,
> wenn sie nicht genutzt wird.

Das ist dem Aessemler egal, ob Du die Interrupt Tabelle benutzt oder 
nicht. Er schreibt sie so wie Du sie schreibst, schreibst Du keine 
schreibt er auch keine.

Tommy schrieb:
> Ah okay! Aber wenn ich einen bestimmten Interrupt bentzen will, dann
> muss ich darauf achten, wo er im Flash hingeschrieben werden muss, da
> sie an fest definierten Adressen stehen müssen.

Wenn ein bestimmter Interrupt ausgelöst wird springt der Programmcounter 
an die bestimmte Adresse von diesem Interrupt, da muß die Sprungadresse 
stehen, wo der Code für diesen Interrupt steht.

Tommy schrieb:
> Kommt er zur Adresse +00000002 springt der zur Adresse 0x00000055 die
> ihm sagt spring nach Adresse +000000000. Fängt das dann nicht wieder von
> vorne an?

Ja! und dann nach 0x00000038 wo dein Programm anfängt.

von Hc Z. (mizch)


Lesenswert?

> Hc Zimmerer schrieb:
Das schrieb ich nicht.  Bitte korrekt zitieren.

von MarioT (Gast)


Lesenswert?

Hc Zimmerer schrieb:
>> Hc Zimmerer schrieb:
> Das schrieb ich nicht.  Bitte korrekt zitieren.

Muß natürlich heißen "Tommy schrieb:"
(Wenn man Text markiert und dann rechts klickt "Markierten Text 
Zitieren" und man erwischt einen anderen passiert dies.) war keine 
Absicht.

Tommy schrieb:
> Dann kann ich doch anhand der Junps am Anfang feststellen, wie viele
> Interrupts der MC hat, oder? Ich weiß zwar nicht was für welche aber die
> Anzahl weiß ich.

Sehe mal hier mit dem Editor ganz unten
C:\Programme\Atmel\AVR Tools\AvrAssembler\Appnotes\m162def.inc

von Tommy (Gast)


Lesenswert?

Ja ja so landsam durchschau ich es! Doch keine Zauberei!
Noch mal vielen Dank an allen.

von Karl H. (kbuchegg)


Lesenswert?

MarioT schrieb:

> Tommy schrieb:
>> Kommt er zur Adresse +00000002 springt der zur Adresse 0x00000055 die
>> ihm sagt spring nach Adresse +000000000. Fängt das dann nicht wieder von
>> vorne an?
>
> Ja! und dann nach 0x00000038 wo dein Programm anfängt.

@Tommy

Ganz genau.
Das Problem ist: Auf einem µC muss immer irgendwas passieren. Ein Aufruf 
eines Interrupts, für den es keinen Handler gibt, ist natürlich ein 
Fehler. Aber was soll denn der Default Handler machen? Er weiß ja nichts 
von deiner Hardware. Also sind die gcc Leute den Weg des geringsten 
Widerstands gegangen und haben das einzig Sinnvolle getan, was sie tun 
können: Sie lassen das komplette Programm neu starten. Das gibt dir zum 
einen die Chance, den Fehler während der Entwicklung festzustellen und 
führt auf der andern Seite dazu dass in einer Produktionsumgebung 
zumindest die Chance besteht, dass dein µC nach dem Fehler wieder seine 
Arbeit aufnimmt.
Denkbar wäre auch gewesen:
den µC in einer Endlosschleife gefangen zu halten. Das hat aber den 
Nachteil, dass dann mit Sicherheit dein µC hängt.
den Interrupt einfach zu ignorieren, gar nichts zu tun und zu returnen. 
Das hat aber wieder den Nachteil, dass du in der Entwicklungsphase es 
nicht bemerkst, dass du einen Interrupt freigegeben hast, der nicht 
behandelt wird.

Zu guter letzt: Es gibt auch noch die Möglichkeit sich selbst einen 
Default-Handler zu schreiben, der sich dann so verhält, wie du dir das 
vorstellst.

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.