Forum: Compiler & IDEs Jump Befehl wie notieren (word oder byte)


von Florian (Gast)


Lesenswert?

Ich bin am Spielen mit dem Bootloader. Klappt so weit auch. Aber eine 
Frage, die sich beim Lesen von 
http://www.mikrocontroller.net/articles/AVR_Bootloader_in_C_-_eine_einfache_Anleitung 
aufdrängt, habe ich:

Wenn ich in den Bootloader vom Application Flash springen will, wie 
mache ich das richtig? Ich habe einen mega16, beide BOOTSIZE Bits 
gesetzt = 1024 words, Startadresse => 0x1C00 (word)/0x3800 (byte)
Es geht:
1
asm("jmp 0x3800");
aber auch
1
((void (*)(void))0x3800)();

Angeblich soll man ja bei AVR GCC die Adresse bei C (also im zweiten 
Fall) in word Schreibweise angeben. Müßte also
1
((void (*)(void))0x1c00)();
lauten.

Schaue ich aber den generierten Maschinencode an, sehe ich bei
1
asm("jmp 0x3800");
als Ergebnis
1
int main()
2
{
3
asm("jmp 0x3800");
4
  6c:  0c 94 00 1c   jmp  0x3800  ; 0x3800 <__stack+0x33a1>
5
  70:  80 e0         ldi  r24, 0x00  ; 0
6
  72:  90 e0         ldi  r25, 0x00  ; 0
7
  74:  08 95         ret
8
9
...
und bei
1
((void (*)(void))0x3800)();
erhalte ich
1
int main()
2
{
3
((void (*)(void))0x3800)();
4
  6c:  e0 e0         ldi  r30, 0x00  ; 0
5
  6e:  f8 e3         ldi  r31, 0x38  ; 56
6
  70:  09 95         icall
7
  72:  80 e0         ldi  r24, 0x00  ; 0
8
  74:  90 e0         ldi  r25, 0x00  ; 0
9
  76:  08 95         ret
10
11
...

Also sind beide schreibweisen in Byte Notierung doch eigentlich richtig, 
denn bei
1
((void (*)(void))0x1c00)();
wird generiert:
1
((void (*)(void))0x1c00)();
2
  6c:  e0 e0         ldi  r30, 0x00  ; 0
3
  6e:  fc e1         ldi  r31, 0x1C  ; 28
4
5
...

Das ist doch dann die falsche Adresse? Oder? Ich bin nun kein 
Assembler-Kenner, aber wenn der Assembler auf 0x1c00 zeigt, dann 
passiert das doch auch, oder?

Gibt man also immer Byte-Werte an? Obwohl das im Tutorial anders steht?
Verwirrter bittet um Hilfe.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Florian schrieb:
> Wenn ich in den Bootloader vom Application Flash springen will, wie
> mache ich das richtig?

So wie in der avr-gcc Dokumentation erklärt:

http://gcc.gnu.org/onlinedocs/gcc/AVR-Options.html

Und dann unter "Jumping to non-symbolic addresses":
1
int main (void)
2
{
3
    extern int func_4 (void);
4
5
    /* Call function at byte address 0x4 */
6
    return func_4();
7
}
Und beim Linken -Wl,--defsym,func_4=0x4 wenn func_4 an Byte-Adresse 0x4 
springen soll.

Den Cast einer Wort-Adresse auf Funktionspointer funktioniert nicht für 
alle AVRs.  Beim asm+[r]jmp/call gibst du eine Byte-Adresse an.

von Florian (Gast)


Lesenswert?

Danke.
Darf ich das noch mal in eigenen Worten zusammenfassen, um zu sehen, ob 
ich es verstanden habe?
1
((void (*)(void))0x????)();
Ist grundsätzlich unsauber ("Jumping to non-symbolic addresses like so 
is not supported"). Kann aber klappen. Angeben würde man aber eine 
Word Adresse? So steht es ja im Tutorial. Warum macht dann der Compiler 
daraus keine Byte Adresse (siehe mein Beispiel?) Oder verstehe ich da 
noch was falsch? Oder ist es, weil avr-gcc eben sagt "not supported"?

1
extern int func_4 (void);
Verstehe ich, finde ich aber doch etwas umständlich.

Demnach ist
1
asm("jmp 0x3800");
gar nicht so übel und macht immer genau was ich will, nämlich zu Byte 
Adresse 0x3800 springen.

Warum steht dann das im Tutorial und auf so vielen Webseiten (die ich 
vorher schon durchgelesen habe) mit dem
1
((void (*)(void))0x????)();
oder ähnlichen Schreibweisen wie
1
 void (*bootloader)( void ) = 0x0C00;
wenn's doch gar nicht klappt oder nur vielleicht aber eigentlich not 
supported ist?

von Stefan E. (sternst)


Lesenswert?

Florian schrieb:
> Also sind beide schreibweisen in Byte Notierung doch eigentlich richtig,

Nein, die ldi-icall-Variante ist falsch.

Florian schrieb:
> wird generiert:((void (*)(void))0x1c00)();
>   6c:  e0 e0         ldi  r30, 0x00  ; 0
>   6e:  fc e1         ldi  r31, 0x1C  ; 28
>
> ...
>
> Das ist doch dann die falsche Adresse?

Nein, die richtige.


Bei asm("jmp 0x3800") erfolgt die Umrechnung durch den Assembler.
>  6c:  0c 94 00 1c   jmp  0x3800
Schau dir den Opcode an, welche Adresse steht da? Genau, die 
Word-Adresse 1c00.

von Karl H. (kbuchegg)


Lesenswert?

Florian schrieb:

> Word Adresse? So steht es ja im Tutorial.

In welchem Tutorial?

Du musst hier ein wenig aufpassen. Wenn du damit ein Assembler Tutorial 
meinst, dann musst du dazu sagen, für welchen Assembler das geschrieben 
ist.
Der AVR-Assembler übernimmt den Zahlenwert so, wie du ihn geschrieben 
hast. D.h. dort musst du dich um die Umsetzung Byte-Adresse zu 
Word-Adresse kümmern. D.h. der AVR Assembler arbeitet mit Word-Adressen.

Der Assembler, der mit dem gcc ausgeliefert wird, arbeitet aber mit 
Byte-Adresse und ein Prozessorspezifischer Teil im Assembler passt die 
dan auf die vom AVR verlanten Word-Adressen an. Dies deshalb, weil es 
diesen Assembler in Adaptierungen für viele verschiedene CPU's gibt und 
Byte Adressierung da nun mal der kleinste gemeinsame Nenner darstellt.
Wohingegen der AVR Assembler vom Hersteller der AVR stammt und damit nur 
speziell an diese Prozessorarchitektur angepasst ist.

von Florian (Gast)


Lesenswert?

Karl Heinz schrieb:
> In welchem Tutorial?

siehe oben:
http://www.mikrocontroller.net/articles/AVR_Bootloader_in_C_-_eine_einfache_Anleitung

Stefan Ernst schrieb:
>> Das ist doch dann die falsche Adresse?
>
> Nein, die richtige.
>
> Bei asm("jmp 0x3800") erfolgt die Umrechnung durch den Assembler.
>>  6c:  0c 94 00 1c   jmp  0x3800
> Schau dir den Opcode an, welche Adresse steht da? Genau, die
> Word-Adresse 1c00.

Ah ja. Ich sagte ja: Assembler ist nicht mein Ding. Aber das verstehe 
ich jetzt. Also ist
1
((void (*)(void))0x1c00)();
und
1
asm("jmp 0x3800");
identisch. Aber die erste schreibweise ist trotzdem "not
supported"

Dann stimmt die Aussage im Tutorial ja doch und ist nur nicht präzise 
genug. Und der Inline Assembler Befehl ist die saubere Lösung neben der 
Variante mit dem Linker.
So?

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.