www.mikrocontroller.net

Forum: Compiler & IDEs Inline Assembler Problem bei Verwendung von Intel Syntax


Autor: Sascha H. (Firma: --) (freeze2046)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

Ich möchte eine C-Funktion die einen reinen Assembler-Body besitzt so 
übersetzen, dass Sie mit GCC kompiliert werden kann. Mein Problem dabei 
ist, dass GCC by default AT&T-Syntax für den Inline-Assembler verwendet. 
Da ich weder Zeit noch Lust dazu habe mir die AT&T Syntax anzueignen 
habe ich folgendes versucht:

1. Den Compiler-Parameter -masm=intel zu werden (außer merkwürdigen 
Compiler-Errors hat das nichts gebracht)
2. Den Code so zu übersetzen

// MSVC 9.0

char* UpperCase(
  char* Src,
  char* Dest
)
{
  __asm
  {
    push esi
    push edi

    mov  esi, BYTE PTR [ebp+8]    ; Src
    lea  edi, BYTE PTR [ebp+12]    ; Dest
    
nextchar:
    lodsb              ; load byte from ESI to al 
    cmp   al, 0x61          
    jb   done            ; below 'a' ?
    cmp  al, 0x7A  
    ja   done            ; above 'z' ?
    xor  al, 0x20          ; set bit 6

done:
    stosb
    cmp   [esi], 0  
    je   end
    loop nextchar
  
end:
        mov eax, [ebp+12]
    pop  edi
    pop  esi
  
  }
}


// GCC 

char* UpperCase(
  char* Src,
  char* Dest
)
{
  asm (".intel_syntax noprefix\n");
  
  asm ("push esi\n");
  asm ("push edi\n");
  
  asm ("mov esi, BYTE PTR [ebp+8]\n");
  asm ("mov edi, BYTE PTR [ebp+12]\n");
  
  asm ("nextchar:\n");
  asm ("lodsb\n");
  asm ("cmp al, 0x61\n");
  asm ("jb done\n");
  asm ("cmp al, 0x7A\n");
  asm ("ja done\n");
  asm ("xor al, 0x20\n");
  
  asm ("done:\n");
  asm ("stosb\n");
  asm ("cmp [esi], 0\n");
  asm ("je end\n");
  asm ("loop nextchar\n");
  
  asm ("end:\n");
  asm ("pop edi\n");
  asm ("pop esi\n");
  asm ("mov eax, [ebp+12]\n");
  
  asm (".att_syntax noprefix");
}


Leider bekomme ich nun immer die Fehlermeldungen:
Error: '%esi' not allowed with 'movb'
Error: '%edi' not allowed with 'movb'
Warning: using '%al' instead of '%eax'due to 'b' suffix
Error: '%esi' not allowed with 'movb'
Error: '%edi' not allowed with 'movb'
Error: symbol 'nextchar' is already defined
Error: symbol 'done' is already defined
Error: symbol 'end' is already defined
Warning: using '%al' instead of '%eax'due to 'b' suffix


... die ich zwar allesamt nachvollziehen könnte, aber nur WENN ich denn 
die entsprechenden Instruktionen verwenden WÜRDE.
Ich zähle auf eure Unterstützung ;-) Irgendjemand arbeitet doch bestimmt 
mit GCC^^.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ganz ehrlich,
Den 3-Zeiler hast du in C schneller selbst geschrieben als du übersetzen 
kannst.
char* UpperCase( const char* Src, char* Dest )
{
  char* tmp = Dest;

  while( *Src ) {
    if( *Src < 'a' || *Src > 'z' )
      *Dest++ = *Src++;
    else
      *Dest++ = *Src++ | 0x20;
  }

  return tmp;
}

Für sowas Assembler einzusetzen, sollte mit Peitschenhieben bestraft 
werden.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Noch besser wäre allerdings
char* UpperCase( const char* Src, char* Dest )
{
  char* tmp = Dest;
  char c;

  while((c = *Src++ ))
    *Dest++ = toupper(c);

  return tmp;
}

Autor: Sascha H. (Firma: --) (freeze2046)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dass liegt daran, dass der Code ursprünglich von einem reinen 
Assembler-Programm kommt. Ich habe ihn eigentlich auch hier nur 
exemplarisch gepostet, da ich generell ein Problem mit dem Inline 
Assembler von GCC habe. Es geht hier also weniger um den Inhalt als ums 
Prinzip.


Ich möchte einfach Intel-Syntax anstatt von AT&T verwenden können, was 
mir jedoch weder mit Compilerschalter noch mit der Variante hier 
gelingt. Was mache ich falsch?

btw. mir stehen keien Std-Funktionen zur Verfügung.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sascha H. schrieb:

> Ich möchte einfach Intel-Syntax anstatt von AT&T verwenden können, ...

Dann bist du beim GCC einfach falsch, der kommt einfach aus der Unix-
Ecke und damit von AT&T (*).  Übrigens übergibt man GCC's inline-
Assesmbler die Parameter ordentlich, statt implizite Annahmen zu machen.
Nur dann kann der Compiler sicherstellen, dass seine eigene
Registerbelegung nicht mit dem inline-Assembler-Code kollidiert.

(*) Von der Syntax des Assemblers her, natürlich nicht von der
Autorschaft des Codes.

Autor: Sascha H. (Firma: --) (freeze2046)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg Wunsch schrieb:
> Sascha H. schrieb:
>
>> Ich möchte einfach Intel-Syntax anstatt von AT&T verwenden können, ...
>
> Dann bist du beim GCC einfach falsch, der kommt einfach aus der Unix-
> Ecke und damit von AT&T (*).  Übrigens übergibt man GCC's inline-
> Assesmbler die Parameter ordentlich, statt implizite Annahmen zu machen.
> Nur dann kann der Compiler sicherstellen, dass seine eigene
> Registerbelegung nicht mit dem inline-Assembler-Code kollidiert.
>
> (*) Von der Syntax des Assemblers her, natürlich nicht von der
> Autorschaft des Codes.


Mein Problem ist, dass ich GCC benutzen MUSS - nicht in meiner 
Entscheidungsgewalt. Und zudem bietet GCC doch offensichtlich gleich 
ZWEI Möglichkeiten Intel-Syntax zu benutzen; also unterstelle ich mal, 
dass das auch irgendwie gehen muss oder nicht? Zudem würde ich ihm die 
Parameter ja gerne explizit übergeben, allerdings hat dass zu noch mehr 
Compilerfehlern geführt, da er die entsprechenden Parameter nicht finden 
konnte.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn du sowieso komplette Funktionen in Assembler programmierst,
warum muss es dann unbedingt der Inline-Assembler sein?  Dafür ist
er weder praktikabel noch notwendig, du kannst die Dateien dann
auch gleich separat assemblieren lassen.  Dann hast du vielleicht
auch bessere Chancen mit der entsprechenden Assembler-Option.
Allerdings habe ich ein wenig meine Zweifel, dass die GNU-Assembler-
Implementierung der Intel-Syntax wirklich bugkompatibel mit andere
Intel-Syntax-Assemblern ist (aber ich habe schon recht lange nichts
mehr mit i386-Assembler gemacht, um ehrlich zu sein).

Autor: Sascha H. (Firma: --) (freeze2046)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die Funktionen direkt in assembler zu programmieren, wäre sicherlich die 
korrekteste Version. Ich weiß allerdings nicht, wie ich mein C-Programm 
dann mit dem ASM-Code in Verbindung bringe. Ich weiß lediglich wie ich 
von assembler C-Funktionen aufrufen kann; umgekehrt muss ich jedoch 
passen.
btw. benutze ich zum asm-programmieren NASM

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sascha H. schrieb:

Einmal mische ich mich noch ein

> Die Funktionen direkt in assembler zu programmieren, wäre sicherlich die
> korrekteste Version.

Die korrekteste Lösung wäre, alles in C zu schreiben. Schliesslich 
benutzt du ja den C-Compiler.

> btw. mir stehen keien Std-Funktionen zur Verfügung.
Das ist aber nun wirklich kein Hindernis :-)
50% aller Standardfunktionen sind extrem einfach zu implementieren. 
Lediglich alles was mit malloc/free bzw. fopen/fclose zu tun hat, ist 
aufwändiger. Und natürlich eine Standardkonform stream-Library (stdin, 
stdout, printf, ...)

Gott sei Dank kommt das alles auf den hier üblichen µC so gut wie nie 
vor, so dass man sagen kann: 95% aller auf einem µC gebräuchlichen 
Standardfunktionen sind extrem einfach zu implementieren.

Autor: Sascha H. (Firma: --) (freeze2046)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:
> Die korrekteste Lösung wäre, alles in C zu schreiben. Schliesslich
> benutzt du ja den C-Compiler.

Ich muss auch auf Ports zu greifen, daher benötige ich zumindest den 
Inline Assembler.

Wenn schon keine Intel-Syntax mit GCC möglich ist, wäre es wirklich 
freundlich, wenn mir jemand helfen kann, reinen Assembler-Code mit 
C-Code "zu verbinden".


Karl heinz Buchegger schrieb:
>
> char* UpperCase( const char* Src, char* Dest )
> {
>   char* tmp = Dest;
> 
>   while( *Src ) {
>     if( *Src < 'a' || *Src > 'z' )
>       *Dest++ = *Src++;
>     else
>       *Dest++ = *Src++ | 0x20;
>   }
> 
>   return tmp;
> }
> 

btw. funktioniert das so natürlich nicht. Muss schon ein xor sein ;-)

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sascha H. schrieb:

> Ich muss auch auf Ports zu greifen, daher benötige ich zumindest den
> Inline Assembler.

Wofür du nur Inline-Funktionen mit genau und nur diesen Befehlen als 
Inline-ASMs benötigst. Wenn sowas nicht schon fertig irgendwo in den 
Includes rumliegt, dann sollte sich das leicht realisieren oder auch 
finden lassen.

Allerdings ist direkter Portzugriff mit IN/OUT-Befehlen mittlerweile 
ausserhalb von Kernels und Device-Drivern etwas selten geworden.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sascha H. schrieb:

> btw. funktioniert das so natürlich nicht. Muss schon ein xor sein ;-)

LOL.
Aber auch das XOR ist eigentlich nicht das Richtige.
(Nochmal in den ASCII Code gelinst)
Eigentlich sollte das ein AND mit ~0x20 sein, damit auch sicher gelöscht 
wird und nicht getoggelt. Klar, das vorhergehende if stellt die Funktion 
in dem Fall sicher :-)

Aber wozu gibst denn die toupper() :-)
Die kennt solche Details, so dass ich mir sie nicht merken muss.

Autor: Sascha H. (Firma: --) (freeze2046)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nunja, die Portzugriff-Routinen habe ich schon hinbekommen, so ist es ja 
nicht ;-)

A. K. schrieb:
> Allerdings ist direkter Portzugriff mit IN/OUT-Befehlen mittlerweile
> ausserhalb von Kernels und Device-Drivern etwas selten geworden.

In meinem Fall ist es auch nicht außerhalb ...
Wie gesagt, ihr habt mich ja überredet; ich geb's mit dem Inline 
Assembler von GCC auf. Entweder wird jetzt (fast) alles C; wobei mich 
weiterhin die Kombi von reinem Asm und C interessiert.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sascha H. schrieb:

> Nunja, die Portzugriff-Routinen habe ich schon hinbekommen, so ist es ja
> nicht ;-)

Wären ansonsten in Linux unter /usr/include/asm/io.h zu finden. Die sind 
grad so effizient wie Assembler pur.

Autor: Sascha H. (Firma: --) (freeze2046)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:
>Klar, das vorhergehende if stellt die Funktion

eben ;-)


A. K. schrieb:
> Wenn die mit "static inline" verziert sind, dann sind die so effizient
> wie Assembler pur.

Warum static? Steht doch nur für die Sichtbarkeit (bei 
Funktionsdeklarationen).



Merkwürdigerweise haut es ihn bei dem Aufruf von UpperCase raus ...
char* UpperCase(
  char* Src,
  char* Dest
)
{
  char* ret = Dest;  
  while( *Src ) 
  {
    if( *Src < 0x61  ||  *Src > 0x7A ) 
      *Dest++ = *Src++;
    else
      *Dest++ = *Src++ ^ 0x20;
  }  
  return ret;
}

[...]

char* cstr = "Dies ist ein Text";
char  buf[64] = {0};

UpperCase(cstr, buf);        // booooooooom!

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sascha H. schrieb:

> Warum static? Steht doch nur für die Sichtbarkeit (bei
> Funktionsdeklarationen).

Eben.

Wenn du die im .h File stehen hast und dieses an mehr als einer Stelle 
includest, dann weisst du sehr schnell weshalb ;-).

Wenn du sie ohne "static" in ein eigenes .c File packst, dann sind sie 
naturgemäss nicht mehr inline, also ein Funktionsaufruf und 
dementsprechend ineffizient.

Und wenn du sie vorneweg in das dich interessierende File reinstellst, 
dann wird der Code dafür ohne "static" auch dann generiert, wenn sie 
dort ausschliesslich inlined benutzt werden, weil der Compiler nicht 
weiss, dass sie niemand sonst braucht.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sascha H. schrieb:

> UpperCase(cstr, buf);        // booooooooom!

Wie äussert sich das?

Jedenfalls liegt das nicht an der Funktion. In welchem Kontext läuft das 
überhaupt? Device-Driver, eigenes Betriebssystem, Mini-PC als 
Luxus-Controller ohne Betriebssystem, ...?

Irgendwelche "unwichtigen" Warnungen ignoriert, oder vorsorglich gar 
nicht erst eingeschaltet (-Wall empfohlen)?

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Du solltest den Code dahingehend ändern, dass auch die Null mit kopiert 
wird.

Autor: Helmut Lenzen (helmi1)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das hier :

> asm ("mov esi, BYTE PTR [ebp+8]\n");

kann so nicht gehen

Daher ist diese Fehlermeldung schon in Ordnung

>Error: '%esi' not allowed with 'movb'

Du versuchst in einem 32 Bit Register ein Byte zuladen
das kann nicht gehen.

das laedt 32 Bit

asm ("mov esi, DWORD PTR [ebp+8]\n");

und das 16 Bit

asm ("mov si, WORD PTR [ebp+8]\n");

8 Bit kann man nicht in (E)SI oder (E)DI laden

Gruss Helmi

Autor: Sascha H. (Firma: --) (freeze2046)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
A. K. schrieb:
> Wenn du die im .h File stehen hast und dieses an mehr als einer Stelle
> includest, dann weisst du sehr schnell weshalb ;-).
>
> Wenn du sie ohne "static" in ein eigenes .c File packst, dann sind sie
> naturgemäss nicht mehr inline, also ein Funktionsaufruf und
> dementsprechend ineffizient.
>
> Und wenn du sie vorneweg in das dich interessierende File reinstellst,
> dann wird der Code dafür ohne "static" auch dann generiert, wenn sie
> dort ausschliesslich inlined benutzt werden, weil der Compiler nicht
> weiss, dass sie niemand sonst braucht.


Sry, aber das verstehe ich nicht.
1. Ist für mich static lediglich ein Sichtbarkeitsspezifizierer der 
angibt, dass die entsprechende Funktion nur in dem einen File sichtbar 
ist.
2. Sorgt das Keyword "inline" doch dafür, dass der Code bei aufrufen an 
die Stelle kopiert wird oder nicht?
3. Bekomme ich die Fehlermeldung "expected '(' to follow inline", wenn 
ich es als C-Code kompiliere (C++ funktioniert es).

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sascha H. schrieb:

> 1. Ist für mich static lediglich ein Sichtbarkeitsspezifizierer der
> angibt, dass die entsprechende Funktion nur in dem einen File sichtbar
> ist.

Richtig.

> 2. Sorgt das Keyword "inline" doch dafür, dass der Code bei aufrufen an
> die Stelle kopiert wird oder nicht?

Kommt drauf an.

In my_io.h
   extern inline unsigned char inb(unsigned);
In my_io.c
   inline unsigned char inb(unsigned) { ... }
In my_file.c:
   #include <my_io.h>
   ... = inb(0xAA);
wird vermutlich funktionieren, aber garantiert nicht inlined, weil der 
Compiler keine Glaskugel hat.

> 3. Bekomme ich die Fehlermeldung "expected '(' to follow inline", wenn
> ich es als C-Code kompiliere (C++ funktioniert es).

Evtl. reinen ANSI-Modus eingestellt, oder sowas. Nimm __inline.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sascha H. schrieb:

> 1. Ist für mich static lediglich ein Sichtbarkeitsspezifizierer der
> angibt, dass die entsprechende Funktion nur in dem einen File sichtbar
> ist.

Yep.
Daraus folgt aber auch, dass der Compiler damit alle Stellen kennt an 
denen die Funktion verwendet werden kann.
Ausserhalb dieses Files kann es daher keine Verwendung geben.
-> Der Compiler muss die Funktion gar nicht erzeugen, wenn er sie 
überall inlined

> 2. Sorgt das Keyword "inline" doch dafür, dass der Code bei aufrufen an
> die Stelle kopiert wird oder nicht?

inline ist letztendlich nur ein Vorschlag für den C-Compiler. Ähnlich 
wie 'register'

Autor: Sascha H. (Firma: --) (freeze2046)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Helmut Lenzen schrieb:
> Du versuchst in einem 32 Bit Register ein Byte zuladen
> das kann nicht gehen.

Warum nicht? Andersrum wäre es doch eher verwerflich.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stefan Ernst schrieb:
> Du solltest den Code dahingehend ändern, dass auch die Null mit kopiert
> wird.

Mist.
Sah gleich irgendwie so komisch aus.
Die toupper Lösung hat das gleiche Problem.

Sorry.

Autor: Hc Zimmerer (mizch)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> [static]
> Ausserhalb dieses Files kann es daher keine Verwendung geben.
> -> Der Compiler muss die Funktion gar nicht erzeugen, wenn er sie
> überall inlined

Und da der Compiler alle aufrufenden Stellen kennt, muss er sich nicht 
an die sonst gültigen Übergaberegeln halten.  Er muss also im Aufrufer 
nicht eine lokale Variable in einen Standard-Übergabeplatz transferieren 
und in der aufgerufenen Funktion von dort aus wieder an einen 
passenderen Platz.  Dito am Ende der Funktion.

Autor: Sascha H. (Firma: --) (freeze2046)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
char* UpperCase(
  char* Src,
  char* Dest
)
{
  char* ret = Dest;  
  while( *Src ) 
  {
    if( *Src < 0x61  ||  *Src > 0x7A ) 
      *Dest++ = *Src++;
    else
      *Dest++ = *Src++ ^ 0x20;
  }  
  *Dest = 0;
  return ret;
}

static inline wäre dann geklärt. Danke.

Autor: Helmut Lenzen (helmi1)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>> Du versuchst in einem 32 Bit Register ein Byte zuladen
>> das kann nicht gehen.

>Warum nicht? Andersrum wäre es doch eher verwerflich.

Das kann die Prozessorhardware aber nicht.
Da wird nix gecastet wie in 'C'

Ein Byte kann man nur in die Register AL,AH,BL,BH,CL,CH,DL,DH laden

SI,DI koennen nur mit 16 Bit und EDI,ESI nur mit 32 Bit geladen werden

willst du ein Byte in ESI laden geht das nur ueber den Umweg ueber ein 
anders Register

also

xor eax,eax                 ; Akku loeschen
mov al,byte ptr[[ebp+8]     ; die unteren 8 Bit laden
mov esi,eax                 ; in esi kopieren

Autor: Sascha H. (Firma: --) (freeze2046)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Bzgl. PTR ... ist natürlich richtig. Schande über mich.

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.