man lädt also einen DWord und möchte diese aufsplitten in seine 4 Bytes.
Der GCC erzeugte Code speichert "d" in 4 nachfolgende Register, gut.
Beim speichern dieser 4 register in die 4 globalen SRAM Bytes erzeugt er
aber zusätzlichen code. Das sieht dann so aus:
1
1878 /* #APP */
2
1879 0b40 2591 lpm r18, Z+
3
1880 0b42 3591 lpm r19, Z+
4
1881 0b44 4591 lpm r20, Z+
5
1882 0b46 5491 lpm r21, Z
6
1883
7
1884 /* #NOAPP */
8
1885 0b48 852F mov r24,r21
9
1886 0b4a 9927 clr r25
10
1887 0b4c AA27 clr r26
11
1888 0b4e BB27 clr r27
12
1889 0b50 8093 0000 sts glcd+41,r24
13
14
1890 0b54 CA01 movw r24,r20
15
1891 0b56 AA27 clr r26
16
1892 0b58 BB27 clr r27
17
1893 0b5a 8093 0000 sts glcd+42,r24
18
19
1894 0b5e BB27 clr r27
20
1895 0b60 A52F mov r26,r21
21
1896 0b62 942F mov r25,r20
22
1897 0b64 832F mov r24,r19
23
1898 0b66 8093 0000 sts glcd+43,r24
24
25
1899 0b6a 2093 0000 sts glcd+39,r18
wie man sieht viel unnötiges gemove und gecleare von unnötigen
Registern.
erwartet hätte ich es so:
1
1878 /* #APP */
2
1879 0b40 2591 lpm r18, Z+
3
1880 0b42 3591 lpm r19, Z+
4
1881 0b44 4591 lpm r20, Z+
5
1882 0b46 5491 lpm r21, Z
6
1883
7
1884 /* #NOAPP */
8
1889 0b50 8093 0000 sts glcd+41,r21
9
1893 0b5a 8093 0000 sts glcd+42,r20
10
1898 0b66 8093 0000 sts glcd+43,r19
11
1899 0b6a 2093 0000 sts glcd+39,r18
immerhin 11 Takte schneller für die 8 Takte zum Speichern im SRAM.
Nebenbei: ein sonstwie gearteter TypCast hate keinen Erfolg !
Nun zur eigentlichen Frage zum inline Assembler:
Wie müsste ich 4 Makros deklarieren die inetwa so aussähen
1
uint8_t=HiHi8(uint32_t);
2
uint8_t=HiLo8(uint32_t);
3
uint8_t=LoHi8(uint32_t);
4
uint8_t=LoLo8(uint32_t);
und die dabei auf die 4 Register in denen der uint32_t gespeichert ist
direkt zugreifen.
Im Beispiel von ob soll also das Makro HiHi8() -> "r21" zurückliefern
damit es dann als Parameter für sts glcd+41, r21 benutzt wird.
Ist das überhaupt mit dem Compiler vereinbar ?
Gruß Hagen
Ja das wäre auch ne Möglichkeit allerdings eben noch unsauberer :(
Denn obige globale Member der glcd Struktur sind nur ein Teil des
Ganzen. Und ich hasse es wenn
Implementations-relevante-Optimierungs-Sachen nach Aussen in der
"Schnittstelle" sichtbar werden. Hm, anders ausgedrückt: auf diese
Struktur greift der Entwickler ja aktiv zu. Hab ich dort schon
"rum-gemorkst" dann versaue ich mir ja quasi die Schnittstelle zu meiner
GLCD Library aus Sicht des Entwicklers. Am liebsten wäre es mir wenn ich
aktiv einen uint32_t TypCasten könnte mit obiger Struct. Das habe ich
aber noch nicht hinbekommen !
Also inetwa so:
1
#define HH8(d) (uint32_t_u)(d).a
2
3
uint32_td=pgm_read_dword();
4
5
glcd.FontWidth=HH8(d);// = (uint32_t_u)(d).a
6
glcd.FontHeight=HL8(d);
7
glcd.BitsPixel=LH8(d);
Ich hasse C das kann ich dir sagen und am meisten hasse ich mich das ich
immerwieder mit solchem Mist Probleme habe und einfach das Wissen fehlt.
Man muß sich das mal vorstellen: da programmiert einer seit 20 Jahren
professionell und verdient Kohle damit. Er kann unzählige
Programmiersprachen: PASCAL, Delphi, PL4, COBOL, BASIC, Assembler
AVR,ARM,6201 usw. und in jeder dieser Sprachen sind solche Konstrukte
wie oben eine Selbstverständlichkeit und absolut intuitiv nur eben im
fu..ing C lernt man es nie. Ich wundere mich wirklich nicht das die
meiste Frage über C die Frage ist wie man nit Zeigern umgeht. Betrachtet
man das zb. in PASCAL so wird man sehen wie wirklich einfach, sicher und
sauber es gehen könnte. Das ist bei mir Unterrichtsstoff von ne halben
Stunde und schon habens meine Lehrlinge intus. Aber in C gibts nur
Probleme.
Wenn ich in PASCAL,COBOL etc. sage "nutze als Parameter 1 Byte" dann
macht er das auch, ausser im C "der Sprache die ja so Hardwarenah ist
wie keine andere, der Sprache mit der man alles machen kann, da wird
idiotischer Weise mit einem festen Datentyp int gearbeitet, weil der
Standard verkorkst ist".
Sorry, ich bin übernächtigt, musste mal raus. Die nächste Frage bei der
ich nicht weiterweis habe ich schon parat !
Gruß Hagen
Wenn man davon ausgeht, dass in der Struktur die Members FontWidth,
FontHeight, FontBitsPixel, FontFirstChar direkt hintereinander und immer
in der Reihnfolge liegen könnte man das doch auch einfach casten, oder?
*((DWORD*)&glcd.FontWidth) = dwordvalue;
MFG
@Albi:
Ja das sollte auch gehen. Eventuell muß man dafür aber beim gcc noch die
Option "-fpack-struct" mit angeben. Bei einzelnen Bytes (uint8_t) sollte
er das aber eigentlich immer so machen. Bin jetzt aber zu faul
nachzuschauen :)
Ansonsten kann man auch einfach die Bytes einzeln aus dem PGM lesen und
sich die lokale Variable komplett sparen.
Die Funktion würde dann etwa so aussehen
@Frank,
dedin letzter Vorschlag ist es den ich bisher benutzt habe. Ein Blick in
die *.LST Datei verät dann das der GCC wiederum unnötige Operationen
einfügt. Das war ja der Grund warum ich nach einer besseren Lösung
suchte.
Deine Makros sind exakt das was ich suchte, danke. Ich werde sie noch
testen und mich dann nochmal melden. Essentiell war es einfach das
Problem das ich die richtigen Typcast nicht kenne. Allerdings könnte das
#define LL8(dw) ((uint8_t)dw) wieder zu einem Problem führen. Wenn ich
einen Tipp abgeben sollte dann meine ich GCC wird bei diesem Makro
erstmal 4 Register kopieren, davon dann 3 clearen und dann nur 1
Register tatsächlöich benutzen, statt sofort und direkt nur 1 Register
in dem der uint32_t gespeichert wurde zu benutzen. Es ging ja darum das
der GCC 19 Takte benötigte für eine einfache Speihceroperation die 8
Takte braucht.
@Albi,
Hm, wenn machbar würde ich solche Tricks eben vermeiden. Einfach um den
Source übersichtlich zu halten.
Gruß Hagen
Shit, das geht auch nicht, bzw. erzeugt andere Probleme ;)
Der Cast zwingt den Compiler zu einem Stackframe, damit er auf Variable
dw per Zeiger typcasten kann. Er muß also dw auf dem Stack ablegen damit
der Cast dann funktionieren kann. Von der Effizienz her also noch
ungünstiger als die anderen Vorschläge.
Bisher hat mein Cast über die Union die besten Resultate erzeugt, auch
wenn der Sourcecode dadurch nicht mehr so elegant erscheint.
Der direkte Zugriff über pgm_read_byte() ist die zweitbeste Methode das
sie
1.) den besten Sourcecode darstellt, eben straightforward
2.) nur 1 Taktzyklus zusätzlich pro Bytezugriff verschwendet, denoch
unnötiger weise
Gruß Hagen
So ich habs endlich. Vielleicht hiltfs ja auch anderen ;)
Nachfolgend erstmal die Funktionen um die 4 Bytes eines DWords zu
extrahieren, ohne das
1.) lokal ein Stackkopie des DWords benötigt wird, bei Typcast über
Zeiger ja nötig
2.) der GCC zusaätzliche Opcodes die sinnlos sind erzeugt
Vielleicht findet ja einer noch eine bessere Lösung, bzw. er sagt einen
Grund warum obige Lösung eventuell problembehaftet sein kann.
Angewendet wirds dann so:
1
voidglcdSelectFont(void*data){
2
3
glcd.FontData=(glcdFontData_t)(data);
4
uint32_td=pgm_read_dword(glcd.FontData);
5
6
glcd.FontWidth=HH8(d);
7
glcd.FontHeight=HL8(d);
8
glcd.FontBitsPixel=LH8(d);
9
glcd.FontFirstChar=LL8(d);
10
}
und erzeugt dieses assembly:
1
2026 .global glcdSelectFont
2
2028 glcdSelectFont:
3
2029 /* prologue: frame size=0 */
4
2030 /* prologue end (size=0) */
5
2031 0b34 FC01 movw r30,r24
6
2032 0b36 9093 0000 sts (glcd+44)+1,r25
7
2033 0b3a 8093 0000 sts glcd+44,r24
8
2034 /* #APP */
9
2035 0b3e 8591 lpm r24, Z+
10
2036 0b40 9591 lpm r25, Z+
11
2037 0b42 A591 lpm r26, Z+
12
2038 0b44 B491 lpm r27, Z
13
2039
14
2040 /* #NOAPP */
15
2041 0b46 8093 0000 sts glcd+41,r24
16
2042 0b4a 9093 0000 sts glcd+42,r25
17
2043 0b4e A093 0000 sts glcd+43,r26
18
2044 0b52 B093 0000 sts glcd+39,r27
19
2045 /* epilogue: frame size=0 */
20
2046 0b56 0895 ret
Diese Funktionen sind ein sehr sinnvoller Ersatz für zb. Shifts der
folgenden Art:
Scheint das man das also generalisieren kann und der GCC damit immer
besseren Code erzeugt. Besonderst das letzte ASM geht manuell gecodet
nicht besser zu machen.
Gruß Hagen