Hallo zusammen,
ich verzweifle gerade an der Übergabe von const-Pointern an den
Inline-Assembler von AVR-GCC. Mein Problem ist recht einfach: Aus
1
volatileuint8_tRxDHead;
2
volatileuint8_tRxDBuf[RXD_BUF_LEN];
3
4
ISR(FOO_vect){
5
6
registeruint8_tbyteasm("r16");
7
8
// [...] Read byte from port, then:
9
10
RxDBuf[RxDHead]=byte;
11
}
macht GCC so etwas wie:
1
LDS R30,0x00E0
2
LDI R31,0x00
3
SUBI R30,0xA0
4
SBCI R31,0xFF
5
STD Z+0,R16
Das möchte ich gern in Inline-Assembler übernehmen, aber ohne mich auf
die numerischen Adressen und Offsets der Variablen RxDHead und RxDBuf
festzulegen. Statt dessen sollen RxDHead und RxDBuf als Operanden
übergeben werden. Ich erspare euch mal die Auflistung sämtlicher
Varianten, die ich gemäß meiner Lesung des Inline-Assembler-Cookbook
schon ausprobiert habe. Kann mir bitte jemand schnell aushelfen?
Herzlichen Dank!
Matthias
Das vereinfacht vermutlich seinen übrigen Code. Dass man in GCC den
inline Assembler vorzugsweise minimalistisch einsetzt, indem nur der
unmittelbare notwendige Teil in asm realisiert und der Rest dem Compiler
überlassen wird, das erschliesst sich dem Anwender nicht gleich. Zudem
muss man sich dann den nicht trivialen Constraints und
Operandenspezifikationen inniger widmen. Die in der GCC Doku nur
spärlich dokumentiert sind (sowas wie %a0 steht dort m.W. nirgends
drin).
Wer Assemblerprogrammierung gewohnt ist, der mag sich mit dem für GCC
typischen Ansatz, asm und C kreativ zu kombinieren, anfangs etwas schwer
tun, und baut den asm Code lieber als grösseren Block komplett selbst.
Rolf Magnus schrieb:> In der Doku der avr-libc sind sie zumindest recht gut dokumentiert.
Die Constraints ja. Aber die Modifier der Operandenspezifikationen (das
besagte %a0, oder auch %B1) habe ich dort nicht beschrieben gefunden.
Nur vereinzelt und unerklärt in ein paar Beispielen, aber nicht komplett
tabellarisch.
Im RN-Link sieht das besser aus.
A. K. schrieb:> Rolf Magnus schrieb:>>> In der Doku der avr-libc sind sie zumindest recht gut dokumentiert.>> Die Constraints ja. Aber die Modifier der Operandenspezifikationen (das> besagte %a0, oder auch %B1) habe ich dort nicht beschrieben gefunden.
"If operands do not fit into a single register, the compiler will
automatically assign enough registers to hold the entire operand. In the
assembler code you use %A0 to refer to the lowest byte of the first
operand, %A1 to the lowest byte of the second operand and so on. The
next byte of the first operand will be %B0, the next byte %C0 and so
on."
und
"A final problem may arise while using pointer register pairs. If you
define an input operand
"e" (ptr)
and the compiler selects register Z (r30:r31), then
%A0 refers to r30 and
%B0 refers to r31.
But both versions will fail during the assembly stage of the compiler,
if you explicitely need Z, like in
ld r24,Z
If you write
ld r24, %a0
with a lower case a following the percent sign, then the compiler will
create the proper assembler line."
Letzteres hätte vielleicht etwas verständlicher erklärt werden können.
> Nur vereinzelt und unerklärt in ein paar Beispielen, aber nicht komplett> tabellarisch.
Tabellarisch ist es nicht erklärt, aber für eine Tabelle wäre die
benötigte Erklärung auch zu lang.
Rolf Magnus schrieb:> Tabellarisch ist es nicht erklärt, aber für eine Tabelle wäre die> benötigte Erklärung auch zu lang.
Sollte beides sein. Beispielorientiert im Text und summarisch als
Tabelle. Ohne Tabelle finde ich das im konkreten Fall nicht, müsste
jedes Mal den halben Text durchwühlen.
Wär mir ja egal wenn das dort stünde wo es hin gehört, nämlich in der
GCC Doku beim asm Code. Tut es aber nicht, oder nur nicht dort wo ich
immer danach suche. Statt dessen lande ich über kurz oder lang meist in
dessen Quelltext, aber in dessen Kurzkommentar von avr.md steht %a auch
nicht drin.
Tabelle geht sehr wohl:
http://www.rn-wissen.de/index.php/Inline-Assembler_in_avr-gcc
Danke! Gleich mal ausprobieren, wenn ich wieder am Compiler sitze.
A. K. schrieb:> Bliebe die Frage wozu dafür unbedingt Assembler hin muss, denn dieses> Statement wird durch Assembler nicht kürzer.
Es handelt sich bei meiner Frage ja nur um einen kleinen Teil der ISR.
Der erste Teil (auskommentiert) ist sehr timingempfindlich, das setzt
GCC nicht richtig um.
Rolf Magnus schrieb:> Die zweite Frage wäre, warum dieses Byte unbedingt in r16 stehen muß.> Warum nicht dem Compiler die Registerwahl überlassen?
Weil er dann zuviele Register nimmt und diese anfangs erst ewig auf den
Stack pushen muss. Das führt dann an anderer Stelle zu Problemen, siehe
oben.
Gruß,
Matthias
Matthias H. schrieb:> A. K. schrieb:>> Bliebe die Frage wozu dafür unbedingt Assembler hin muss, denn dieses>> Statement wird durch Assembler nicht kürzer.>> Es handelt sich bei meiner Frage ja nur um einen kleinen Teil der ISR.> Der erste Teil (auskommentiert) ist sehr timingempfindlich, das setzt> GCC nicht richtig um.>> Rolf Magnus schrieb:>> Die zweite Frage wäre, warum dieses Byte unbedingt in r16 stehen muß.>> Warum nicht dem Compiler die Registerwahl überlassen?>> Weil er dann zuviele Register nimmt und diese anfangs erst ewig auf den> Stack pushen muss. Das führt dann an anderer Stelle zu Problemen, siehe> oben.
Warum dann nicht gleich die komplette ISR in Assembler, und zwar in
einer separaten S-Datei. Dann musst du dich nicht mit den Eigenheiten
des Inline-Assembler befassen, und du hast die volle Kontrolle, auch
z.B. über den Prolog. Du kannst dann z.B. auch die PUSHs für die
Register, die erst später benutzt werden, weiter nach hinten
verschieben.
Stefan Ernst schrieb:> des Inline-Assembler befassen, und du hast die volle Kontrolle, auch> z.B. über den Prolog.
Hat er ggf. mit dem Attribut "naked" auch.
A. K. schrieb:> Stefan Ernst schrieb:>>> des Inline-Assembler befassen, und du hast die volle Kontrolle, auch>> z.B. über den Prolog.>> Hat er ggf. mit dem Attribut "naked" auch.
Dann kann er aber keinen C-Code mehr benutzen. Und wenn eh die ganze
Funktion in Assembler ist, wozu sich dann mit Inline-Assembler
rumärgern? Naked mit Inline-Assembler macht eigentlich nur bei
Inline-Funktionen richtig Sinn (oder bei extrem simplen Sachen),
ansonsten würde ich immer die separate Assembler-Datei empfehlen.
Matthias H. schrieb:> volatile uint8_t RxDHead;> volatile uint8_t RxDBuf[RXD_BUF_LEN];> RxDBuf[RxDHead] = byte;> Das möchte ich gern in Inline-Assembler übernehmen, aber ohne mich auf> die numerischen Adressen und Offsets der Variablen RxDHead und RxDBuf> festzulegen.> macht GCC so etwas wie:>> LDS R30,0x00E0> LDI R31,0x00> SUBI R30,0xA0> SBCI R31,0xFF> STD Z+0,R16
Nö, macht er nicht ;-)
Das ist ein Disassembly, nicht die Ausgebe des Compilers.
Der verwendet symbolische Adressen und keine absoluten Werte, weil
er die nämlich noch garnicht kennt:
1
lds r24,RxDHead
2
ldi r31,0
3
subi r30,lo8(-(RxDBuf))
4
sbci r31,hi8(-(RxDBuf))
5
st Z,r16
und das kannst du natürlich genauso machen.
> Das möchte ich gern in Inline-Assembler übernehmen
Um was GCC-generiertes in Inline-Assembler zu übernehmen, ist ein
Disassembly ne schlechte Vorlage. Besser ist, die Compiler-Ausgabe
selbst zu verwenden, evtl. nach Hand-Optimierung.
Vielen Dank für alle hilfreichen Antworten und natürlich auch die
Unkenrufe (letztere ja bekanntlich die Butter auf dem Brot des
Mikrocontroller-Forenteilnehmers :-))
Gruß,
Matthias