Hallo Leute, ich habe die im Betreff gestellte Frage, die ich noch ein bißchen präzisieren möchte. Ich habe mir zu dem (von mir häufig benutzten) inline-assembly wiederholt die Dokumentation von avr-libc durchgelesen. Die Syntax einer inline-assembly-Verwendung schaut ja so aus : asm volatile ( Anweisungen : Outputs : Inputs : Clobbers) So weit so gut. Die Anweisungen enthalten Stringkonstanten (oder eben nur eine), die outputs und inputs bestehen aus Listen von durch Kommas getrennten Ausdrücken - auch klar. Mit der Clobber-Liste verhält sich das genauso. Wenn man nun Eingabe- und Ausgabevariablen in die inputs/outputs einträgt, dann sucht sich der Compiler passende Register (nach der Spezifikation in der input/output-Liste) aus und trägt diese in den Assembler-Code ein. Wenn man den Wert eines Registers ändert, das nicht auf diese Weise in der input/output-Liste steht, dann muss man das dem Compiler mitteilen, damit dieser auf diesen Umstand reagieren kann. Da kommt dann mein Verständnis : Wenn ich ein Register verbiege, dann sage ich dem Compiler welches und er rettet vorher den Inhalt und stellt ihn nach dem inline-assembly wieder her. -> Ist das richtig so weit ? -> Falls das stimmt, gilt das für alle Register, die in der Clobber-Liste stehen oder werden manche Register nicht gerettet, obwohl sie drin stehen ? Mein Problem war folgendes: Bei einer Programmierung, bei der explizit r0 und r1 verwendet werden mussten (hat was mit SPM zu tun), habe ich im inline assembly die beiden Register verwendet und auch in die Clobber-Liste eingetragen. Dass r0 nicht gerettet wird ist ja völlig OK, denn diese kann ja auch unter dem Namen _tmp_reg_ verwendet werden. Warum wird aber der Inhalt von r1 (_zero_reg_) nicht wiederhergestellt, auch wenn man es in die Clobber-Liste schreibt ?! -> Ist das ein Bug oder welchen Sinn hat das ? Falls es ein Bug ist, dann ist er bekannt, denn die Routinen in "boot.h" löschen das Register nach der Verwendung wieder (ist eben das _zero_reg_). In der FAQ zur avr-libc steht, dass man r1 zum merken in eigenen Funktionen usw. verwenden darf, man es aber wiederherstellen muss. Meiner Meinung sollte diese Wiederherstellung erledigt werden, wenn man das Register in die Clobber-Liste aufnimmt, bei den anderen Register, die man nicht ungestraft verclobbern kann ist das ja auch der Fall. MfG, Daniel
> Da kommt dann mein Verständnis : Wenn ich ein Register verbiege, > dann sage ich dem Compiler welches und er rettet vorher den Inhalt > und stellt ihn nach dem inline-assembly wieder her. -> Ist das > richtig so weit ? Im Prinzip ja, wobei er sich normalerweise wohl zuerst einmal Mühe geben wird, das Register gar nicht erst selbst zu benutzen. > -> Falls das stimmt, gilt das für alle Register, die in der > Clobber-Liste stehen oder werden manche Register nicht gerettet, > obwohl sie drin stehen ? Sollte für alle sein, aber du hast ja wohl eine rhetorische Frage gestellt, da du ja das Gegenteil soeben selbst beobachtet hast. > Warum wird aber der Inhalt von r1 (_zero_reg_) nicht > wiederhergestellt, auch wenn man es in die Clobber-Liste schreibt ?! > -> Ist das ein Bug oder welchen Sinn hat das ? Könnte ein Bug sein. Die Sonderbedeutung von r0 und r1 beim AVR-GCC scheint sich in den GCC-Quellen nicht sonderlich gut ,,herumgesprochen'' zu haben. Wir hatten ja neulich auch schon Peter Danneggers Fall, dass der Compiler nicht meckert, wenn man versucht, eins davon via ... asm("r1") an eine Variable zu binden.
Hallo Jörg, danke für Deine Antwort. Ich interpretiere das so, dass es keine dokumentierten Ausnahmen für die Behandlung von Elementen der Clobber-Liste gibt. Eine andere Merkwürdigkeit habe ich noch festgestellt : Wenn man als input den Z-Pointer ("z"(pv)) angibt und anschließend r30/r31 in die Clobber-Liste schreibt, dann erzählt mir der Compiler, dass er r30 und r31 nicht kennt. Lässt man den Z-Pointer als input weg, dann kennt er sie wieder. Ich habe das Gefühl, dass die Clobber-Liste nocht nicht so wirklich ausgereifte Ergebnisse bringt. Eine Kontrollempfehlung für das Assembler-Listing ist auf jeden Fall sinnvoll (hat mich jetzt bald eine Woche Schweiß gekostet ;-)). Ich habe aber viele andere Sachen bei der Fehlersuche gelerent - in so fern ist das fast ein Gewinn. MfG, Daniel.
> Ich interpretiere das so, dass es keine dokumentierten Ausnahmen für > die Behandlung von Elementen der Clobber-Liste gibt. So sehe ich das. > Eine andere Merkwürdigkeit habe ich noch festgestellt : Wenn man als > input den Z-Pointer ("z"(pv)) angibt und anschließend r30/r31 in die > Clobber-Liste schreibt, dann erzählt mir der Compiler, dass er r30 > und r31 nicht kennt. Das halte ich einfach mal für einen Bedienfehler. > Ich habe das Gefühl, dass die Clobber-Liste nocht nicht so wirklich > ausgereifte Ergebnisse bringt. Das ist Quark. Der inline-Assembler dürfte ein eher ausgereiftes und ausgefeiltes Teil sein. Ich habe mir neulich mal den vom IAR ansehen dürfen (der ja ansonsten ein wirklich ausgereifter Compiler ist), der hat vielleicht 10 % Funktionsumfang des GCC-inline-asm. Die Geschichte mit r0/r1 ist sicher ein Problem, dass deren Sonderbedeutung innerhalb des GCC nicht ordentlich definiert worden ist, sodass die architekturunabhängigen Teile des GCC davon mal einfach nichts wissen. Daher wissen sie weder, dass man diese Register nicht mittels ... asm(regname) vergeben darf noch, dass r1 beim Auftauchen in der clobber list auch dann wiederhergestellt werden muss, wenn der Compiler es gerade eigentlich nicht verwendet hat. Siehe meine vorhergehende Antwort: wenn du ein Register in der clobber list angibst, wird sich der Compiler in allererster Linie wohl darum bemühen, das Register gar nicht erst selbst zu verwenden, damit er es eben nicht retten und restaurieren muss. Genau das hat er ja bei dir auch getan: er hat r1 gemieden und musste es daher nicht retten und seiner Meinung nach auch nicht restaurieren. > Eine Kontrollempfehlung für das Assembler-Listing ist auf jeden Fall > sinnvoll Das ist bei inline asm wohl immer der Fall. Man nimmt es eigentlich im eigenen Code nur im größten Ausnahmefall. Es macht den Code unwahrscheinlich schlecht les- und wartbar, kostet einen Haufen Aufwand, bis man es in Sack und Tüten hat. Nur selten lohnt sich das. Meist ist es, wenn schon Assembler, dann effektiver, gleich eine separate Assembler-Quelldatei zu nehmen. Seine wirkliche Stärke und Berechtigung hat der Inline-Assembler für die Implementierer einer C-Umgebung. Viele der Dinge, die in den Headerdateien der avr-libc stecken, wären ohne ihn gar nicht oder nicht so schön elegant zu machen. Dafür wiederum lohnt's aber auch den Aufwand, da es hernach viele Leute nachnutzen können.
Hallo, vielleicht hat einer der Spezialisten einen Tipp für mich: Ich will ein Einzelbit-Fifo bauen, welches a) nicht allzu tief ist, b) variables tief sein soll, c) aus gutem Grund nicht über pointer, sondern per Umkopieren realisiert ist. Hierzu habe ich mir eine inline-asm geschrieben:
1 | unsigned char bit_fifo(volatile uint8_t *buffer, uint8_t length, uint8_t newbit) |
2 | {
|
3 | uint8_t shifted; |
4 | asm volatile ( |
5 | "ror %3" "\n" |
6 | "BIT_FF_LOOP%=:" "\n" |
7 | "ld %0, %a1" "\n" |
8 | "adc %0, %0" "\n" |
9 | "st %a1+, %0" "\n" |
10 | "dec %2" "\n" |
11 | "brne BIT_FF_LOOP%=" "\n" |
12 | : "=&r" (shifted) // outputs |
13 | : "e" (buffer), "r" (length), "r" (newbit) // inputs |
14 | : "memory" // clobbered |
15 | );
|
16 | return(shifted); |
17 | }
|
Innerhalb dieser inline wird der pointer auf buffer verschoben, allerdings geht gcc in der rufenden Routine davon aus, dass er unverändert bleibt und lädt ihn nicht nach. Ich habe schon Attribut "z" auf buffer und R30, R31 in der clobberliste probiert, dann meckert der Compiler, dass er keine Platz im Z-Adressraum hätte. (Gleiches für X und Y). Gibt es eine Möglichkeit, gcc zu sagen, dass *buffer void geworden ist? Servus Wolfgang
Das liegt an deiner Funktionssignatur. Du gibst den Zeiger ja nur in die Funktion hinein und nicht zurück. Du müsstest entweder die Adresse des Zeigers übergeben (dann kann der Gerufene sie ändern), oder aber du gibst einen geänderten Zeiger als Funktionsrückkehrwert zurück. (Das geht hier aber nicht, da du ja schon einen anderen Rückgabewert hast.) Die Benutzung des inline-Assemblers führt hier (ganze Funktion ist in Assembler implementiert) übrigens nur zu unlesbarem Code. Ich würde sie stattdessen in eine separate Assemblerquelldatei schreiben.
Danke, mir schwante schon sowas. Wenn der übergebene Pointer auf buffer selbst als volatile definiert ist, dann geht es. Ich werde es wohl auslagern - ich habe es bisher nicht geschafft, das mit den normalen Mitteln von C so knapp hinzukriegen. Servus Wolfgang (www.opendcc.de)
Du hast buffer als Pointer auf char volatile definiert. Es muss aber ein volatile Pointer auf char (ggf. volatile) sein ;-) EDIT: Ich habe zu lange experimentiert, bist ja inzwischen selbst drauf gekommen ;-)
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.