Forum: Compiler & IDEs AVR-GCC Inline-Assembler: const Pointer / Arrays etc. als Operanden


von Matthias H. (retrode)


Lesenswert?

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
volatile uint8_t RxDHead;
2
volatile uint8_t RxDBuf[RXD_BUF_LEN];
3
4
ISR(FOO_vect) {
5
6
  register uint8_t byte asm("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

von (prx) A. K. (prx)


Lesenswert?

1
  asm("st %a0,%1" : : "e"(&RxDBuf[RxDHead]), "r"(byte));

Bliebe die Frage wozu dafür unbedingt Assembler hin muss, denn dieses 
Statement wird durch Assembler nicht kürzer.

von (prx) A. K. (prx)


Lesenswert?


von Rolf Magnus (Gast)


Lesenswert?

Die zweite Frage wäre, warum dieses Byte unbedingt in r16 stehen muß. 
Warum nicht dem Compiler die Registerwahl überlassen?

von (prx) A. K. (prx)


Lesenswert?

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.

von Rolf Magnus (Gast)


Lesenswert?

In der Doku der avr-libc sind sie zumindest recht gut dokumentiert.

von (prx) A. K. (prx)


Lesenswert?

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.

von Rolf Magnus (Gast)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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

von Matthias H. (retrode)


Lesenswert?

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

von Stefan E. (sternst)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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.

von Stefan E. (sternst)


Lesenswert?

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.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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.

von Matthias H. (retrode)


Lesenswert?

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

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.