Somit ließe sich erklären warum sich mein Programm doch recht
"merkwürdig" verhält. r17 Muss laut avr libc doku bei einem
Funktionsaufruf gesichert werden, r0 jedoch nicht.
Bug beim GCC? oder ein Feature, oder habe ich die falsche Syntax
verwendet?
An feste Register gebundene Variablen werden nur auf globaler Ebene
unterstützt. Für lokale Variablen kannst du dem Registerallokator
nicht vorschreiben, wohin er sie legen soll.
naked functions are evil.
naked functions are evil.
naked functions are...
Schreib die komplette Funktion in eine Assemblerdatei. Das
naked-Attribut hat wirklich nur Sinn für Dinge wie die
nicht-wirklich-Funktionen für die .initN sections.
r0 ist _tmp_reg_, r1 ist _zero_reg_.
Die Register ab r8 sind auch nur dann für fest gebundene Variablen
benutzbar, wenn man keine Funktionen mit vielen/großen Parametern
in der Applikation hat, ansonsten werden sie für die Parameterübergabe
mit genutzt. In den Mini-Applikationen, in denen man typischerweise
versuchen wird, viele Variablen an Register zu binden, dürfte das
aber wohl eher eine untergeordnete Rolle spielen.
Jörg Wunsch wrote:
> An feste Register gebundene Variablen werden nur auf globaler Ebene> unterstützt. Für lokale Variablen kannst du dem Registerallokator> nicht vorschreiben, wohin er sie legen soll.
Danke, das wusste ich nicht.
> naked functions are evil.
Ich sichere direkt alle Register, die die avr-libc als "Call-saved
registers" nennt. Solange die Funktion nicht auch noch extra Speicher
auf dem Stack benötigt sehe ich deswegen kein Problem. Ich könnte auch
auf das Naked verzichten, aber dann wüsste ich nicht welche Register der
Compiler für mich sichert und welche mir noch fehlen. Wenn ich dann
allerdings weiterhin alle sicher würde, würde die Funktion unnötig
Flash, Rechnenzeit und SRAM benötigen.
> Schreib die komplette Funktion in eine Assemblerdatei.
Dazu fehlen mir noch die nötigen Kentnisse, lassen sich aus
Assemblerdateinen überhaupt in Header Dateien per #define definerten
Werte und gewönliche Arrays (bzw dessen Startadresse) zugreifen?
Außerdem wollte ich soweit wie möglich alles in C lösen.
Als simplen Workaround habe ich die Variablen Definitionen einfach
global definiert und damit läuft mein Programm jetzt auch mit dem gcc
4.1 :-)
Wahrscheinlich werde ich noch die globalen Variablen weglassen und das
Einlesen statt durch
sreg = SREG;
mit
asm volatile ("in r0, %0" : : "I" (_SFR_IO_ADDR(SREG)): "r0");
erledigen.
>> naked functions are evil.> Ich sichere direkt alle Register, die die avr-libc als "Call-saved> registers" nennt. Solange die Funktion nicht auch noch extra Speicher>auf dem Stack benötigt sehe ich deswegen kein Problem. Ich könnte auch> auf das Naked verzichten, aber dann wüsste ich nicht welche Register> der Compiler für mich sichert und welche mir noch fehlen.
Warum mußt du denn überhaupt irgendwelche Register manuell sichern?
> Dazu fehlen mir noch die nötigen Kentnisse, lassen sich aus> Assemblerdateinen überhaupt in Header Dateien per #define definerten> Werte und gewönliche Arrays (bzw dessen Startadresse) zugreifen?
Kommt auf den Header an, aber im Prinzip ja. gcc benutzt für Assembler
denselben Präprozessor wie für C, also kann er auch #defines auflösen,
sofern sie nicht durch was C-spezifisches ersetzt werden. Dort, wo es
für C und Assembler unterschiedlich ist, könnte man im Header auch eine
Fallunterscheidung einbauen. Und externe Symbole können im Assembler
natürlich auch genutzt werden. Schließlich macht auch der Compiler aus
dem C-Code erstmal Assembler-Code.
> Außerdem wollte ich soweit wie möglich alles in C lösen.
Dann drängt sich umso mehr die Frage auf, warum du dann überhaupt
irgendwelche Register sichern willst.
> Wahrscheinlich werde ich noch die globalen Variablen weglassen und das> Einlesen statt durch> sreg = SREG;> mit> asm volatile ("in r0, %0" : : "I" (_SFR_IO_ADDR(SREG)): "r0");> erledigen.
Warum muß es unbedingt r0 sein? Gerade von r0 sollte man die Finger
lassen, denn es ist ja wie schon erwähnt im Compiler bereits mehrfach
belegt. Ich würde dem Compiler die Registerverwaltung überlassen, etwa
so:
uint8_t sreg;
asm volatile ("in %0, %1" : "=r" (sreg): "I" (_SFR_IO_ADDR(SREG)));
Rolf Magnus wrote:
> Warum mußt du denn überhaupt irgendwelche Register manuell sichern?
Bei dem Programm Teil handelt es sich um einen kooperativen Scheduler.
Es werden also alle Register bei der sich die libc auf ein Beibehalten
des Inhalts bei einem Funktionsaufruf verlässt gesichert, der SP auf den
Stack eines anderen Task gesetzt und für diesen alle Register wieder
hergestellt. Das Ganze funktioniert auch inzwischen wunderbar.
> Kommt auf den Header an, aber im Prinzip ja. gcc benutzt für Assembler> denselben Präprozessor wie für C, also kann er auch #defines auflösen,> sofern sie nicht durch was C-spezifisches ersetzt werden. Dort, wo es> für C und Assembler unterschiedlich ist, könnte man im Header auch eine> Fallunterscheidung einbauen. Und externe Symbole können im Assembler> natürlich auch genutzt werden. Schließlich macht auch der Compiler aus> dem C-Code erstmal Assembler-Code.
Gut zu wissen.
> Warum muß es unbedingt r0 sein? Gerade von r0 sollte man die Finger> lassen, denn es ist ja wie schon erwähnt im Compiler bereits mehrfach> belegt.
Es muss nicht r0 sein, sondern einfach eins welches
http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_reg_usage
nicht als "Call-saved registers" aufführt. r0 habe ich genommen weil
dort steht "may be used to remember something for a while within one
piece of assembler code" und ich denke einen Wert einlesen und im
nächsten Schritt sichern fällt darunter. Danach brauche ich den Inhalt
nicht mehr.
Malte __ wrote:
>> Warum mußt du denn überhaupt irgendwelche Register manuell sichern?> Bei dem Programm Teil handelt es sich um einen kooperativen Scheduler.
Das ist aber nun wirklich ein typischer Kandidad dafür, dass man das
Stück komplett in eine Assemblerdatei schreibt.
Oder eine naked-Funktion in C, die dann aber den ganzen Code als
Inline-Assembler enthält, was aber letztendlich dann auch keinen Vorteil
mehr gegenüber einer reinen Assembler-Datei hat. Aber den Compiler
seinen Teil erledigen lassen und dann irgendwie in Inline-Assembler den
Rest dranflanschen ist eher keine gute Idee.