Für ne Berechnung auf nem ATtiny26 benötige ich ne große Dynamik, aber
float paßt ja nicht rein.
Da dachte ich, nimmste einfach long long (64Bit), dürfte ja nur doppelt
soviel Code sein, wie long (32Bit).
Aber Pustekuchen, das ist total verschwenderisch programmiert (noch viel
teurer als float).
Ne Division kostet etwa 5kB !!!
Also Finger weg von long long, wenns unter nem ATmega16 ist !
Nun wollte ich mir ne Divisionsfunktion in Assembler schreiben (35 Words
= 70Byte), also 2 Parameter übergeben (r25..r18, r17..10 -> r25..18).
Aber wieder Pustekuchen, die Parameter werden gepusht, dann mein
Assembler ausgeführt und dann die Parameter gepopt, also meine
Berechnung weggeschmissen, schöne Scheiße.
Obendrein wird noch gemeckert, daß kein Returnwert da ist.
Ich bin mit meinem Latein am Ende :-(
Hat überhaupt schonmal jemand erfolgreich Assembler mit Parameter und
Returnwert in ne Funktion integrieren können ?
Peter
Hallo, Peter,
bei einem anderen Prozessor (Toshiba TLCS-900-Umgebung) habe ich es so
gemacht: ich habe einen Funktionsrumpf in C geschrieben (gleiche
Parameter und Rückgabe wie bei meiner späteren Assembler-Funktion,
minimale Funktionalität in der Funktion), habe mir dann den
Assembler-Code angeschaut, den der Compiler daraus macht (quasi
abkopiert), und dann meine Funktionalität in die Assembler-Funktion
eingebaut.
Bei den AVR's habe ich keine Erfahrung mit Assembler, aber es könnte
hier ja genauso gehen?
Günter
Peter Dannegger wrote:
> Ne Division kostet etwa 5kB !!!
Das ist mehr oder weniger ein known issue. Da werden nur die
generischen (in C geschriebenen) Funktionen der libgcc.a benutzt, für
64 bit gibt's keine handoptimierten Varianten.
Du kannst ja gern mithelfen, in Assembler kennst du dich aus, warum
schlägst du nicht mal einen Patch für die libgcc.a vor? Einziger
Wermutstropfen: damit er von GNU akzeptiert wird, musst du deren
Lizenzabkommen unterschreiben (Abtretung der Rechte aus dem Copyright
an die FSF), ohne das akzeptieren sie keine Patches für den GCC, die
mehr als `trivial patches' sind.
(Bitte keine weitere Baustelle wie bei floating point schaffen, bei
der die avr-libc die Funktionen aus der libgcc.a ersetzt. Das ist
Sch***e und führt dazu, dass entsprechende Bugreports bei GCC nicht
ernst genommen werden, auch dann, wenn sie eigentlich echte Bugs
sind.)
> Aber wieder Pustekuchen, die Parameter werden gepusht, dann mein> Assembler ausgeführt und dann die Parameter gepopt, also meine> Berechnung weggeschmissen, schöne Scheiße.> Obendrein wird noch gemeckert, daß kein Returnwert da ist.
Dann hast du wohl irgendwas flasch deklariert, die Warnung passt ja
wohl gut zum Verhalten des Compilers, den Rückkehrwert dann auch zu
ignorieren.
> Hat überhaupt schonmal jemand erfolgreich Assembler mit Parameter> und Returnwert in ne Funktion integrieren können ?
Na klar, macht die avr-libc haufenweise:
% find ~/src/avr-libc/lib* -name \*.c | wc -l
61
% find ~/src/avr-libc/lib* -name \*.S | wc -l
126
Doppelt so viele Assemblerquellen wie in C geschriebene. Ich mag
jetzt nicht zählen, wie viele davon Werte zurückgeben, ist mir zu
müßig.
1
#include<stdint.h>
2
3
externuint64_tmyfunc(uint64_t,uint64_t);
4
5
uint64_tx,y,z;
6
7
void
8
dosomething(void)
9
{
10
z=myfunc(x,y);
11
}
Daraus wird generiert:
1
.globaldosomething
2
.typedosomething,@function
3
dosomething:
4
/* prologue: frame size=0 */
5
pushr2
6
pushr3
7
pushr4
8
pushr5
9
pushr6
10
pushr7
11
pushr8
12
pushr9
13
pushr10
14
pushr11
15
pushr12
16
pushr13
17
pushr14
18
pushr15
19
pushr16
20
pushr17
21
/* prologue end (size=16) */
22
ldsr18,y
23
ldsr19,y+1
24
ldsr20,y+2
25
ldsr21,y+3
26
ldsr22,y+4
27
ldsr23,y+5
28
ldsr24,y+6
29
ldsr25,y+7
30
ldsr2,x
31
ldsr3,x+1
32
ldsr4,x+2
33
ldsr5,x+3
34
ldsr6,x+4
35
ldsr7,x+5
36
ldsr8,x+6
37
ldsr9,x+7
38
movr10,r18
39
movr11,r19
40
movr12,r20
41
movr13,r21
42
movr14,r22
43
movr15,r23
44
movr16,r24
45
movr17,r25
46
movr18,r2
47
movr19,r3
48
movr20,r4
49
movr21,r5
50
movr22,r6
51
movr23,r7
52
movr24,r8
53
movr25,r9
54
rcallmyfunc
55
stsz,r18
56
stsz+1,r19
57
stsz+2,r20
58
stsz+3,r21
59
stsz+4,r22
60
stsz+5,r23
61
stsz+6,r24
62
stsz+7,r25
63
/* epilogue: frame size=0 */
64
popr17
65
popr16
66
popr15
67
popr14
68
popr13
69
popr12
70
popr11
71
popr10
72
popr9
73
popr8
74
popr7
75
popr6
76
popr5
77
popr4
78
popr3
79
popr2
80
ret
81
/* epilogue end (size=17) */
82
/* function dosomething size 98 (65) */
Zugegebenermaßen ist das Umspeichern von x über r2...r9 nach r18...r25
alles andere als optimal, aber das Ergebnis wird ordentlich nach z
abgespeichert.
Bitte nicht mehr als 2 uint64_t übergeben: da gibt's einen offenen
GCC-Bug dafür, für den noch keiner eine richtige Lösung parat hat.
Der Fehler scheint im generischen GCC-Teil zu liegen, tritt aber nur
bei nicht-mainstream-CPUs wie dem AVR zu Tage, da die anderen ihre
eigenen Parameterübergaberoutinen haben. Das führt leider dazu, dass
bei GCC daraus eine recht geringe Priorität resultiert. :-(
Mit dem Qualifier naked kann man ja beim GCC für ARM und AVR den
Prolog und Epilog von Funktionen unterdrücken.
http://www.delorie.com/gnu/docs/gcc/gcc_55.html
Hilft dir das vielleicht bei deinem Push/Pop Problem weiter?