Hallo,
ich frage mich gerade echt, was gcc eigentlich optimiert. Ich schreibe
ein Programm für einen Prozessor (attiny), der nicht so viel Flash hat
und der Speicher geht mir einfach flöten. Das Schlimme daran, es ist
nicht meine falsche Prozessorwahl (höchstens knapp gewählt), sondern der
Compiler! 10 Zeilen inline Assembler verkürzen den Flashbedarf um
100Byte (10%), weil der Compiler nicht erkennt, dass man Variablen am
Anfang der Funktion laden sollte und nicht zwischendurch bei jedem
Schreibzugriff erst laden und danach sofort wieder in den SRAM
schreiben.
Im Internet liest man immer wieder von der tollen Optmierung der
mordernen Compiler - auf dem Avr muss man alles selber machen.
Gibts irgendwelche Tricks um das Programm klein zu halten, damit gcc
nicht auf blöde Ideen kommt? Bzw. Ausdrücke, die man aufjedenfall
vermeiden sollte? Ich möchte nicht mein ganzes Programm in inline
Assembler schreiben.
Erlich, das Programm kannste auch gleich in Assembler schreiben, soviel
wie da schon an Inline-Assembler einsetzt.
Was soll denn das ganzee mit dem T-Flag?
Floh schrieb:> Erlich, das Programm kannste auch gleich in Assembler schreiben, soviel> wie da schon an Inline-Assembler einsetzt.
Ich weiß, aber hier ist nichts anderes sinnvoll. Gcc hat keine
Bitverschiebungsfunktion.
Manchmal sind kleine Inlineasm-Makros auch sehr praktisch. Z.b.
mem_clear passt in 5 Words. Gcc macht gern das doppelte daraus, wenn man
es in c schreibt.
> Was soll denn das ganzee mit dem T-Flag?
Das ganze ist ein Teil einer Lcd_Ansteuerungsroutine. Aber auf einer
einseitigen Platine kann man den LcdTreiber und die Lcd-Segmente nicht
"richtig" verbinden. Deswegen muss ich die ganzen Bits tauschen.
Samuel K. schrieb:> Das ganze ist ein Teil einer Lcd_Ansteuerungsroutine. Aber auf einer> einseitigen Platine kann man den LcdTreiber und die Lcd-Segmente nicht> "richtig" verbinden. Deswegen muss ich die ganzen Bits tauschen.
Wie ist das ganze denn verdrahtet?
Wenn nur die Segmente vertauscht sind, lässt sich das ganze mit einer
Tabelle mit 4 (digits) x 10 (Möglichkeiten) = 40 Einträgen erschlagen.
Samuel K. schrieb:> Ich möchte nicht mein ganzes Programm in inline> Assembler schreiben.
Hast du aber gemacht. Wenn du jedoch ohnehin praktisch den
gesamten Assemblercode bereits vorgibst, was soll denn der
Compiler dann überhaupt noch optimieren können?
Vielleicht beschreibst du ja mal die Funktion verbal, die du
erreichen willst, und lässt dir von jemandem hier eine
Implementierung dafür vorschlagen.
Wo ist in diesem Codefragment jetzt der Teil, den gcc nicht deinen
Wünschen entsprechend optimiert und mit welcher Kommandozeile hast du
das übersetzt?
Samuel K. schrieb:> weil der Compiler nicht erkennt, dass man Variablen am> Anfang der Funktion laden sollte und nicht zwischendurch bei jedem> Schreibzugriff erst laden und danach sofort wieder in den SRAM> schreiben.
Das habe ich bisher nur erlebt, wenn die Variable volatile ist oder der
Optimizer gar nicht eingeschaltet war.
Floh schrieb:> Wenn nur die Segmente vertauscht sind, lässt sich das ganze mit einer> Tabelle mit 4 (digits) x 10 (Möglichkeiten) = 40 Einträgen erschlagen.
Wenn du dir mal die Pinanordnungnen von Lcds anschaust (=40Pins), dann
siehst du dass das schon extrem schwer ist.
Rolf Magnus schrieb:> Wo ist in diesem Codefragment jetzt der Teil, den gcc nicht deinen> Wünschen entsprechend optimiert und mit welcher Kommandozeile hast du> das übersetzt?
Hier der Ouput von case1 (39 Words):
1
case 1:
2
bst(value, 2);
3
216: 62 fb bst r22, 2
4
bld(lcd[2+(digit&1)], 7);
5
218: e8 2f mov r30, r24
6
21a: f0 e0 ldi r31, 0x00 ; 0
7
21c: e1 70 andi r30, 0x01 ; 1
8
21e: f0 70 andi r31, 0x00 ; 0
9
220: 34 96 adiw r30, 0x04 ; 4
10
222: 9f 01 movw r18, r30
11
224: 25 59 subi r18, 0x95 ; 149
12
226: 3f 4f sbci r19, 0xFF ; 255
13
228: d9 01 movw r26, r18
14
22a: 8c 91 ld r24, X
15
22c: 87 f9 bld r24, 7
16
22e: 8c 93 st X, r24
17
bst(value, 3);
18
230: 63 fb bst r22, 3
19
bld(lcd[4+(digit&1)], 0);
20
232: 32 96 adiw r30, 0x02 ; 2
21
234: df 01 movw r26, r30
22
236: a5 59 subi r26, 0x95 ; 149
23
238: bf 4f sbci r27, 0xFF ; 255
24
23a: 8c 91 ld r24, X
25
23c: 80 f9 bld r24, 0
26
bst(value, 4);
27
23e: 64 fb bst r22, 4
28
bld(lcd[4+(digit&1)], 1);
29
240: 81 f9 bld r24, 1
30
242: 8c 93 st X, r24
31
bst(value, 5);
32
244: 65 fb bst r22, 5
33
bld(lcd[digit&1], 7);
34
246: 34 97 sbiw r30, 0x04 ; 4
35
248: e5 59 subi r30, 0x95 ; 149
36
24a: ff 4f sbci r31, 0xFF ; 255
37
24c: 80 81 ld r24, Z
38
24e: 87 f9 bld r24, 7
39
bst(value, 6);
40
250: 66 fb bst r22, 6
41
bld(lcd[digit&1], 6);
42
252: 86 f9 bld r24, 6
43
254: 80 83 st Z, r24
44
bcpf(value, lcd[2+(digit&1)], 0x03);
45
256: f9 01 movw r30, r18
46
258: 80 81 ld r24, Z
47
25a: 63 70 andi r22, 0x03 ; 3
48
25c: 8c 7f andi r24, 0xFC ; 252
49
25e: 86 2b or r24, r22
50
260: d9 01 movw r26, r18
51
262: 8c 93 st X, r24
52
264: 08 95 ret
53
break;
Zum vergleich der Ouput mit dem inlineasm Code, der die Variablen vorher
in Register lädt und danach sichert (27 Words):
Samuel K. schrieb:> Wenn du dir mal die Pinanordnungnen von Lcds anschaust (=40Pins), dann> siehst du dass das schon extrem schwer ist.
Zeig sie her, am besten als Schaltplan.
Durch deine Bitkopiererei denk ich mich jetzt nicht durch.
Wie ist das Verhältnis beider Versionen, wenn du in C das gleiche
Verfahren verwendest wie in der durch Assembler aufgeputschten Version?
Wenn du also vorweg die 4 LCD-Bytes in d1,...d4 ziehst und hinterher
wieder zurückschreibst - aber eben nicht in Assembler, sondern in C.
Register-Optimierung von globalen Array-Zugriffen mit berechnetem Index
ist ein deutlich heisseres Eisen als Optimierung von lokalen skalaren
Variablen. Da solltest du nicht erwarten, dass er von sich aus das
Verfahren völlig umstellt, wie du es in der handoptimierten Version
getan hast.
Da ich inziwschen ein paar andere Funktionen umgeschrieben habe, ist der
Gesamtcode ein bisschen kleiner. Ich habe jedoch keine Funktion, die mit
der obrigen in Verbindung steht verändert. Die geänderten waren beim
Compilieren auskommentiert, da sie noch nicht fertig sind.
Inline Assembler: 644 Bytes
C: 688 Bytes
Der Unterschied ist nur noch klein, auch wenn es viel ist, dafür das es
nur die Lade und Speicherroutinen sind.
Samuel K. schrieb:> Ich weiß, aber hier ist nichts anderes sinnvoll. Gcc hat keine> Bitverschiebungsfunktion.
Bitte?!? Schonmal "<<" und ">>" probiert?
M.e. gibt es keinen Grund, Assembler zu verwenden, außer es kommt
wirklich auf allerhöchste Optimierung bei der Geschwindigkeit an. gcc
optimiert so hervorragend, dass Assembler im Grunde überflüssig ist.
Samuel K. schrieb:> Der Unterschied ist nur noch klein, auch wenn es viel ist, dafür das es> nur die Lade und Speicherroutinen sind.
Yep, dieser Code ist wirklich etwas schräg. Aber der Unterschied ist nun
m.E. so klein, dass man es als Ergebnis eines Compilers akzeptieren
kann. Falls dich das immer noch nervt, dann solltest du auf die
Verwendung eines Compilers gleich ganz verzichten, denn Du wirst immer
über sowas stolpern.
Im obigen Attachment sind beide Versionen der Funktion drin, die erste
mit Assembler, die zweite ohne. Übersetzbar ist es nicht, da fehlt zu
viel: http://www.mikrocontroller.net/attachment/124421/SetDigit.c
Die bld/bst Horden sind dort natürlich trotzdem drin. Er verwendet C
eher als Gerüst für seine Assembler-Inlines, könnte man eine
strukturierte Assembler-Programmierung nennen.
Samuel K. schrieb:> Gibts irgendwelche Tricks um das Programm klein zu halten, damit gcc> nicht auf blöde Ideen kommt? Bzw. Ausdrücke, die man aufjedenfall> vermeiden sollte? Ich möchte nicht mein ganzes Programm in inline> Assembler schreiben.
Auch wenn das im vorliegenden Fall keine Rolle spielt, gibt es zwei ganz
entscheidende Dinge, auf die man bei Microcontrollern ohne FPU
verzichten sollte: Jegliche Fließkommaoperationen! Diese blähen den Code
gewaltig auf und sind eigentlich fast immer überflüssig. Außerdem
sollten bei entsprechenden Controllern (z.B. AVR) String-Konstanten mit
"PROGMEM" oder PSTR() erzeugt werden, damit sie während der Laufzeit
nicht ins RAM kopiert werden. Beides ist extrem speicherschonend.
Volker U. schrieb:> Bitte?!? Schonmal "<<" und ">>" probiert?>> M.e. gibt es keinen Grund, Assembler zu verwenden, außer es kommt> wirklich auf allerhöchste Optimierung bei der Geschwindigkeit an. gcc> optimiert so hervorragend, dass Assembler im Grunde überflüssig ist.
Wenn man bits kopieren möchte (verschieben war ein schlechter Ausdruck),
sind die HW-Instructions bst und bld am geeignesten. Ich glaube nicht
dass gcc sie je einsetzt.
A. K. schrieb:> Im obigen Attachment sind beide Versionen der Funktion drin, die erste> mit Assembler, die zweite ohne. Übersetzbar ist es nicht, da fehlt zu> viel: http://www.mikrocontroller.net/attachment/124421/SetDigit.c
Stimmt, ich hatte vergessen ein Makro hinzuzufügen. Hier mit
Projektdatei im Avrstudio4, im default Ordner ist auch das Makefile
erhalten.
> Die bld/bst sind dort natürlich trotzdem drin. Er verwendet C eher als> Gerüst für seine Assembler-Inlines.
Ich hab nicht so viel Flash, aber ich muss noch ein bisschen
kompliziertere Logik rein, deswegen optimiere ich lieber die einfacheren
Sachen von Hand - sonst könnte ich gleich in Assembler schreiben.
Volker U. schrieb:> Jegliche Fließkommaoperationen!
Keine Sorge, auf solche Ideen komme ich nicht. Dafür wollte ich vor 2
Jahren mal einen 64bit Integer verwenden, das kostet KB-weise Code.
Volker U. schrieb:> damit sie während der Laufzeit> nicht ins RAM kopiert werden.
Kann man avrgcc dazu zwingen entweder allgemein oder speziell verbieten
Variablen zu initialisieren?
PS: Ich verwende inline Assembler hier nicht um auf Geschwindigkeit zu
optimieren, es geht mir allein um die Größe. Im Moment habe ich noch 1KB
Flash frei, sollte es nicht reichen, bekomme ich noch 300B durch den
Bootloader.
Samuel K. schrieb:> Wenn man bits kopieren möchte (verschieben war ein schlechter Ausdruck),> sind die HW-Instructions bst und bld am geeignesten. Ich glaube nicht> dass gcc sie je einsetzt.
Das mit dem Glauben solltest du besser den Kirchen überlassen, die
sind darauf spezialisiert. ;-)
1
$ egrep -i 'bst|bld' *
2
avr-protos.h:extern void avr_output_bld (rtx operands[], int bit_nr);
3
avr.c:#include "obstack.h"
4
avr.c: avr_output_bld (operands, bit_nr);
5
avr.c: avr_output_bld (operands, bit_nr);
6
avr.c: avr_output_bld (operands, bit_nr);
7
avr.c: AS2 (bld,%3,%2-1)));
8
avr.c: AS2 (bld,r1,5) CR_TAB
9
avr.c: return (AS2 (bst,%0,6) CR_TAB
10
avr.c: AS2 (bld,%0,0));
11
avr.c: AS2 (bld,r1,3) CR_TAB
12
avr.c: The scratch register is substituted for real move. */
13
avr.c: int hadbst = 0, hadlsl = 0;
14
avr.c: /* To avoid using bst and bld if the source and
avr.h:/* No libstdc++ for now. Empty string doesn't work. */
23
avr.md: clt\;bld %D0,7"
24
avr.md: bst %D0,7\;com %D0\;bld %D0,7\;com %D0"
25
libgcc-fixed.S: bst r_res, 7
26
libgcc-fixed.S: bst r_resL, 7
27
libgcc-fixed.S: bst r_resL, 7
28
libgcc-fixed.S: bst r_resL, 7
29
libgcc.S: bst r_arg1,7 ; store sign of dividend
30
libgcc.S: bst r_arg1H,7 ; store sign of dividend
31
libgcc.S: bst r_arg1HH,7 ; store sign of dividend
> Ich hab nicht so viel Flash, aber ich muss noch ein bisschen> kompliziertere Logik rein, deswegen optimiere ich lieber die einfacheren> Sachen von Hand
Falsche Voraussetzungen. Für die Entwicklungsarbeit beginnt man
einfach mit einem größeren Controller. Optimiert wird am Ende,
und dann kann man auch sehen, ob es sich lohnt, hie oder da mit
ein wenig inline asm so weit nachzuhelfen, dass man auf diese Weise
auf den nächst kleineren Controller gehen kann. Am Anfang sollte
man einfach erstmal mit dem Projekt so weit zu Potte kommen, dass
es die gewünschten Ergebnisse bringt, ohne dass man an allen Ecken
und Enden mit dem Ressourcenverbrauch geizen muss. Zum Debuggen
braucht man schnell auch mal paar Strings mehr, weil sie einem das
Entwicklerleben einfacher machen.
Samuel K. schrieb:> Kann man avrgcc dazu zwingen entweder allgemein oder speziell verbieten> Variablen zu initialisieren?
Speziell: ja, es gibt ein Attribut um sie in eine Sektion für nicht zu
initialisierende Variablen zu bringen. Steht wohl irgendwo in der Doku
der avr-libc.
Das Spielt beim Platz aber keine Rolle, denn ob eine Variable implizit
mit 0 initialisiert wird, oder überhaupt nicht, ist neutral. Nur
explizite Initialisierungen kosten Flash.
Jörg Wunsch schrieb:> Falsche Voraussetzungen. Für die Entwicklungsarbeit beginnt man> einfach mit einem größeren Controller. Optimiert wird am Ende,> und dann kann man auch sehen, ob es sich lohnt, hie oder da mit> ein wenig inline asm so weit nachzuhelfen, dass man auf diese Weise> auf den nächst kleineren Controller gehen kann. Am Anfang sollte> man einfach erstmal mit dem Projekt so weit zu Potte kommen, dass> es die gewünschten Ergebnisse bringt, ohne dass man an allen Ecken> und Enden mit dem Ressourcenverbrauch geizen muss. Zum Debuggen> braucht man schnell auch mal paar Strings mehr, weil sie einem das> Entwicklerleben einfacher machen.
Ich weiß, allersdings, seit ich Assembler programmiere merkte ich
wieviel mit effizienter Programmierung möglich ist. Außerdem: Ein Inline
Asm Schnipsel kostet mich 2 Minuten, ein größerer Avr Geld (und als
Schüler hat man vom ersten mehr als genug).
A. K. schrieb:> Im obigen Attachment sind beide Versionen der Funktion drin, die erste> mit Assembler, die zweite ohne. Übersetzbar ist es nicht, da fehlt zu> viel: http://www.mikrocontroller.net/attachment/124421/SetDigit.c
@Samuel: Ist es möglich, das als übersetzbaren C-Code zu bekommen?
Wenn es um Compiler-Optimierungen geht, dann ist Inline-Assembler als
Beispiel-Code nicht zielführend.
Wenn du dich über schlechten Code vom Compiler beschwerst, dann bitte
ein Beispiel in dem der Compiler schlechten Code macht und nicht eines,
wo das — wie auch immer — umgangen wird.
> Die bld/bst Horden sind dort natürlich trotzdem drin. Er verwendet C> eher als Gerüst für seine Assembler-Inlines, könnte man eine> strukturierte Assembler-Programmierung nennen.
Ja, sieht so aus...
Samuel K. schrieb:> Ich weiß, allersdings, seit ich Assembler programmiere merkte ich> wieviel mit effizienter Programmierung möglich ist.
Wenn dem so wäre, hätte niemand Compiler erfunden. Kann sein, dass
du damit geringfügig kleineren Code erzeugen kannst (das bestreitet
ohnehin keiner), aber er ist am Ende schwerer wartbar.
> Außerdem: Ein Inline> Asm Schnipsel kostet mich 2 Minuten, ein größerer Avr Geld (und als> Schüler hat man vom ersten mehr als genug).
Die 3 Euro für den nächstgrößeren Controller schick ich dir noch im
Brief zu, wenn du sonst keinen Sponsor dafür findest. ;-) Wir
reden hier ja nicht über den Ersatz durch einen Controller mit
256 KiB Flash und was weiß ich, sondern darüber, dass man für den
Anfang eben besser einen ATmega168 oder sowas nimmt, damit man nicht
gleich gegen die Gummiwand "Flash-Größe" rennt beim ersten Anlauf.
Eine alte Programmiererweisheit besagt: "Never start optimizing before
you have profiled it.", oder "Premature optimization is the root of
all evil."
Wenn deine Projekte noch so übersichtlich sind, dass dies alles in
Assembler klaglos machbar ist, dann lass den inline-Asembler
beiseite (der hat andere Aufgaben), pack' die entsprechenden paar
Routinen in separate, handgefeilte Assemblerdateien, und schreib
das Framework ringsrum in C. Die Parameterkonventionen für den
Aufruf von Assemblerroutinen aus C heraus sind ja nun nicht gerade
das best gehütete Geheimnis der Welt.
Noch so als Beispiel für Bitstauschen, das relativ gut übersetzt werden
dürfte:
1
//bit reverse als beispiel
2
3
uint8_tsource=34;//Quelle
4
uint8_tdes=0;//Ziel
5
6
if(source&(1<<0))
7
des|=(1<<7);
8
if(source&(1<<1))
9
des|=(1<<6);
10
if(source&(1<<2))
11
des|=(1<<5);
12
if(source&(1<<3))
13
des|=(1<<4);
14
if(source&(1<<4))
15
des|=(1<<3);
16
if(source&(1<<5))
17
des|=(1<<2);
18
if(source&(1<<6))
19
des|=(1<<1);
20
if(source&(1<<7))
21
des|=(1<<0);
Dürfte recht kompakt übersetzt werden, da die Einzelbitzugriffe als
Maschinenbefehle existieren (sbrs, sbr) und sie vom Compiler (avr gcc)
auch verwendet werden.
Allerdings wärs trotzdem mal sinvoll, die Pinbelegung herzuzeigen.
Denn mit angepasster Tabelle wird das ganze nochmal viel kompakter.
:-)
Johann L. schrieb:> Hier ist wohl nicht mehr viel zu holen...
Klar. War eigentlich gedacht, den Sourcewert variabel zu machen :-)
Aber der Code, der dabei rauskommt ist optimal im Bezug auf Laufzeit und
Codegröße für beliebiges tauschen.
Hier ist der Compiler sehr gut angepasst.
Und das ganze ist sogar lesbar und in C.
:-)
Ich hab den Code oben in der zip datei gepostet.
Bei meinem Problem kommt halt hinzu, dass es 4 Bytes sind in denen Bits
getauscht werden müssen. Zudem maximal 8 gleichzeitig. Also schnell mal
den Buffer löschen geht nicht. Bei Flohs Routine müsste man erstmal alle
relevanten Bits löschen.
Samuel K. schrieb:> Bei Flohs Routine müsste man erstmal alle> relevanten Bits löschen.
Quark. Bits im Register oder direkt auf dem Port machen beim AVR keinen
Unterschied.
Einfach so was:
uint8_t digit = 45; //deine Information
PORTD = 0; //Alles löschen
if(digit & (1<<2))
PORTD |= (1<<7);
...
Sind für 4 Byte auch nur 32 solche Konstrukte, die 64 Assemblerbefehle
ergeben. Pinbelegung ist vollständig frei wählbar.
Samuel K. schrieb:> Ich hab den Code oben in der zip datei gepostet.
Das ist doch aber nach wie vor das verwürgte inline-asm-Geraffel.
Wir wollten den C-Code sehen, der deiner Meinung nach zu unpraktikabel
großen Code generieren würde, denn durch dieses inline-asm-Zeug steigt
kein Schwein durch, und du kannst von keinem Compiler der Welt
erwarten, dass er bei bereits zu 90 % hart vorgegebenem Assemblercode
dann auch noch was dran optimieren könnte.
Floh schrieb:> PORTD = 0; //Alles löschen>> if(digit & (1<<2))> PORTD |= (1<<7);
Hier tauchen die Bits aber der Reihe nach einzeln auf PORTD auf. Kann
für den vorliegenden Fall egal sein, wird aber nicht immer das sein,
was gewünscht ist.
Samuel K. schrieb:> Ich hab den Code oben in der zip datei gepostet.Johann L. schrieb:> Wenn es um Compiler-Optimierungen geht, dann ist Inline-Assembler als> Beispiel-Code nicht zielführend.>> Wenn du dich über schlechten Code vom Compiler beschwerst, dann bitte> ein Beispiel in dem der Compiler schlechten Code macht und nicht eines,> wo das — wie auch immer — umgangen wird.
Und wo ist da jetzt schlechter Code für Bitmanipulation???
Das ist doch alles Inline-Assembler!
Manno, dauern über Compiler mosern und nörgeln aber noch nichma ein
gscheites Beispielprogramm hinschreiben können oder erklären worum es
geht.
NEIN, DER COMPILER WIRD KEINEN INLINE-ASSEMBLER-CODE OPTIMIEREN
Er soll keinen Inline-Assembler Code optimieren. Aber: In meinem Code,
egal ob ihr in versteht oder nicht, werden je nach Fall Bits getauscht.
Das Tauschen ist inline Assembler, das soll er auch nicht ändern, aber
wenn man nicht explizit die Variablen aus dem Ram in die Register holt,
holt er sie immer vor dem Schreibvorgang raus und schreibt sie danach
wieder rein.
Der Compiler macht aus:
Kopiere in SRAM Bit
Kopiere in SRAM Bit
das:
Lade Byte
Kopiere Bit
Speichere Byte
Lade Byte
Kopiere Bit
Speichere Byte
wobei die Bytes dieselben sind. Natürlich macht er das nicht direkt
hintereinander, es kommt jedoch vor.
Floh schrieb:> Samuel K. schrieb:>> Bei Flohs Routine müsste man erstmal alle>> relevanten Bits löschen.> Quark. Bits im Register oder direkt auf dem Port machen beim AVR keinen> Unterschied.
Nichts Quark:
> Einfach so was:>> uint8_t digit = 45; //deine Information> PORTD = 0; //Alles löschen
Damit löscht du mir alle Bits
> if(digit & (1<<2))> PORTD |= (1<<7);
Bit 7 wird gesetzt, Bit 0:6 sind verloren gegangen.
> ...> Sind für 4 Byte auch nur 32 solche Konstrukte, die 64 Assemblerbefehle> ergeben. Pinbelegung ist vollständig frei wählbar.
+ 4 mal Löschen per andi nicht durch Zuweisung.
bst und bld brauchen 64 Assembler-Befehle.
Samuel K. schrieb:> Aber: In meinem Code,> egal ob ihr in versteht oder nicht, werden je nach Fall Bits getauscht.
Dann schreib' diesen vermaledeiten Code endlich mal als C auf.
Danach diskutieren wir über die Optimierung des Compilers.
Niemand wird sich hinstellen und den Compiler auf genau deinen
pathologischen Fall optimieren, dass 80 ... 90 % der Arbeit durch
den Inline-Assembler vorgegeben werden, er aber bitteschön den
Rest jetzt noch mit zwei Befehlen weniger hinbekommen soll, weil
du das im Assembler ja auch geschafft hättest.
Siehe oben: wenn du Assembler programmieren willst, dann schreib
eine Assembler-Quelldatei und lass den Compiler aus dem Spiel. Wenn
du C programmieren willst, dann schreib eine C-Quelldatei. Nein,
"asm" ist kein Schlüsselwort der Programmiersprache C, das ist
erst einmal komplett tabu.
Gerne würde ich es extern auslagern, nur finde ich kein gutes Tutorial
das sagt wie man auf externe Variablen zugreift, wie man Funktionen und
deren Argumente bzw. Rückgabewerte definiert...
Allerdings hat man dann den Nachteil, dass die Register fest definiert
sind und nicht vom Compiler passend gewählt werden können.
Ich finde wenn man C mit inline Assembler schreibt, kann man nebenbei
optimieren und gleichzeitig übersichtlich schreiben. In Assembler reicht
eine kleiner Fehler und man sucht sich tot.
Samuel K. schrieb:> Ja ich meine digitfunc == 2.
Aber da stehen doch Steicherstellen wie
1
bld(lcd[2+(digit&1)],7);
in einem volatile inline assembler und die Constraint ist "+r". Das
muss doch gelesen und geschrieben werden.
bcpf ist übrigens falsch. Es muss heissen:
1
#define bcpf(source, dest, mask) \
2
asm ("andi %[src], %[msk] \n\t" \
3
"andi %[dst], ~%[msk] \n\t" \
4
"or %[dst], %[src]" \
5
: [dst] "+d" (dest), [src] "+d" (source) \
6
: [msk] "M" (mask))
Inline-Assembler ist noch fehleranfälliger als Assembler, da man die
Fehler von Assembler und die des Inline-Assembler Interfaces machen
kann.
> Bei digitfunc > 2 durch das gleiche in C.
Nein. Für digitfunc > 2 steht auch alles in Inline-Assembler.
Samuel K. schrieb:> Gerne würde ich es extern auslagern, nur finde ich kein gutes Tutorial> das sagt wie man auf externe Variablen zugreift, wie man Funktionen und> deren Argumente bzw. Rückgabewerte definiert...
Braucht man denn heutzutage für alles 'n Tutorial?
Die Konventionen für die Parameterübergabe stehen in der avr-libc-
FAQ, außerdem gibt's bei der avr-libc ein Beispielprojekt, bei dem
eine ISR in Assembler realisiert worden ist, der Rest in C.
Globale Variablen kann man, wenn man freundlich sein will, seinem
Assembler als .extern kund tun. Muss man aber nicht, denn alle
Symbolnamen, die der Assembler nicht kennt, trägt er in der Symbol-
tabelle automatisch als `global undefined' ein, um sie durch den
Linker auflösen zu lassen.
Exportierte Symbole muss man als .global deklarieren, alle anderen
Symbole sind lokal in der Assemblerdatei und werden nicht in die
Symboltabelle des Objektmoduls übernommen. In der Regel wird man
nur die Funktionseintrittspunkte in der Assemblerdatei exportieren
wollen.
> Ich finde wenn man C mit inline Assembler schreibt, kann man nebenbei> optimieren und gleichzeitig übersichtlich schreiben.
Ich finde nicht, dass da noch irgendwas wirklich übersichtlich ist.
Ich würde das C-Programm komplett anders aufziehen. Da du den von
dir gewünschten Algorithmus aber nicht formal (oder durch ein
einfaches C-Programmstück) beschreiben kannst oder willst, ist dir
in dieser Hinsicht auch nicht zu helfen. Nicht nur ich bin immer
noch der Meinung, dass deine fest vorgefasste Idee, man müsse diese
Bitwurschtelei partout über BST und BLD implementieren, keineswegs
notwendigerweise das Optimum darstellt.
> In Assembler reicht> eine kleiner Fehler und man sucht sich tot.
Das ist bei deinem Inline-Assemblercode nicht anders. Schon allein,
die Constraints ordentlich auf die Reihe zu bringen, ist nicht immer
ganz einfach, und oftmals merkst du die Fehler nicht sofort, weil
der Compiler halt je nach Randbedingungen u. U. völlig anders agiert.
Johann L. schrieb:> Aber da stehen doch Steicherstellen wiebld (lcd[2+(digit&1)], 7); in einem
volatile inline assembler und die Constraint ist "+r". Das
> muss doch gelesen und geschrieben werden.
Naja, +r heißt doch nur, dass es sowohl input als auch output ist. Der
Compiler hat doch die Freiheit sich ein paar Register zu schnappen und
die Variablen zu puffern.
Danke für die Korrektur, ich dachte wenn das Register nicht mehr
weiterverwendet wird, darf man es als nicht output angeben.
Jörg Wunsch schrieb:> Nicht nur ich bin immer> noch der Meinung, dass deine fest vorgefasste Idee, man müsse diese> Bitwurschtelei partout über BST und BLD implementieren, keineswegs> notwendigerweise das Optimum darstellt.
Ja ich bin festüberzeugt, dass es nicht schneller als 2 Takte/bit geht.
Ich habe ein 3-1/2 Lcd. Der Treiber hat 8Byte Ram für 64 Segmente. Jetzt
müssen (da die Leitungen nicht "richtig" geroutet sind, fast alle Bits
getauscht werden um die einzelnen Segmente anzusteuern.
Wenn du folgendes (4bit) auf 32bit skalierst siehst du was umsortiert
werden muss.
cbda -> abcd
Ich habe mir auch schon überlegt, das ganze mit irgendeinem kleinen
hin-und herschieben aufzulösen, aber man hat keine Chance. Selbst eine
Tabelle welche die Zuordnungen beschreibt, wäre viel zu groß: 10bit/Byte
+ Code (der Code wird sicher größer als 6bit*21 Segmente=16Byte=8Words).
Ich hoffe man hat das verstanden. Jetzt bin ich gespannt. Ich bin immer
offen für kleiner Lösungen. Das EEprom ist schon reserviert.
Jörg Wunsch schrieb:>> Ich finde wenn man C mit inline Assembler schreibt, kann man nebenbei>> optimieren und gleichzeitig übersichtlich schreiben.>> Ich finde nicht, dass da noch irgendwas wirklich übersichtlich ist.
Ich finde es ist übersichtilicher als Assembler, besonders dann wenn man
die Makros per #define in eine Headerdatei steckt.
Samuel K. schrieb:> Johann L. schrieb:>> Aber da stehen doch Steicherstellen wiebld (lcd[2+(digit&1)], 7);>> in einem volatile inline assembler und die Constraint ist "+r".>> Das muss doch gelesen und geschrieben werden.>> Naja, +r heißt doch nur, dass es sowohl input als auch output ist. Der> Compiler hat doch die Freiheit sich ein paar Register zu schnappen und> die Variablen zu puffern.
Nein. Es ist volatile.
Samuel K. schrieb:> Ich habe ein 3-1/2 Lcd. Der Treiber hat 8Byte Ram für 64 Segmente. Jetzt> müssen (da die Leitungen nicht "richtig" geroutet sind, fast alle Bits> getauscht werden um die einzelnen Segmente anzusteuern.>> Wenn du folgendes (4bit) auf 32bit skalierst siehst du was umsortiert> werden muss.> cbda -> abcd
Ich wage noch einen Versuch:
Beschreib doch bitte mal genau die Position der Segmente in den Bytes,
also vielleicht in der Form:
Byte 1:
1a 1c 1b 1d 1e ...
Byte 2:
2a 2d 2f ...
Dann lässt sich abschätzen, obs eine Tabelle bringt. :-)
PS.: Und am besten mal einen Kauflink auf den TreiberIC, alldatasheets
ist anscheinend offline.
Samuel K. schrieb:> Johann L. schrieb:>> Aber da stehen doch Steicherstellen wiebld (lcd[2+(digit&1)], 7); in einem> volatile inline assembler und die Constraint ist "+r". Das>> muss doch gelesen und geschrieben werden.>> Naja, +r heißt doch nur, dass es sowohl input als auch output ist.
Es ging darum, daß die asm-Anweisung volatile ist und das den Zugriff
erzwingt. Warum ist die überhaupt volatile? Wenn die Constraints richtig
definiert sind, ist das unnötig.
Ein input-Operand muß übrigens laut Doku auch zwingend in der
Input-Liste stehen, was er bei dir gar nicht tut.
Samuel K. schrieb:> Kann man avrgcc dazu zwingen entweder allgemein oder speziell verbieten> Variablen zu initialisieren?
Deklarierte Variablen werden NIEMALS intialisiert! Eine Variable hat
nach der Deklaration einen undefinierten Wert (nämlich den, der an der
Speicherstelle zufällig gerade stand). Erst nach der Initialisierung hat
sie einen definierten Wert.
Beispiel:
1
unsignedcharc;
c enthält einen undefinierten Wert zwischen 0 und 255.
Floh schrieb:> Dann lässt sich abschätzen, obs eine Tabelle bringt. :-)> PS.: Und am besten mal einen Kauflink auf den TreiberIC, alldatasheets> ist anscheinend offline.
Ich hab ihn bei reichelt gekauft, doch Angelika hat ihn aus dem Programm
genommen (leider). Bei mir sieht der Link gut aus. Hier nochmal die
Links: Beitrag "Re: gcc und Optimierung"
Byte 1: + ,BAT, 2f, 2a, 2b, 3g, 1g, 1f
Byte 2: a1, b1, : , 2g, 2d, 2e,dp2, 1c
Byte 3: 1d, 1e,dp0, , 3a, 3f, K , -
Byte 4: 3b, 3c, 3d, 3e, , ,dp1, 2c
+,-,:,BAT,K sind andere Zeichen. K steht für die führende 1 aus dem Lcd.
Rolf Magnus schrieb:> Ein input-Operand muß übrigens laut Doku auch zwingend in der> Input-Liste stehen, was er bei dir gar nicht tut.
Sie stehen immer in der Input-Liste, außer sie sind sowohl input als
auch output (+).
Rolf Magnus schrieb:> Warum ist die überhaupt volatile? Wenn die Constraints richtig> definiert sind, ist das unnötig.
Nö, nicht so wie es momentan dasteht: T wird gesetzt und gelesen.
Aber auch mit volatile ist das falsch, denn obwohl die asms volatile
sind, kann der Compiler T verändern. Das T-Flag wird genauso verwendet
wie __tmp_reg__ aka. R0.
Die einzig korrekte Weise, mit T umzugehen, ist die beiden Makros zu
einem zu kombinieren.
Volker U. schrieb:> Deklarierte Variablen werden *NIEMALS* intialisiert! Eine Variable hat> nach der Deklaration einen undefinierten Wert (nämlich den, der an der> Speicherstelle zufällig gerade stand).
Das gilt nur für lokale auto-Variablen.
Variablen im static Storage (egal ob lokal oder global) werden zu 0
initialisiert.
Samuel K. schrieb:> Byte 1: + ,BAT, 2f, 2a, 2b, 3g, 1g, 1f> Byte 2: a1, b1, : , 2g, 2d, 2e,dp2, 1c> Byte 3: 1d, 1e,dp0, , 3a, 3f, K , -> Byte 4: 3b, 3c, 3d, 3e, , ,dp1, 2c
Das nenn ich mal Anarchie :-)
Tabelle scheidet also klar aus.
Allerdings wär da noch mein Vorschlag mit dem Setzen der Bits abhängig
von einem anderen Bit.
Als Input bekommst du 4 8-Bit-Werte (deine Digitstellen).
Du richtest dir 4 leere Byte ein (temporär) und setzt dann je nach Bit
im Input die Bits im dem Zwischenspeichere wieder.
Also in der Richtung:
if(digit0 & (1<<0)) //a1 gesetzt?
byte2 |= (1<<a1);
if(digit0 & (1<<1)) //b1 gesetzt?
byte2 |= (1<<b1);
if(digit0 & (1<<2)) //c1 gesetzt?
byte2 |= (1<<b1);
...
Eine if-Abfrage pro möglichem Bit, ergibt also bei 32 Bit insgesamt 64
Befehle (128 Byte Flash) + ein paar Bytes Initialisierung.
Schneller geht es meiner Meinung nach nicht.
Dann musst du byte1 - byte4 nur noch über i2c übertragen. Voila.
:-)
> Hier ist wohl nicht mehr viel zu holen...
Och, du bist aber jetzt gemein. Mit einer Variablen statt 34 sieht es
nicht mehr ganz so wunderschön aus, wie du schon selbst schreibst ;).
Dein Beispiel ist aber sehr schön um zu zeigen, wie gut gcc optimiert.
Genauso arbeitet gcc übrigens mit Fließkommaoperationen: Der Precompiler
optimiert die wunderbarsten Berechnungen weg, sofern dabei nur
Konstanten benutzt werden.
Und mal ganz allgemein zum alten Disput Assembler vs. Compiler:
Die Sprache C wurde extra so maschinennah gestaltet, damit sie perfekt
optimiert werden kann. Anfang der 70er Jahre war jedes Byte im Computer
noch so wertvoll wie heute im Mikrocontroller. Die MC-Programmierung
führt uns heute deshalb wieder zurück zu den Ursprüngen, was ich sehr
schön finde. Es gibt wohl keine andere Sprache, die so übersichtlich und
gleichzeitig so hoch optimierbar ist, wie C. Deshalb liebe ich sie auch
so. Und um mit Boltzmann zu sprechen: "Wüssten wir nicht, dass sie von
Dennis Ritchie erfunden wurde, könnten man denken, ein Gott hätte sie
geschaffen" lol. Naja, Boltzmann hatte das zwar mehr in Bezug auf die
Maxwellschen Gleichungen gemeint, aber es passt trotzdem schön ;-).
Wie schon erwähnt wurde, sollte ein Gerüst immer aus hochsprachlichem
Code bestehen und nur wenige, zeitkritische Routinen sollten dann als
separate, externe Assembler-Files eingebunden werden. Sonst findet man
meist schon nach wenigen Wochen durch seinen eigenen Code nicht mehr
durch.
Man sollte sich guten Programmierstil von Anfang an angewöhnen.
Gruß, Volker
Johann L. schrieb:> Volker U. schrieb:>> Deklarierte Variablen werden *NIEMALS* intialisiert! Eine Variable hat>> nach der Deklaration einen undefinierten Wert (nämlich den, der an der>> Speicherstelle zufällig gerade stand).>> Das gilt nur für lokale auto-Variablen.>> Variablen im static Storage (egal ob lokal oder global) werden zu 0> initialisiert.
Aha!? Ist das eine Spezialität von gcc oder bestimmter Controller? Laut
Sprach-Definition ist eine statische Variable anfänglich ebenso
undefiniert, wie eine dynamische.
Volker U. schrieb:> Aha!? Ist das eine Spezialität von gcc oder bestimmter Controller?
Das ist eine Spezialität der Sprache C und hat alle Versionen überlebt.
> Laut Sprach-Definition ist eine statische Variable anfänglich ebenso> undefiniert, wie eine dynamische.
Definitiv falsch, was statische/globale angeht. Korrekt nur für "auto",
wie Johann schon schrieb.
Volker U. schrieb:> Die Sprache C wurde extra so maschinennah gestaltet, damit sie perfekt> optimiert werden kann.
Haha.
Pointeraliasing führt schonmal dazu, daß nix optimiert wird.
Und maschinennah? Du kannst in portablem C nicht mal das Speicherlayout
einer Struktur oder eines Bitfeldes beschreiben.
C ist relativ ausdrucksschwach, stellt also dem Compiler diverse
Informationen nicht bereit, die er für Optimierungen nutzen könnte.
Die Sprache wurde so gestaltet wie sie ursprünglich war, um einfach
einen für damalige Verhältnisse recht effizienten Compiler
zusammenstricken zu können.
Schau dir doch mal an, was Ada-Compiler so optimieren können. Oder wie
Ocaml performancemäßig an C rankommt, obwohl die Sprache viel
high-leveliger ist und der komplette Ocaml-Compiler kleiner ist als der
SSA-Optimierer im gcc alleine.
Volker U. schrieb:> Johann L. schrieb:>>>> Variablen im static Storage (egal ob lokal oder global) werden zu 0>> initialisiert.>> Aha!? Ist das eine Spezialität von gcc oder bestimmter Controller? Laut> Sprach-Definition ist eine statische Variable anfänglich ebenso> undefiniert, wie eine dynamische.
1
Buch: ISO/IEC 9899:1999
2
Kapitel: 6.7.8
3
Vers: 10
4
5
If an object that has automatic storage duration is not initialized
6
explicitly, its value is indeterminate. If an object that has static
7
storage duration is not initialized explicitly, then:
8
9
— if it has pointer type, it is initialized to a null pointer;
10
— if it has arithmetic type, it is initialized to (positive or unsigned) zero;
11
— if it is an aggregate, every member is initialized (recursively)
12
according to these rules;
13
— if it is a union, the first named member is initialized (recursively)
A. K. schrieb:> Das ist eine Spezialität der Sprache C und hat alle Versionen überlebt.
Hmm, da hast du wohl Recht. Ich habs grade mal getestet, statische
Variablen bzw. Pointer werden sogar schon bei MSC 1.0 zu 0 bzw. NULL
initialisiert. Das war mir irgendwie nicht (mehr?) bewusst.
Wenn eine Variable initialisiert werden muss, macht man dies ja immer
explizit, allein schon der Übersichtlichkeit halber. Deshalb geraten
solche Automatismen irgendwann in Vergessenheit. Wie gut, wenn es
Menschen gibt, die sogar solche Kleinigkeiten noch im Kopf haben... Aber
gibt es eigentlich irgendeinen plausiblen Grund, warum statische
Variablen initialisiert werden und dynamische nicht?
Dann dürfte die Information auf dieser URL auch falsch sein:
http://www.eckart-winkler.de/computer/c/statvar.htm
Gruß, Volker
xJohann L. schrieb:> Rolf Magnus schrieb:>>> Warum ist die überhaupt volatile? Wenn die Constraints richtig>> definiert sind, ist das unnötig.>> Nö, nicht so wie es momentan dasteht: T wird gesetzt und gelesen.
Und was hat das mit volatile zu tun?
Martin schrieb:> Pointeraliasing führt schonmal dazu, daß nix optimiert wird.
Dafür gibt's doch das Schlüsselwort restrict.
Martin schrieb:> Die Sprache wurde so gestaltet wie sie ursprünglich war, um einfach> einen für damalige Verhältnisse recht effizienten Compiler> zusammenstricken zu können.
Und insbesondere, damit man auf den damaligen 16-Bit Maschinen überhaupt
in der Lage war, einen Compiler zusammenzustricken, der in erträglicher
Zeit compilieren konnte. So arg viel Platz hatte man da nicht.
Dieser Aspekt trug auch dazu bei, das aus Ada nie wirklich etwas wurde.
Bis die Compiler und die Maschinen so weit waren, dass sie sich
gegenseitig vertrugen, war der Zug längst abgefahren. Die ersten
Compiler waren zu unhandliche Klöpse - oder die Maschinen noch nicht
weit genug, sie zu ertragen. Nicht der beste Weg, an Popularität zu
gewinnen.
Volker U. schrieb:> gibt es eigentlich irgendeinen plausiblen Grund, warum statische> Variablen initialisiert werden und dynamische nicht?
C soll ja schnell sein, dann das würde ja auch hiesen das speicher der
mit malloc angefordert wird initalisiert wird.
Global und Static müssen nur einmal initalisiert werden das ändert dann
nichts an der Laufzeit.
Martin schrieb:> Volker U. schrieb:>> Die Sprache C wurde extra so maschinennah gestaltet, damit sie perfekt>> optimiert werden kann.> C ist relativ ausdrucksschwach, stellt also dem Compiler diverse> Informationen nicht bereit, die er für Optimierungen nutzen könnte.
Wir vergleichen es wohl mal lieber mit anderen Sprachen, die zu der Zeit
(1973) verfügbar waren: Fortran, Basic, Cobol. Davon ist nur C noch
weitgehend unverändert übrig geblieben.
Hinzu kommt, dass C auf allen Plattformen verfügbar ist und optimale
Portabilität bietet, was man von Ada und Ocaml nicht unbedingt behaupten
kann.
Du vergleichst ja auch keinen Audi A6 mit einer Postkutsche ;)
Volker U. schrieb:> Wenn eine Variable initialisiert werden muss, macht man dies ja immer> explizit
Bei Controllern ist das eher kontraproduktiv, denn eine explizite
Initialisierung benötigt Platz im Flash, eine implizite nicht.
> gibt es eigentlich irgendeinen plausiblen Grund, warum statische> Variablen initialisiert werden und dynamische nicht?
Statische werden vom Loader oder vom Startup-Code initialisiert. Das
darf man als gratis ansehen.
Volker U. schrieb:> Hinzu kommt, dass C auf allen Plattformen verfügbar ist
Das ja...
> und optimale Portabilität bietet
...aber das?
Portabilität in dem Sinn, dass man auf so ziemlich jeder Zielplattform
einen C Compiler findet, egal ob sie sich dafür eignet oder nicht, das
ja. Aber in dem Sinn, dass eine Migration auf eine Plattform mit anderen
Randbedingungen bei von diesbezüglich Ungeübten geschriebenen Programme
ohne grössere Revision hinhaut, das nein.
A. K. schrieb:>> Wenn eine Variable initialisiert werden muss, macht man dies ja immer>> explizit>> Bei Controllern ist das eher kontraproduktiv, denn eine explizite> Initialisierung benötigt Platz im Flash, eine implizite nicht.
Nicht bei einem halbwegs ernstzunehmenden Compiler. So schwer ist es für
den ja nun nicht gerade, explizite Null-Iinitialisierungen als solche zu
erkennen und mit den impliziten zusammenzuwerfen.
Peter II schrieb:> Volker U. schrieb:>> gibt es eigentlich irgendeinen plausiblen Grund, warum statische>> Variablen initialisiert werden und dynamische nicht?>> Global und Static müssen nur einmal initalisiert werden das ändert dann> nichts an der Laufzeit.
Trotzdem benötigt die Initialisierung auch im statischen Fall Zeit, wenn
auch nur einmal. Meinst du wirklich, dass man bei dynamischen Variablen
nur wegen der Zeit auf eine Initialisierung verzichtet hat? Okay, das
ist zumindest ein halbwegs nachvollziehbarer Grund. Trotzdem irgendwie
inkonsistent.
Rolf Magnus schrieb:> Nicht bei einem halbwegs ernstzunehmenden Compiler.
klar doch.
[c]
void foo() {
int i;
...
i = x + y;
}
[\c]
hier müsste dann i immer wieder mit 0 initalisiert werden, das kostet
jedesmal zeit und ist auch noch unnötig. Man kann es auch nicht nur
einmal machen, weil es immer auf einer anderne stelle auf dem Stack
liegen kann.
A. K. schrieb:> Volker U. schrieb:>>> Wenn eine Variable initialisiert werden muss, macht man dies ja immer>> explizit>> Bei Controllern ist das eher kontraproduktiv, denn eine explizite> Initialisierung benötigt Platz im Flash, eine implizite nicht.
Wenn der Loader das miterledigt, hast du Recht. Dann macht dies wohl
Sinn, weil es keinen zusätzlichen Maschinencode bzw. Platz benötigt.
Darüber hatte ich noch gar nicht nachgedacht. Wieder was dazu gelernt
:-)
Volker U. schrieb:> Trotzdem benötigt die Initialisierung auch im statischen Fall Zeit, wenn> auch nur einmal.
Wenn wir mal den Rahmen der Microcontroller verlassen und uns in
Bereiche ernsthafter Betriebssysteme begeben: Ein Kernel kann einem
Prozess keinen uninitialisierten Speicher übergeben, da könnten ja die
Passwörter vom Vorgänger drin sein.
Daher muss eine Initialisierung des Speichers stattfinden. Und was
liegt da näher, als 0 zu verwenden. Das gilt natürlich auch für den
Stack, aber eben nur bei der ersten Benutzung davon, nützt daher nichts.
> Meinst du wirklich, dass man bei dynamischen Variablen> nur wegen der Zeit auf eine Initialisierung verzichtet hat?
Mit Sicherheit. Die C Compiler der ersten ca. 2 Jahrzehnte Jahre waren
was Optimierung angeht nicht sonderlich weit entwickelt. Viele hätten
bei
int i;
i = 1;
seelenruhig nacheinander erst 0 dann 1 reingeschrieben.
> Trotzdem irgendwie inkonsistent.
Ist es. Aber C erweckt im Vergleich zu den Wirth'schen Sprachen sowieso
nicht grad den Eindruck, als ob da jemand vorher gross nachgedacht
hätte. Da stand m.E. eher das Prinzip "legen wir mal los und sehen was
am Ende rauskommt" Pate.
Rolf Magnus schrieb:> A. K. schrieb:>> Bei Controllern ist das eher kontraproduktiv, denn eine explizite>> Initialisierung benötigt Platz im Flash, eine implizite nicht.>> Nicht bei einem halbwegs ernstzunehmenden Compiler. So schwer ist es für> den ja nun nicht gerade, explizite Null-Iinitialisierungen als solche zu> erkennen und mit den impliziten zusammenzuwerfen.
Hihi, ein Expertenstreit beginnt :-). Erkennt der gcc-Compiler nun die
unnötige, explizite Initialisierung und optimiert sie weg? Dann könnte
ich sie der Übersichtlichkeit halber ja beruhigt stehen lassen :)
A. K. schrieb:> Daher muss eine Initialisierung des Speichers stattfinden. Und was> liegt da näher, als 0 zu verwenden. Das gilt natürlich auch für den> Stack, aber eben nur bei der ersten Benutzung davon, nützt daher nichts.
aber das gilt nicht für den Programmablauf. Dann für Variabel auf dem
Heap wird im normalfall kein Speicher von BS angefordern. Selbst bei
einem malloc kann die Runtime noch speicher auf vorrat haben bevor er
von BS angeordert wird.
Andere Baustelle. Heap ist ja nicht static. Dieses Verhalten ist also
konsistent, weder auto noch malloc initialisieren, weil beides zur
Laufzeit Kosten verursachen würde. Ausserdem gibts immer noch calloc.
sry - ich muss euch mal kurz unterbrechen.
Ich hab noch eine Frage zum externen Assemblercode: Woher weiß der
Compiler welche Argumente einer Funktion übergeben werden und welche den
Rückgabewert bilden?
Samuel K. schrieb:> Ich hab noch eine Frage zum externen Assemblercode: Woher weiß der> Compiler welche Argumente einer Funktion übergeben werden und welche den> Rückgabewert bilden?
Weil du es ihm sagst:
extern double f (long x, int y);
A. K. schrieb:> Ist es. Aber C erweckt im Vergleich zu den Wirth'schen Sprachen sowieso> nicht grad den Eindruck, als ob da jemand vorher gross nachgedacht> hätte. Da stand m.E. eher das Prinzip "legen wir mal los und sehen was> am Ende rauskommt" Pate.
Klar, man muss es in die damalige Zeit einordnen. Es ging darum, schnell
neue Betriebssysteme für neue Plattformen zu schreiben (z.B. Unix), die
sich damals sprunghaft entwickelten. Dafür benötigte man eine schnelle
und zuverlässige, system- und vor allem praxisnahe Sprache. Das war C.
Ich erinnere mich noch gut an die ersten 68000er (16 Bit) Systeme Mitte
der 80er Jahre. Man war begeistert von der neuen Geschwindigkeit, die
diese ersten, kleinen Multiuser/Multitasking Systeme für den
Hausgebrauch erreichten. Schönheit war da nicht gefragt, sondern vor
allem Schnelligkeit. Man denke daran, dass bei der Datenübertragung noch
300-2400 bit/sek üblich waren.
Vor dem Hintergrund ist es doch mehr als respektabel, dass sich die
Sprache bis heute weitgehend unverändert erhalten hat.
A. K. schrieb:> Samuel K. schrieb:>>> Ich hab noch eine Frage zum externen Assemblercode: Woher weiß der>> Compiler welche Argumente einer Funktion übergeben werden und welche den>> Rückgabewert bilden?>> Weil du es ihm sagst:> extern double f (long x, int y);
Das schreibt man ja in den Header. Im Assemblercode steht kann ja jedes
Register für x oder y stehen.
A. K. schrieb:> Samuel K. schrieb:>>> Ich hab noch eine Frage zum externen Assemblercode: Woher weiß der>> Compiler welche Argumente einer Funktion übergeben werden und welche den>> Rückgabewert bilden?>> Weil du es ihm sagst:> extern double f (long x, int y);
Ich glaube, Samuel meinte mehr die Assembler-Seite, oder?
Volker U. schrieb:> Ich glaube, Samuel meinte mehr die Assembler-Seite, oder?
Er sprach aber vom Compiler. Aber wir sind uns offenbar darin einig,
dass wir nicht wirklich wissen, was er meint. ;-)
Samuel K. schrieb:> Im Assemblercode steht kann ja jedes> Register für x oder y stehen.
Nein. Die Art der Parameterübergabe ist vom Compiler oder vom ABI
festgelegt. Mich dünkt, dass man das beim AVR (GCC) irgendwo in der Doku
der avr-libc finden kann. Also dass der ersten Paramater in Register
soundso steht, der zweite in ... etc.
Ich, ok, ihr habt ja recht. Wie weiß der Compiler/Linker/Assembler, wenn
die Funktion Bin3BCD aufgerufen wird, welche Argumente übergeben werden
sollen und am Ende in welchen Registern stehen.
Ich brauche diese Funktion: http://avr-asm.tripod.com/math32x.html
(ziemlich in die Mitte scrollen)
Wie der Compiler das weiss hatte ich oben bereits geschrieben. Und in
welchen Registern sie stehen weiss er, weil er das vorgibt und die
externe Assembler-Funktion sich gefälligst daran zu halten hat.
Also ich bin kein Experte für Assembler, kenne mich da eher nur
rudimentär aus meiner 8086-Zeit aus und habe bisher nur ein bisschen
Assembler für AVRs programmiert.
/* Calling conventions on gcc:
* First parameter passed in r24/r25, second in r22/23 and so on.
* Callee must preserve r1-r17, r28/r29
* Result is passed in r24/r25
*/
LSB ist das jeweils niederigere Register, MSB das höhere.
Danke euch, ich hab jetzt ein paar Seiten gefunden. Ich werde es wohl
trotzdem in Inline Assembler schreiben - wer hat sich den Schrott
ausgedacht Input und Output Register übereinander zulegen?
Samuel K. schrieb:> Danke euch, ich hab jetzt ein paar Seiten gefunden. Ich werde es wohl> trotzdem in Inline Assembler schreiben - wer hat sich den Schrott> ausgedacht Input und Output Register übereinander zulegen?
Wieso Schrott? Wäre andernfalls Verschwendung.
Samuel K. schrieb:> Danke euch, ich hab jetzt ein paar Seiten gefunden. Ich werde es wohl> trotzdem in Inline Assembler schreiben - wer hat sich den Schrott> ausgedacht Input und Output Register übereinander zulegen?
Das habe ich mich auch schon gefragt, aber AVR-Register sind eben knapp.
Hier ein kurzer Beispielcode für eine Warteroutine, die als externe
Assembler-Datei eingebunden werden kann.
Die Deklaration in C lautet:
A. K. schrieb:> Grad ausprobiert. Er erkennt es.
Erst seit Version 3.x.
Samuel K. schrieb:> Danke euch, ich hab jetzt ein paar Seiten gefunden. Ich werde es wohl> trotzdem in Inline Assembler schreiben - wer hat sich den Schrott> ausgedacht Input und Output Register übereinander zulegen?
Mit deiner Arroganz wirst du es noch weit bringen im Leben.
Das mach viele ABIs so, denn Register sind immer knapp, und zu
viele davon will man nicht schon für die Parameterübergabe reservieren.
Bei manchen CPUs (bspw. ARM) wird das ABI übrigens vom
Prozessorhersteller sogar vorgegeben, damit sind unterschiedliche
Compiler dann ABI-kompatibel.
Jörg Wunsch schrieb:> Mit deiner Arroganz wirst du es noch weit bringen im Leben.
Ach, nicht gleich so böse, er hat sich doch nur unreflektiert geärgert
;-)
> Das mach viele ABIs so, denn Register sind immer knapp, und zu> viele davon will man nicht schon für die Parameterübergabe reservieren.
Zumal die "Doppelnutzung" ja auch völlig unproblematisch ist, da die
Übergabe "by value" und nicht "by reference" stattfindet, die
Eingangs-Parameter also unverändert bleiben. Für einen passionierten
Basic-Programmierer ist das auf den ersten Blick vielleicht "Schrott".
;)
By the way: Bist du eigentlich für das avrdude Paket zuständig? Falls
ja, kannst du bei Interesse meinen Schnipsel für den ATtiny167
aufnehmen: Beitrag "ATtiny167: Erweiterung der avrdude.conf". ATtiny87 kommt
auch noch... (sorry für OT, fiel mir grade so ein, als ich deinen Namen
sah)
Gruß, Volker
Volker U. schrieb:> By the way: Bist du eigentlich für das avrdude Paket zuständig?
Jaja, alles zu seiner Zeit. ;-)
Nachdem ich kürzlich endlich das leidige Überspringen von Bytes (bzw.
Pages), die nicht in der Eingabedatei enthalten sind, implementiert
habe, ist der nächste Schritt nun erst einmal, dass sich avrdude.conf-
Einträge auf andere beziehen können und diese nur noch partiell
abändern. Damit kann man sich für all die Controller, die sich nur
in der signature unterscheiden, diesen Wust an expliziten Einträgen
sparen.
Wenn das getan ist, werde ich mal sehen, das ganze avrdude.conf zu
verschlanken, danach kommen die neuen Einträge hinzu.
Samuel K. schrieb:> wer hat sich den Schrott> ausgedacht Input und Output Register übereinander zulegen?
Es zwingt dich niemand, diesen Schrott zu verwenden.
Und wie sonst willst du mitteilen, daß der Inline-Schnippel einen Wert
ändert und welche Registerklassen zulässigf sind?
Ein MULSU Ra, Rb hat andere Nebenbedingungen als ein LDD Ra, Rb+const
Wie Register verwendet und Parameter übergeben werden steht zB in
http://www.rn-wissen.de/index.php/Avr-gcc/Interna#Registerverwendung
Samuel K. schrieb:> Bei mir sieht der Link gut aus.
Naja, aber eben nur bei dir, vermutlich hat deine Brause das was
gecachet. Aber warum jemanden wie alldatasheet befragen, wenn man
die Info auch komplett authentisch von NXP bekommt?
http://www.nxp.com/documents/data_sheet/PCF8577C.pdf
Rolf Magnus schrieb:> Johann L. schrieb:>> Rolf Magnus schrieb:>>>>> Warum ist die überhaupt volatile? Wenn die Constraints richtig>>> definiert sind, ist das unnötig.>>>> Nö, nicht so wie es momentan dasteht: T wird gesetzt und gelesen.>> Und was hat das mit volatile zu tun?
Wenn das bld-asm nur eine input-Constraint hat (der Input wird nicht
verändert, das "+r" ist also überflüssig und "r" reicht) und das asm
nicht volatile ist, kann es komplett entfernt werden. Das asm überlebt
nur deshalb und setzt T, weil ev volateil ist bzw. wegen der Constraint
"+r" und weil der Output später nich verwendet wird.
Samuel K. schrieb:> Byte 1: + ,BAT, 2f, 2a, 2b, 3g, 1g, 1f> Byte 2: a1, b1, : , 2g, 2d, 2e,dp2, 1c> Byte 3: 1d, 1e,dp0, , 3a, 3f, K , -> Byte 4: 3b, 3c, 3d, 3e, , ,dp1, 2c
OK, das ist ja mal ein Anfang. ;-)
Jetzt müsstest du nur noch erklären, wie die Eingangsdaten dafür
aussehen. Das wird aus deiner SetDigit-Routine nicht wirklich
ersichtlich, zumal du von einem 8-Byte-Array lcd[] schreibst.
Was darin die 8 Byte sind, und ob das lcd[] nun die Eingangs- oder
Ausgangsdaten sind, ist mir nicht klar. Ich vermute letzteres,
aber wie passt das dann zu den nur 4 Byte, die ich hier oben
zitiert habe?
Btw.:
Samuel K. schrieb:>> Nicht nur ich bin immer>> noch der Meinung, dass deine fest vorgefasste Idee, man müsse diese>> Bitwurschtelei partout über BST und BLD implementieren, keineswegs>> notwendigerweise das Optimum darstellt.>> Ja ich bin festüberzeugt, dass es nicht schneller als 2 Takte/bit geht.
Zuvor ging es dir aber immer um die Codegröße, nicht um die
Geschwindigkeit.
> Dürfte recht kompakt übersetzt werden, da die Einzelbitzugriffe als> Maschinenbefehle existieren (sbrs, sbr) und sie vom Compiler (avr gcc)> auch verwendet werden.
Genauso muß man es machen, das ganze Assembler-Rumgewurstel ist voll für
die Katz!
Und da es hier um 32 Pins des LCD geht, die 32 Bits in 4 Registern
zugeordnet sind, nimmt man ganz einfach source und dest als uint32_t.
Das ergibt dann bis zu 32 Zuordnungen, also rund 68 Words Code (136
Byte).
Noch weniger Code könnte man mit einer Schleife und einer Tabelle mit 32
Einträgen (Bit-Index) erreichen.
Peter
Jörg Wunsch schrieb:> Jetzt müsstest du nur noch erklären, wie die Eingangsdaten dafür> aussehen.
Die Eingangsdaten sind sortiert, so dass für die Zeichen eine Tabelle
genutzt werden kann: a,b,c,d,e,f,g. Diese werden dann bitweise in das
Ausgangsarray gesetzt (lcd). Das Array ist so aufgebaut, dass es einfach
an den Slave gesendet werden kann, der die Daten dann übernimmt.
> Das wird aus deiner SetDigit-Routine nicht wirklich> ersichtlich, zumal du von einem 8-Byte-Array lcd[] schreibst.> Was darin die 8 Byte sind, und ob das lcd[] nun die Eingangs- oder> Ausgangsdaten sind, ist mir nicht klar. Ich vermute letzteres,> aber wie passt das dann zu den nur 4 Byte, die ich hier oben> zitiert habe?
8Bytes weil es 2 Lcds sind, die vom Treiber mulitplext werden.
> Btw.:>> Samuel K. schrieb:>>> Nicht nur ich bin immer>>> noch der Meinung, dass deine fest vorgefasste Idee, man müsse diese>>> Bitwurschtelei partout über BST und BLD implementieren, keineswegs>>> notwendigerweise das Optimum darstellt.>>>> Ja ich bin festüberzeugt, dass es nicht schneller als 2 Takte/bit geht.>> Zuvor ging es dir aber immer um die Codegröße, nicht um die> Geschwindigkeit.
2 Words / bit ^= (meistens) 2 Takte / bit
Peter Dannegger schrieb:> Genauso muß man es machen, das ganze Assembler-Rumgewurstel ist voll für> die Katz!
Warum? Es ist (meiner Meinung nach) nicht übersichtlicher und weder
schneller noch kleiner.
> Und da es hier um 32 Pins des LCD geht, die 32 Bits in 4 Registern> zugeordnet sind, nimmt man ganz einfach source und dest als uint32_t.> Das ergibt dann bis zu 32 Zuordnungen, also rund 68 Words Code (136> Byte).
bst/bld sind doch genau für diese Aufgabe gedacht -> 64Words = 128Byte
> Noch weniger Code könnte man mit einer Schleife und einer Tabelle mit 32> Einträgen (Bit-Index) erreichen.
Das stimmt, man spart vielleicht 40Byte hat dafür 10fache Laufzeit.
Samuel K. schrieb:> Die Eingangsdaten sind sortiert, so dass für die Zeichen eine Tabelle> genutzt werden kann: a,b,c,d,e,f,g. Diese werden dann bitweise in das> Ausgangsarray gesetzt (lcd).
OK.
> 8Bytes weil es 2 Lcds sind, die vom Treiber mulitplext werden.
Ah, dieses Detail fehlte bislang.
>> Genauso muß man es machen, das ganze Assembler-Rumgewurstel ist voll für>> die Katz!>> Warum? Es ist (meiner Meinung nach) nicht übersichtlicher und weder> schneller noch kleiner.
Kann man möglicherweise mit paar Präprozessor-Tricks noch recht
übersichtlich hingeschrieben bekommen.
>> Noch weniger Code könnte man mit einer Schleife und einer Tabelle mit 32>> Einträgen (Bit-Index) erreichen.>> Das stimmt, man spart vielleicht 40Byte hat dafür 10fache Laufzeit.
Nun widersprichst du dir aber komplett selbst. Oben schriebst du
noch, dass du das alles nur wegen der Codegröße machst, jetzt
lehnst du eine in dieser Hinsicht bessere Version jedoch vehement
ab.
Wenn du gar nichts anderes willst, als genau deine BLD/BST-Idee auf
Teufel komm raus durchzuboxen, dann tu's, aber belästige uns nicht,
dass dir der Compiler deinen vorgeworfenen Fraß nicht mehr optimieren
kann. Du hast ihm zu wenige Brocken als "Jongliermaterial" dafür
übrig gelassen, noch was zu optimieren.
Ich habe eine ähnliche Aufgabe mal für ein 20stelliges VFD
implementiert, angesteuert mit 3 x MAX6921 (SPI-angesteuertes
20-bit-Schieberegister mit VFD-Treibern). Die Verdrahtung war dabei
auch komplett durch das Layout vorgegeben, es musste also alles in
Software gemappt werden. Der Code dafür sieht dann so aus:
1
/*
2
* MAX6921 chain setup:
3
*
4
* bit#7 6 5 4 3 2 1 0
5
* byte 0 x x x x g20 g19 g18 g17
6
* 1 g16 g15 g14 g13 g12 g11 g10 g9
7
* 2 g8 g7 g6 g5 g4 g3 g2 g1 [IC4]
8
* 3 x x a1 a3 a5 a7 a9 a11
9
* 4 a13 a15 a17 a19 a21 a23 a25 a27
10
* 5 a29 a31 a33 a35 [IC3]
11
* [5] x x x a2
12
* 6 a34 a32 a30 a28 a26 a24 a22 a20
13
* 7 a18 a16 a14 a12 a10 a8 a6 a4 [IC2]
14
* ^-- MOSI
15
*/
16
17
staticconstdigit_bitsdbits[NDIGITS]PROGMEM=
18
{
19
{0b0000,0b00000000,0b00000001},/* g1 */
20
{0b0000,0b00000000,0b00000010},
21
{0b0000,0b00000000,0b00000100},
22
{0b0000,0b00000000,0b00001000},
23
{0b0000,0b00000000,0b00010000},
24
{0b0000,0b00000000,0b00100000},
25
{0b0000,0b00000000,0b01000000},
26
{0b0000,0b00000000,0b10000000},
27
{0b0000,0b00000001,0b00000000},
28
{0b0000,0b00000010,0b00000000},
29
{0b0000,0b00000100,0b00000000},
30
{0b0000,0b00001000,0b00000000},
31
{0b0000,0b00010000,0b00000000},
32
{0b0000,0b00100000,0b00000000},
33
{0b0000,0b01000000,0b00000000},
34
{0b0000,0b10000000,0b00000000},
35
{0b0001,0b00000000,0b00000000},
36
{0b0010,0b00000000,0b00000000},
37
{0b0100,0b00000000,0b00000000},
38
{0b1000,0b00000000,0b00000000},/* g20 */
39
};
40
41
/*
42
* Diagnostic patterns.
43
*/
44
staticconstsegment_bitssbits[]PROGMEM=
45
{
46
// a aaaaaaaa aaaa aaaaaaaa aaaaa
47
// aaaaa1 11112222 2333 a 33322222 11111aaa
48
// xx135791 35791357 9135xxx2 42086420 86420864
49
{0b00000000,0b00000000,0b00000000,0b00000000,0b00000000},/* all off */
50
{0b00111111,0b11111111,0b11110001,0b11111111,0b11111111},/* all on */
Allerdings habe ich mir einen ATmega48 gegönnt dafür, der empfängt die
Daten seriell und stellt sie multiplex auf dem VFD dar. Dabei werden
noch ein paar Steuerzeichen (Cursorbewegung, Löschen und dergleichen)
implementiert, das alles passt zusammen mit dem 5x7-Font dann in 2,7
KiB. Dafür kann ich das da oben auch heute noch lesen. :-)
Jörg Wunsch schrieb:>>> Noch weniger Code könnte man mit einer Schleife und einer Tabelle mit 32>>> Einträgen (Bit-Index) erreichen.>>>> Das stimmt, man spart vielleicht 40Byte hat dafür 10fache Laufzeit.>> Nun widersprichst du dir aber komplett selbst. Oben schriebst du> noch, dass du das alles nur wegen der Codegröße machst, jetzt> lehnst du eine in dieser Hinsicht bessere Version jedoch vehement> ab.
Sollte ich am Ende die letzten freien Bytes suchen, dann würde ich die
Tabelle wählen. Es ist so, dass je effizienter das Programm ist, desto
langsamer kann ich den Takt wählen, desto weniger Strom verbraucht das
Gerät. 500 Taktzyklen mehr bei 32kHz sind viel.
Jörg Wunsch schrieb:> Wenn du gar nichts anderes willst, als genau deine BLD/BST-Idee auf> Teufel komm raus durchzuboxen, dann tu's, aber belästige uns nicht,> dass dir der Compiler deinen vorgeworfenen Fraß nicht mehr optimieren> kann. Du hast ihm zu wenige Brocken als "Jongliermaterial" dafür> übrig gelassen, noch was zu optimieren.
Ich lasse mich gern überzeugen. Doch ich glaube dass meine 40Byte
Progrose sehr optimistisch war.
128Byte (bld/bst) - 10bit*32 Tabelle = 68 Byte = 34 Words.
Diese Bitfummelei in 34 Befehle zu bekommen wird schwer sein.
Jörg Wunsch schrieb:> ist der nächste Schritt nun erst einmal, dass sich avrdude.conf-> Einträge auf andere beziehen können und diese nur noch partiell> abändern. Damit kann man sich für all die Controller, die sich nur> in der signature unterscheiden, diesen Wust an expliziten Einträgen> sparen.
Na, das hört dich doch sehr vielversprechend an. Frohes Schaffen!
Beim tiny167 hätte es mir aber nicht so viel geholfen, da Speichermodell
und Timing hier deutlich anders sind, als bei anderen, ähnlichen AVRs
(z.B. tiny84). Aber ich habe es genossen, den Eintrag zu erstellen, da
ich so endlich gezwungen war, mich genauer mit der Struktur der
avrdude.conf auseinanderzusetzen, die ich zuvor eher als kryptisch
empfand. ;)
Tschö, Volker
Samuel K. schrieb:> Es ist so, dass je effizienter das Programm ist, desto> langsamer kann ich den Takt wählen, desto weniger Strom verbraucht das> Gerät. 500 Taktzyklen mehr bei 32kHz sind viel.
32 kHz? ;-)))
Wieviel verbraucht der Prozessor denn bei 128 kHz (50 uA?) oder bei 1
MHz (400 uA)? Selbst bei Batteriebetrieb sollte das eigentlich kaum ins
Gewicht fallen, da die anderen Komponenten vermutlich deutlich höhere
Verluste haben.
Samuel K. schrieb:> Sollte ich am Ende die letzten freien Bytes suchen, dann würde ich die> Tabelle wählen. Es ist so, dass je effizienter das Programm ist, desto> langsamer kann ich den Takt wählen, desto weniger Strom verbraucht das> Gerät. 500 Taktzyklen mehr bei 32kHz sind viel.
Du solltest natürlich alle Optimierungskriterien schon vorab nennen.
Allerdings ist es meist sinnvoller, den Prozessor mit einem höheren
Takt laufen zu lassen und dann nach getaner Arbeit schlafen zu
legen. Während dieser Zeit kann man ja auch die CPU runtertakten.
Logischerweise ist sorgfältig handgefeilter Assemblercode dann das
Beste, wenn man:
. sowohl nach Codegröße als auch Ausführungszeit optimieren muss,
weil die anderen Randbedingungen das verlangen,
. ausreichend Zeit hat, den Kram zu pflegen (auch bei Änderungen
in der Verdrahtung zum Beispiel)
Dann aber, wie schon genannt, am besten als separate Assemblerdatei.
Innerhalb dieser bist du dein eigener Herr, und du kannst trotzdem
noch den C-Compiler außen herum für den Rest benutzen, bei dem du
irgendwelche komplexere Logik umsetzen willst.
Ansonsten hier ein Vorschlag, wie man es in C schreiben könnte. Ja,
es ist größer als dein handgefeilter Code (das war zu erwarten), aber
man kann es zumindest lesen ;-), und es lässt sich bei Bedarf schnell
anpassen. Vielleicht fällt ja noch jemandem was ein, wie man es
kürzer bekommt (das Array ist ja ziemlich `sparse').
Jörg Wunsch schrieb:> ielleicht fällt ja noch jemandem was ein, wie man es> kürzer bekommt (das Array ist ja ziemlich `sparse').
Ach, klar, man kann natürlich statt der fertigen Bitwerte die
Bitnummern ins Array schreiben.
Die Daten liegen schon in einer gepackten struct? Warum greift gcc nicht
mit einem Pointer darauf zu? Das würde einen Haufen Flash sparen. Gcc
verwendet im Code auch dauernd jmp, obwohl rjmp reichen würde? Kann man
das abschalten?
>> Dürfte recht kompakt übersetzt werden, da die Einzelbitzugriffe als>> Maschinenbefehle existieren (sbrs, sbr) und sie vom Compiler (avr gcc)>> auch verwendet werden.>> Genauso muß man es machen, das ganze Assembler-Rumgewurstel ist voll für> die Katz!
Für Bitgewurschdl ist vielleicht auch folgendes interessant:
http://gcc.gnu.org/onlinedocs/gcc/AVR-Built_002din-Functions.html
Damit geht dann