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
1
// MSVC 9.0
2
3
char*UpperCase(
4
char*Src,
5
char*Dest
6
)
7
{
8
__asm
9
{
1
push esi
2
push edi
3
4
mov esi, BYTE PTR [ebp+8] ; Src
5
lea edi, BYTE PTR [ebp+12] ; Dest
6
7
nextchar:
8
lodsb ; load byte from ESI to al
9
cmp al, 0x61
10
jb done ; below 'a' ?
11
cmp al, 0x7A
12
ja done ; above 'z' ?
13
xor al, 0x20 ; set bit 6
14
15
done:
16
stosb
17
cmp [esi], 0
18
je end
19
loop nextchar
20
21
end:
22
mov eax, [ebp+12]
23
pop edi
24
pop esi
1
2
}
3
}
4
5
6
// GCC
7
8
char*UpperCase(
9
char*Src,
10
char*Dest
11
)
12
{
13
asm(".intel_syntax noprefix\n");
14
15
asm("push esi\n");
16
asm("push edi\n");
17
18
asm("mov esi, BYTE PTR [ebp+8]\n");
19
asm("mov edi, BYTE PTR [ebp+12]\n");
20
21
asm("nextchar:\n");
22
asm("lodsb\n");
23
asm("cmp al, 0x61\n");
24
asm("jb done\n");
25
asm("cmp al, 0x7A\n");
26
asm("ja done\n");
27
asm("xor al, 0x20\n");
28
29
asm("done:\n");
30
asm("stosb\n");
31
asm("cmp [esi], 0\n");
32
asm("je end\n");
33
asm("loop nextchar\n");
34
35
asm("end:\n");
36
asm("pop edi\n");
37
asm("pop esi\n");
38
asm("mov eax, [ebp+12]\n");
39
40
asm(".att_syntax noprefix");
41
}
Leider bekomme ich nun immer die Fehlermeldungen:
1
Error: '%esi' not allowed with 'movb'
2
Error: '%edi' not allowed with 'movb'
3
Warning: using '%al' instead of '%eax'due to 'b' suffix
4
Error: '%esi' not allowed with 'movb'
5
Error: '%edi' not allowed with 'movb'
6
Error: symbol 'nextchar' is already defined
7
Error: symbol 'done' is already defined
8
Error: symbol 'end' is already defined
9
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^^.
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.
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.
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.
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).
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
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.
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:
>
1
>char*UpperCase(constchar*Src,char*Dest)
2
>{
3
>char*tmp=Dest;
4
>
5
>while(*Src){
6
>if(*Src<'a'||*Src>'z')
7
>*Dest++=*Src++;
8
>else
9
>*Dest++=*Src++|0x20;
10
>}
11
>
12
>returntmp;
13
>}
14
>
btw. funktioniert das so natürlich nicht. Muss schon ein xor sein ;-)
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.
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.
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.
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.
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 ...
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.
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)?
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
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).
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.
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'
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.
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.
> [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.
>> 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