Forum: Compiler & IDEs Inline Assembler


von Simon K. (simon) Benutzerseite


Lesenswert?

Hallo,

Ich versuche folgendes in Inline Assembler zu realisieren:
1
inline void spi_putb(u08 b)        
2
{
3
  SPDR0 = b;
4
  while(!(SPSR0 & (1<<SPIF0)));
5
6
}

Ich habe mir dazu 
http://www.nongnu.org/avr-libc/user-manual/inline_asm.html durchgelesen. 
Aber mir werden sicher einige zustimmen, wenn ich sage, dass es nicht so 
einfach ist, sofort jedes Detail zu verstehen ;)

Ich habe einfach mal per Try&Error folgenden Code geschrieben:
1
inline void spi_putb(u08 b)        
2
{
3
  asm("out %0, %1" "\n\t"
4
      "sbis %2, 7" "\n\t"
5
      "rjmp .-4" "\n\t"
6
      :"=I" (_SFR_IO_ADDR(SPDR0))
7
      :"r" (b),
8
       "I" (_SFR_IO_ADDR(SPSR0)));
9
10
}

Allerdings funktioniert das ganze auch nicht so recht...
Der Compiler gibt mir folgende Fehlermeldungen aus:

../sd.c:55: error: invalid lvalue in asm statement
../sd.c:50: error: output operand constraint lacks `='


Meine Fragen:

1. Wieso ist der lvalue (damit ist doch sicher die Rückgabe von 
_SFR_IO_ADDR gemeint) invalid?
2. Was hat es mit dem Gleichheitszeichen, das in der Fehlermeldung 
angesprochen wird auf sich?
3. Wann muss ich genau Operanden in den Output-Operand Bereich und wann 
in den Input-Operand Bereich schreiben?
Versteh ich das richtig, dass zB "out" einen Output und einen Input 
Operanden hat, "sbis" jedoch zwei Input Operanden?

Bitte Hilfe :-)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Simon Küppers wrote:

> Ich versuche folgendes in Inline Assembler zu realisieren:

Nur aus Neugier: warum eigentlich?  Der Compiler macht aus:
1
#include <stdint.h>
2
#include <avr/io.h>
3
4
static inline void spi_putb(uint8_t b)
5
{
6
  SPDR0 = b;
7
  while(!(SPSR0 & (1<<SPIF0)));
8
9
}
10
11
void doit(void)
12
{
13
        spi_putb(42);
14
}
das hier:
1
doit:
2
        ldi r24,lo8(42)
3
        out 78-0x20,r24
4
.L2:
5
        in __tmp_reg__,77-0x20
6
        sbrs __tmp_reg__,7
7
        rjmp .L2
8
        ret
Ich wüsste nicht, was es da noch einzusparen gäbe. :)

> 1. Wieso ist der lvalue (damit ist doch sicher die Rückgabe von
> _SFR_IO_ADDR gemeint) invalid?

Weil es kein lvalue ist.  Ein output parameter muss Dinge aus dem
inline asm statement ins C-Programm zurückgeben können, dazu benötigt
es ein zuweisbares Objekt (lvalue) im C-Programm.  Ein IO-Register ist
sowas nicht.

Ein IO-Register kann man nur als input parameter übergeben (man
übergibt dessen Wert aus dem C-Programm ins inline asm statement).

> 2. Was hat es mit dem Gleichheitszeichen, das in der Fehlermeldung
> angesprochen wird auf sich?

Das erübrigt sich dann von selbst.

Der clobber ist übrigens auch Quatsch: das bezieht sich nur auf
CPU-Register, nicht auf IO-Register.  Mit einem clobber teilt man ja
dem Compiler lediglich mit, dass er das entsprechende CPU-Register in
dieser Zeit nicht selbst verfügbar hat.  Auf IO-Register (mit Ausnahme
des Stackpointers) greift der Compiler sowieso nicht selbst zu.

von Simon K. (simon) Benutzerseite


Lesenswert?

Hi Jörg,

Die Iteration
1
      sbis %2, 7
2
      rjmp .-4

ist doch wohl kürzer als
1
        in __tmp_reg__,77-0x20
2
        sbrs __tmp_reg__,7
3
        rjmp .L2

oder vertue ich mich da? ;)


>>Weil es kein lvalue ist.  Ein output parameter muss Dinge aus dem
>>inline asm statement ins C-Programm zurückgeben können, dazu benötigt
>>es ein zuweisbares Objekt (lvalue) im C-Programm.  Ein IO-Register ist
>>sowas nicht.

Oha..

>>Ein IO-Register kann man nur als input parameter übergeben (man
>>übergibt dessen Wert aus dem C-Programm ins inline asm statement).

Aha!

>>Der clobber ist übrigens auch Quatsch:....

Was denn für ein Clobber? Ich habe doch nur Input und Output Operands 
angegeben. Für den Clobber übergebe ich doch garkeine Daten. Dafür fehlt 
doch ein ":" am Ende. Oder etwa nicht?!

von Günter R. (galileo14)


Lesenswert?

Hallo, Jörg,

dieses Thema interessiert mich auch sehr. Ich möchte aus einem 
Applikationsprogramm zum Bootloader zurückspringen können und benutze 
dazu derzeit

asm volatile ("jmp 0x1e000"::);

Es gibt noch weitere Eintrittspunkte im Bootloader; diese möchte ich 
alle aber lieber nicht explizit in die Zeile reinschreiben, sondern als 
Defines am Programmanfang angeben, also etwa so:

#define BL_ADDR 0x1e000

asm volatile ("jmp BL_ADDR"::);

Aber so geht es natürlich nicht, weil der Präprozessor innerhalb von 
Strings keine Parameter ersetzt. Also hätte ich gerne geschrieben

asm volatile ("jmp %0" : "???" (BL_ADDR) :);

Aber ich komme nicht drauf, was ich an die Stelle "???" einsetzen müßte; 
das im Thread genannte Asm-Tutorial kenne ich schon (ist Teil des 
avr-libc Reference Manuals), aber in der dortigen Liste der Mnemonics 
(S. 205 in der Version 1.4.3) fehlt "jmp", und auch in der 
Constraint-Liste gibts nichts für Adressen (jedenfalls erkenne ich 
nichts). Kannst Du mir weiterhelfen?

Danke schon mal.

Günter



von Dirk B. (sharandac)


Lesenswert?

Simon Küppers wrote:
> Hi Jörg,
>
> Die Iteration
>
1
>       sbis %2, 7
2
>       rjmp .-4
3
>

irgentwie vermisse ich den zugriff auf das IO bei deiner optimierung :-)

> ist doch wohl kürzer als
>
1
>         in __tmp_reg__,77-0x20
2
>         sbrs __tmp_reg__,7
3
>         rjmp .L2
4
>

MfG Dirk

von Dirk B. (sharandac)


Lesenswert?

äh ... sorry, sehe selber den fehler :-) schnell vergessen

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Simon Küppers wrote:

> Die Iteration
>
1
>       sbis %2, 7
2
>       rjmp .-4
3
>

> ist doch wohl kürzer als

>
1
>         in __tmp_reg__,77-0x20
2
>         sbrs __tmp_reg__,7
3
>         rjmp .L2
4
>

Ja, ist sie.  2 Bytes gespart.  Zeit sparst du eh' nicht, weil's ja
eine Warteschleife ist. ;-)  Wenn nicht gerade diese beiden Bytes
darüber entscheiden, dass man sonst die nächstgrößere ROM-Größe nehmen
muss, würde ich dafür meine Zeit nicht mit dem Inline-Assembler
vertun.

>>>Der clobber ist übrigens auch Quatsch:....

> Was denn für ein Clobber?

Sorry, ich habe die drei Zeilen flasch gelesen.  War ein bisschen
zwischen Tür und Angel heute mittag.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Günter R. wrote:

> #define BL_ADDR 0x1e000

> asm volatile ("jmp BL_ADDR"::);

> Aber so geht es natürlich nicht, weil der Präprozessor innerhalb von
> Strings keine Parameter ersetzt.

Naja, dafür gibt's doch die string concetanation:
1
#define BL_ADDR 0x1e000
2
#define STR(x) STRINGIFY(x)
3
#define STRINGIFY(x) #x
4
5
void foo(void)
6
{
7
  asm volatile ("jmp " STR(BL_ADDR)::);
8
}

  

von Günter R. (galileo14)


Lesenswert?

Hallo, Jörg,

herzlichen Dank! Funktioniert perfekt! So einfach geht das ... aber man 
muß eben wissen, wie. Auf das doppelte String-Makro wäre ich nicht 
gekommen.

Günter

von Simon K. (simon) Benutzerseite


Lesenswert?

>>Ja, ist sie.  2 Bytes gespart.  Zeit sparst du eh' nicht, weil's ja
>>eine Warteschleife ist. ;-)  Wenn nicht gerade diese beiden Bytes
>>darüber entscheiden, dass man sonst die nächstgrößere ROM-Größe nehmen
>>muss, würde ich dafür meine Zeit nicht mit dem Inline-Assembler
>>vertun.

Hehe, habe ich auch gemerkt. Man wartet ja eh.. Aber das war ja eh nur 
ein Beispiel. Von daher..

Vielen Dank!

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.