Forum: Compiler & IDEs gccasm - auf dem Weg zum Verständnis


von Ein dummer Besucher (Gast)


Lesenswert?

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!

von Ein dummer Besucher (Gast)


Lesenswert?

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!

von Stefan B. (stefan) Benutzerseite


Lesenswert?

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.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

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.

von Ein dummer Besucher (Gast)


Lesenswert?

Wunderbare Antwort(en), da bedanke ich mich recht herzlich!
Das gibt erst mal wieder eine ganze Zeit neuen Stoff zum nachdenken und 
ausprobieren.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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).

von Johann L. (gjlayde) Benutzerseite


Lesenswert?


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.