Hallo, liebe Gemeinde!
Ich möchte diesen thread Nutzen, um den gccasm möglichst gut zu
verstehen. Dabei geht es erst mal nur um das Interesse, später am eh
esten um asm files, die in c-Projekte eingefügt werden. (wenn ich recht
verstehe ist es ein Unterschied, ob ich asm Funktionen aus einem
c-Programm aufrufe oder umgekehrt c generierte Funktionen in einem asm
Projekt; s.u. Register sichern)
Zu mir, ich ein ziemlicher Frischling, habe vor einem halben Jahr mit
dem avr tutorial angefangen und dann das c tutorial angefangen. Von
daher bin ich vielleicht naiv oder fehlinterpretationen erlegen - ich
bitte das zu entschuldigen. Es ist alles nur Hobby! Kein Grund, mich
selber unter Druck zu setzen bzw. andersrum kein grund geflamt zu werden
wegen blöder Fragen...
Also los. Eins nach dem anderen. Wie gesagt es geht darum, wenn
überhaupt, *.S Dateien mit asm Funktionen in einem c-Projekt zu
verwenden.
verstehe ich
http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_reg_usage schon
mal richtig, daß ich dann in der asm Funktion die Register R2-R17 und
das y-Register sichern muss, wenn diese eins davon verändert und
andersrum R18-R27 und Z-Register GANZ beliebig einfach verwendet werden
kann? Ganz beliebig, weil der c Compiler vor einem asm Aufruf weiß, daß
er eins davon verwendet und deshalb selber, VOR dem Aufruf der asm
Funktion diese auf den Stack pusht und auch wieder zurückspeichert?
Falls das stimmt: wenn ich eine isr in Assembler schreiben möchte, muss
ich alle benutzen register selber sichern und zurückschreiben - gcc weiß
ja nicht, wann der Interrupt auslöst....
Dann würde c ja quasi alle Pointer Register sichern, denn ich würde
erwarten, daß die sozusagen immer benutzt werden?
Eine Konkrete Frage: Wenn ich eine Funktion wie void funk(void* ptr) in
asm schreibe, dann wird der pointer als erstes Argument in Register
R24/R25 übergeben? Der muss dann direkt in ein Pointer Register x oder z
(?) kompiert werden? Diese müsste ich aber nicht selber sichern, weil c
das von sich aus täte, wenn die belegt sind..... Wenn das stimmt hat man
schon das sichern und kopieren des Pointers als zusätzliche zeit
verglichen mit einem reinem Assemblerprogramm, wo ich geziehlt schon ein
pointer register laden kann und nciht erst kopieren muss.
Eine letzte Frage ist das y-register, welches ich selber sichern muss zu
beginn der asm Funktion. Das nennt sich "Framing Pointer". Was ist
darunter zu verstehen und was mache ich damit? Ich verstehe die Funktion
darin, daß es eine Kopie des Stackpointers ist wenn die Funktion
startet. Was ich damit mache ist mir nur nicht klar.... :-(
Also jegliche Hilfe, Kommentare, beispiele sind mir sehr willkommen!
Flames nicht, denn dafür fühle ich mich nicht sicher genug.
Weitere Fragen würd ich hier drinnen posten. Kann aber dauern, denn wie
gesagt ich bewege mich beruflich ganz woanders und beschäftige mich aus
Interesse und Lust an der Freud damit. Der Ehrgeiz treibt mich aber so
weit, genauer verstehen zu wollen, wie das alles Funktioniert und was
der c compiler genau macht. :-(
Ich bin gespannt! schöne Grüße, Micha!
Schande über mich, Doppelpost!
habe vergessen zu Fragen, was es mit Zugriff auf Register auf sich hat.
Es wird wiederholt erwähnt, daß ein Offset von 20hex nötig ist, wenn in
einer asm Datei ein Register verwendet wird.
Ich bin bei meinem Studium von google auf ein #define
Ich_hab_es_leiser_vergessen gestoßen, was nötig ist für dieses Offset.
Da fehlt mir Wissen, ich kann den Link aber auch nicht mehr finden -
Hilfe erbeten!
Dein Fragen sind nicht blöd, jedenfalls nicht blöder als andere ;-)
Die FAQ lese ich genauso wie du es tust.
Die Überlegung bei der ISR ist auch richtig. Was der GCC bei ISRs in C
sichert kannst du dir leicht ansehen, indem du dir ein
Disassemblerlisting erzeugen lässt bzw. im AVR Studio disassemblierst.
Bei der funk() und den Übergabeparametern hast du grundsätzlich Recht.
Das Umkopieren benötigt Zusatzinstruktionen. Du musst abwägen, ob deine
ASM-Routine diesen Aufwand lohnt. Allerdings kannst du auch in C
veranlassen, dass die Umkopiererei entfällt. Wenn du deine ASM-Routine
in Inline-Assembler statt als eigene Funktion schreibst, kannst du den
Pointer aus der aufrufenden Routine direkt in die Zielregister laden
(nachdem die zuvor gesichert sind).
Den Framepointer hat deine aufgerufene ASM-Routine wie in der FAQ
geschrieben in Ruhe zu lassen (und zu sichern). Der GCC könnte einen
Framepointer benutzen, um in folgender Situation auf die lokalen
Variablen a und b zuzugreifen, wenn a und/oder b nicht in einem freien
Register gehalten werden können, sondern auf dem Stack residieren.
a wäre dann innerhalb caller() z.B. Inhalt von Framepointer+x und b
Inhalt von Framepointer+y. Innerhalb meine_ASM_routine() kannst du nicht
sicher (ohne Blick in das konkrete Einzeldisassemblat) sagen, auf welche
lokale Variable des Aufrufers der Framepointer zeigt. Aus dem
Gültigkeitsmodell für Variablen her ist ein mit Framepointer getrickster
Zugriff auf a und b aus meine_ASM_routine() heraus illegal. Für den
legalen Zugriff müsstest du z.b. die Adressen oder Inhalte als
Funktionsparameter mitgeben
extern void meine_ASM_routine(void);
void caller(void)
{
int a, b;
a = ....
meine ASM_routine();
b = a * ...
}
Wie gesagt, ein Blick in die Disassemblerlistings ist auch nicht
schlecht. Dabei ruhig auch mit den Optimierungsstufen experimentieren.
Oft sieht man in den unoptimierten Listings fast nix sinnvolles und in
den optimierten schon.
Ein dummer Besucher schrieb:> Schande über mich, Doppelpost!> habe vergessen zu Fragen, was es mit Zugriff auf Register auf sich hat.> Es wird wiederholt erwähnt, daß ein Offset von 20hex nötig ist, wenn in> einer asm Datei ein Register verwendet wird.> Ich bin bei meinem Studium von google auf ein #define> Ich_hab_es_leiser_vergessen gestoßen, was nötig ist für dieses Offset.> Da fehlt mir Wissen, ich kann den Link aber auch nicht mehr finden -> Hilfe erbeten!
Das steht in
http://www.nongnu.org/avr-libc/user-manual/group__avr__sfr__notes.html
in Verbindung mit
http://www.nongnu.org/avr-libc/user-manual/assembler.html
Im Prinzip musst du schauen, ob für deinen AVR _SFR_ASM_COMPAT bzw. wie
__SFR_OFFSET definiert sind und dann deinen ASM-Code entsprechend
schreiben.
Bei der Standardsituation (__SFR_OFFSET (so it will be 0x20 by default))
benutzt du das Makro z.B. _SFR_IO_ADDR(PORTD), wenn du mit out(/in...)
auf die SFRs zugreifen willst, d.h. das für den Memeoryadressraum
definierte PORTD im I/O Adressraum abbilden willst.
Bzw. wenn du auf SFR im Memoryadressraum mit lds/sts zugreifen willst,
dann geht das direkt mit z.B. sts PORTD, r24 bzw. besser mit sts
_SFR_ADDR(PORTD), r24, wenn nicht standardgemäße __SFR_OFFSET im Spiel
sind.
Ein dummer Besucher schrieb:> Hallo, liebe Gemeinde!>> Ich möchte diesen thread Nutzen, um den gccasm möglichst gut zu> verstehen. Dabei geht es erst mal nur um das Interesse, später am eh> esten um asm files, die in c-Projekte eingefügt werden. (wenn ich recht> verstehe ist es ein Unterschied, ob ich asm Funktionen aus einem> c-Programm aufrufe oder umgekehrt c generierte Funktionen in einem asm> Projekt; s.u. Register sichern)
Es ist nicht wirklich ein Unterschied, weil das gleiche Application
Binary Interface (ABI) verwendet wird.
Wenn also eine C-Funktion aufgerufen wird, so werden nach deren
Ausführung Y und R2-R17 unveränderte Wert haben, während davon
ausgegangen werden muss, daß R0, R18-27 und Z veränderte Werte haben.
R1 hat eine Sonderrolle. Das ABI sagt, daß in diesem Register immer der
Wert 0 zu stehen hat. Verändert eine Codesequenz diesen Wert (etwa bei
MUL), so muss selbst dafür Sorge getragen werden, daß danach wieder eine
0 darin steht. Wird eine C-Funktion mit einen anderen Wert als 0 in R1
aufgerufen, so wird der C-Code evtl. fehlerhaft ausgeführt weil das ABI
verletzt ist, und der Wert darin kann mit 0 überschrieben wirden sein.
Die Instruktionen die in einer Funktion zur Ausführung kommen, müssen
also dem ABI genügen. Egal, ob sie von einem Compiler erstellt wurden
oder Handarbeit sind.
Mit dem Wert von Y kannst in Asm nix anfangen. ALso entweder Y sichern
oder nicht anfassen, genau wie mit allen anderen call-saved Registern
auch.
> Also los. Eins nach dem anderen. Wie gesagt es geht darum, wenn> überhaupt, *.S Dateien mit asm Funktionen in einem c-Projekt zu> verwenden.
Ein Beispiel, wie's konkret aussehen kann, gibt's in
http://www.mikrocontroller.net/articles/AVR_Arithmetik/Sinus_und_Cosinus_%28CORDIC%29> verstehe ich> http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_reg_usage schon> mal richtig, daß ich dann in der asm Funktion die Register R2-R17 und> das y-Register sichern muss, wenn diese eins davon verändert und
Ja. Zudem siehe R1 oben
> andersrum R18-R27 und Z-Register GANZ beliebig einfach verwendet werden
Ja. Dito für R0
> kann? Ganz beliebig, weil der c Compiler vor einem asm Aufruf weiß, daß> er eins davon verwendet und deshalb selber, VOR dem Aufruf der asm> Funktion diese auf den Stack pusht und auch wieder zurückspeichert?
Im Prinzip ja, auch wenn avr-gcc hier eine etwas andere Strategie fährt:
Die call-used Regs werden -- falls verwendet -- im Prolog/Epilog der
Funktion verarztet, nicht um jeden einzelnen Funktionsaufrufe herum.
> Falls das stimmt: wenn ich eine isr in Assembler schreiben möchte, muss> ich alle benutzen register selber sichern und zurückschreiben - gcc weiß> ja nicht, wann der Interrupt auslöst....
Genau. ALLE verwendeten Rehs sichern und wieder herstellen. Auch R1 und
R0. Hier auf keinen Fall am ISR-Ende eine 0 in R1 schreiben, falls die
ISR R1 verwendet, sondern auf den ursprünglichen Inhalt restaurieren!
> Eine Konkrete Frage: Wenn ich eine Funktion wie void funk(void* ptr) in> asm schreibe, dann wird der pointer als erstes Argument in Register> R24/R25 übergeben? Der muss dann direkt in ein Pointer Register x oder z> (?) kompiert werden? Diese müsste ich aber nicht selber sichern, weil c> das von sich aus täte, wenn die belegt sind..... Wenn das stimmt hat man> schon das sichern und kopieren des Pointers als zusätzliche zeit> verglichen mit einem reinem Assemblerprogramm, wo ich geziehlt schon ein> pointer register laden kann und nciht erst kopieren muss.
C (bzw. avr-gcc) sichert X und Z auch nicht. Er verwendet es, wenn er
eines davon braucht und weiß, daß der Wert durch einen Funktionaufruf
potentiell kaputt geht. Soll ein Wert einen Funktionsaufruf überdauern,
so wird er (evtl. nur temporär) in einem call-saved Reg abgelegt oder
lebt im Frame der Funktio und wird nur von dort geladen, falls benötigt.
> Eine letzte Frage ist das y-register, welches ich selber sichern muss zu> beginn der asm Funktion. Das nennt sich "Framing Pointer". Was ist> darunter zu verstehen und was mache ich damit? Ich verstehe die Funktion> darin, daß es eine Kopie des Stackpointers ist wenn die Funktion> startet. Was ich damit mache ist mir nur nicht klar.... :-(
Entweder sichern oder nicht anfassen.
Die lokalen Variablen, die eine Routine braucht, müssen irgendwo gemerkt
werden. Falls es nicht genug Register gibt, werden die Variablen im
Frame angelegt (aber auch dann, wenn man z.B. die Adresse einer lokalen
Variablen oder eines Parameters nimmt, eine Struktur mit "krummer" Größe
wie 3 Byte anlegt oder eine lokale Variable volatile macht). Da ist
Platz auch dem Stack, der don der jeweiligen Funktion dynamisch belegt
und wieder freigegeben wird. Weil AVR keine indirekte Adressierung über
den SP zulässt, muss ein anderes Register dafür herhalten. Das ABI hat
dafür Y auserkoren, und dies ist auch die einzig sinnvolle Wahl (X kann
nicht mit Offsets adressieren und Z wird gebraucht für LPM und CALLI,
JUMPI).