Forum: Compiler & IDEs Codestück für Mega8 verkleinern (Flash reicht nicht).


von Sam .. (sam1994)


Lesenswert?

Hi

Ich hab durch zufall herausgefunden, dass folgendes Codestück 3KB des 
Megas verbraucht, und darum reicht mir der Speicherplatz nicht.
1
switch(key)
2
        {
3
            case 2: 
4
                if(digit == 0)
5
                    digit = 8;
6
                while(!((uint8_t)(1<<--digit) & mask))
7
                    if(digit < 1)
8
                        digit = 8;
9
                _delay_ms(100);
10
                seconds10 = 0;
11
                break;
12
            case 4:
13
            case 6:
14
                pre = key - 5;
15
                switch(mode)
16
                {
17
                    case MODE_HOUR_MIN:
18
                         Add(&currentGame[digit / 4].time, valency[digit % 4] * pre, MAX_TIME);
19
                         break;
20
                    case MODE_SEC:
21
                         Add(&currentGame[digit / 4].time, valency[digit % 4] * pre, MAX_TIME);
22
                         break;
23
                    case MODE_GUILLOTINE1:
24
                    case MODE_GUILLOTINE2:
25
                    case MODE_GUILLOTINE3:
26
                    case MODE_GUILLOTINE4:
27
                    case MODE_GUILLOTINE5:
28
                    case MODE_GUILLOTINE6:
29
                    case MODE_GUILLOTINE7:
30
                        for(uint8_t i = 0; i < 2; i++)
31
                            Add(&currentGame[i].guillotine[mode - MODE_GUILLOTINE1], valency[digit % 4] * pre, MAX_GUILLOTINE);
32
                        break;
33
                    case MODE_BONUS:
34
                        for(uint8_t i = 0; i < 2; i++)
35
                            Add(&currentGame[i].bonus, valency[digit % 4] * pre, MAX_BONUS);
36
                    case MODE_GUILLOTINE_COUNT:
37
                        for(uint8_t i = 0; i < 2; i++)
38
                            Add(&currentGame[current].options, (1 << 5), 224);
39
                        break;
40
                    case MODE_MOVES_COUNT1:
41
                    case MODE_MOVES_COUNT2:
42
                    case MODE_MOVES_COUNT3:
43
                    case MODE_MOVES_COUNT4:
44
                    case MODE_MOVES_COUNT5:
45
                    case MODE_MOVES_COUNT6:
46
                    case MODE_MOVES_COUNT7:   
47
                        for(uint8_t i = 0; i < 2; i++)
48
                            Add(&currentGame[current].moves[mode - MODE_MOVES_COUNT1], valency10[digit % 4] * pre, 7);
49
                        break;
50
                    case MODE_MOVES:
51
                        Add(&moves, valency10[digit % 4] * pre, MAX_MOVES);
52
                        break;
53
                    _delay_ms(50);
54
                };
55
                break;
56
            case 5:
57
                WaitForKeyUp();
58
                return FINISHED;
59
                break;
60
            case 8:
61
            if(digit > 6)
62
                digit = 255;
63
                while(!((uint8_t)(1<<++digit) & mask))
64
                    if(digit > 6)
65
                        digit = 255;
66
                _delay_ms(100);
67
                seconds10 = 0;
68
                break;
69
            case 9:
70
                return ABORT;
71
                break;
72
        };
Weiß jemand wie ich das verkleinern kann. Compiler Optimiert schon auf 
Größe.
Das komische: Wenn ich die case 4 und 6 auseinandernehme und das ganze 
ohne die Addfunktion schreibe, komm ich auf ein viertel der Größe, 
allerdings steht dann alles doppelt da und ich würde es lieber mit so 
und damit übersichtlicher machen.

: Verschoben durch Moderator
von Floh (Gast)


Lesenswert?

Add funktion zeigen!
Ich hoffe du hast nirgends floats verwendet?

von Sam .. (sam1994)


Lesenswert?

Nein hab ich nicht.
1
void Add(uint32_t* source, int32_t add, uint32_t max)
2
{
3
    int64_t tmp = (int64_t)*source + add;
4
    if(tmp > 0 && tmp <= max)
5
        *source = tmp;
6
}

Sry, hab ich vergessen.

von Peter (Gast)


Lesenswert?

int64_t wird wohl das Problem sein, das ist auf einem 8bitter nicht mehr 
so sinnvoll.

von Sam .. (sam1994)


Lesenswert?

Danke, Peter. Du hattest Recht das hat 2KB gekostet.

Kann man den Code vielleicht noch mehr optimieren, bzw. ist es so 
sinnvoll das mit switch zu machen, oder gibt es da bessere 
Möglichkeiten?

von Peter (Gast)


Lesenswert?

Samuel K. schrieb:
> Kann man den Code vielleicht noch mehr optimieren, bzw. ist es so
> sinnvoll das mit switch zu machen, oder gibt es da bessere
> Möglichkeiten?
so schlimm ist der code nicht. Aber am meisten kann man sparen wenn man 
die genau funktion eines Programmes kennt. Leider kann man anhand der 
paar zeilen nicht wirklihc erkennen was das ding genau macht.

von Sam .. (sam1994)


Lesenswert?

Die Funktion regelt die Eingabe einer Schachuhr. Die Funktion KeyRo 
bekommt da als Argumente ein Modus, der angibt was eingestellt wird, und 
das wird in der switch abgefragt. Die Maske bestimmt dabei nur welche 
Digits der 8stelligen 7Segment Anzeige benutzt werden.

GetKey gibt eine Taste entprellt zurück sobald sie gedrückt ist.

Hier ist nochmal der korrigierte Code, da ein paar kleine Fehler in 
KeyRo drin waren (ich konnte ihmnja nicht testen, da er zu groß war).
1
uint8_t KeyRo(uint8_t mask, uint8_t mode)
2
{
3
    uint8_t digit = 255;
4
    while(!((uint8_t)(1<<++digit) & mask))
5
        if(digit > 6)
6
            return ABORT;
7
    while(1)
8
    {
9
        int8_t pre = 5;
10
        uint8_t key = 255;
11
        while(key == 255)
12
            key = GetKey();
13
        switch(key)
14
        {
15
            case 2: 
16
                if(digit == 0)
17
                    digit = 8;
18
                while(!((uint8_t)(1<<--digit) & mask))
19
                    if(digit < 1)
20
                        digit = 8;
21
                _delay_ms(100);
22
                seconds10 = 0;
23
                break;
24
            case 4:
25
            case 6:
26
                pre -= key;
27
                switch(mode)
28
                {
29
                    case MODE_HOUR_MIN:
30
                         Add(&currentGame[digit / 4].time, (int32_t)valency[digit % 4] * pre, MAX_TIME);
31
                         break;
32
                    case MODE_SEC:
33
                         Add(&currentGame[digit / 4].time, valency[digit % 4] * pre, MAX_TIME);
34
                         break;
35
                    case MODE_GUILLOTINE1:
36
                    case MODE_GUILLOTINE2:
37
                    case MODE_GUILLOTINE3:
38
                    case MODE_GUILLOTINE4:
39
                    case MODE_GUILLOTINE5:
40
                    case MODE_GUILLOTINE6:
41
                    case MODE_GUILLOTINE7:
42
                        for(uint8_t i = 0; i < 2; i++)
43
                            Add(&currentGame[i].guillotine[mode - MODE_GUILLOTINE1], valency[digit % 4] * pre, MAX_GUILLOTINE);
44
                        break;
45
                    case MODE_BONUS:
46
                        for(uint8_t i = 0; i < 2; i++)
47
                            Add(&currentGame[i].bonus, valency[digit % 4] * pre, MAX_BONUS);
48
                    case MODE_GUILLOTINE_COUNT:
49
                        for(uint8_t i = 0; i < 2; i++)
50
                            Add(&currentGame[current].options, (1 << 5), 224);
51
                        break;
52
                    case MODE_MOVES_COUNT1:
53
                    case MODE_MOVES_COUNT2:
54
                    case MODE_MOVES_COUNT3:
55
                    case MODE_MOVES_COUNT4:
56
                    case MODE_MOVES_COUNT5:
57
                    case MODE_MOVES_COUNT6:
58
                    case MODE_MOVES_COUNT7:   
59
                        for(uint8_t i = 0; i < 2; i++)
60
                            Add(&currentGame[current].moves[mode - MODE_MOVES_COUNT1], valency10[digit % 4] * pre, 7);
61
                        break;
62
                    case MODE_MOVES:
63
                        Add(&moves, valency10[digit % 4] * pre, MAX_MOVES);
64
                        break;
65
                };
66
                _delay_ms(50);
67
                break;
68
            case 5:
69
                WaitForKeyUp();
70
                return FINISHED;
71
                break;
72
            case 8:
73
            if(digit > 6)
74
                digit = 255;
75
                while(!((uint8_t)(1<<++digit) & mask))
76
                    if(digit > 6)
77
                        digit = 255;
78
                _delay_ms(100);
79
                seconds10 = 0;
80
                break;
81
            case 9:
82
                return ABORT;
83
                break;
84
        };
85
        //Show on Display, but first set the time to show
86
        SetTime();
87
        //Now we can call Show to show the time
88
        Show(digit, mode);
89
    }

von Peter D. (peda)


Lesenswert?

Switch ist schon recht effektiv.
Aber man kennt von Deinen Variablen nicht den Typ, daher kann man keine 
Optimierungen angeben.

Versuche, möglichst kleine Typen zu nehmen (uint8_t) und möglichst wenig 
globale Variablen.

Wenn Du Code postest, sollte er auch compilierbar sein, d.h. Prototypen 
für alle benutzten Variablen, Funktionen und Macrodefinitionen!
Im Assemblerlisting kann man nämlich am besten sehen, wo es klemmt.


Peter

von Sam .. (sam1994)


Angehängte Dateien:

Lesenswert?

Ok, hier ist der ganze Code zum kompilieren. Die Funktion oben ist die 
viertletzte.

PS: Wer zufällig einen Max7219 an einem Maga hängen hat, kann es sogar 
testen. Allerdings braucht man noch eine Matrixtastur.

von Sam .. (sam1994)


Lesenswert?

In dem http://www.mikrocontroller.net/articles/AVR-GCC-Codeoptimierung 
steht folgendes:

>> Switch-Statements werden durch -mno-tablejump manchmal deutlich kürzer.

Was ist das?

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Peter Dannegger schrieb:
> Switch ist schon recht effektiv.

Bei Mehrfach-Cases (wie in diesem Fall) kann der Schuss aber auch nach 
hinten losgehen. Ich habe schon erlebt, dass hier eine if-else-if-Kette 
mit Or-Verknüpfung statt der Mehrfach-Cases wesentlich kleineren Code 
erzeugt. Vielleicht sollte Samuel den "Unter-Switch" für die Fälle 4 und 
6 mal als if-else-if-Kette umformulieren.

Gruß,

Frank

von Peter D. (peda)


Lesenswert?

Samuel K. schrieb:
> Ok, hier ist der ganze Code zum kompilieren.


Paßt doch bequem rein:
1
AVR Memory Usage
2
----------------
3
Device: atmega8
4
5
Program:    6290 bytes (76.8% Full)
6
(.text + .data + .bootloader)
7
8
Data:        138 bytes (13.5% Full)
9
(.data + .bss + .noinit)


Hier mal ein paar Optimierungsschalter:
1
-fno-inline-small-functions
2
-fno-split-wide-types
3
-fno-move-loop-invariants
4
-Wl,--relax
5
--combine -fwhole-program


Allerdings solltest Du Dich mal um die Warnungen kümmern, z.B.:
1
CHESS.C:62: warning: excess elements in array initializer
2
CHESS.C:62: warning: (near initialization for 'Games')

Entweder Du läßt die [] leer oder Du schreibst die richtige Anzahl rein.


Peter

von Peter D. (peda)


Lesenswert?

Frank M. schrieb:
> Ich habe schon erlebt, dass hier eine if-else-if-Kette
> mit Or-Verknüpfung statt der Mehrfach-Cases wesentlich kleineren Code
> erzeugt.

Ich habe immer nur das Gegenteil erlebt.
Beim Switch versucht er nämlich, ähnlichen Code von Cases zusammen zu 
fassen.
Man sieht eigentlich immer, daß er in andere Cases hineinspringt, obwohl 
das nicht so im Code steht.

Bei if/else läuft aber alles nacheinander ab und daher kann er nicht 
mehr ähnlichen Code mehrfach nutzen.


Peter

von Sam .. (sam1994)


Lesenswert?

Danke für die Optimierungargumente. Kannst du mir vielleicht noch sagen 
wie ich die im MAkefile einbinde (hab noch nie ein Makefile selber 
geschrieben)?

von Sam .. (sam1994)


Lesenswert?

OK ich hab das jetzt geschafft. Aber wenn ich die letzte Zeile als 
Compiler Flag einsetze kommt ein Linker Error, dass er meine 2 
Funktionen in main nicht findet.

von Andreas F. (aferber)


Lesenswert?

Samuel K. schrieb:
> Aber wenn ich die letzte Zeile als
> Compiler Flag einsetze kommt ein Linker Error, dass er meine 2
> Funktionen in main nicht findet.

Diese Flags darfst du nur verwenden, wenn dein Projekt entweder nur aus 
einer einzigen Sourcedatei besteht, oder wenn alle Sourcen mit einem 
einzigen Compileraufruf in einem Rutsch kompiliert und gelinkt werden. 
Letzteres wird wohl in deinem Makefiles nicht gemacht (ist auch eher 
selten der Fall), deshalb kannst du die Flags so nicht verwenden.

Andreas

von Peter D. (peda)


Lesenswert?

Samuel K. schrieb:
> Aber wenn ich die letzte Zeile als
> Compiler Flag einsetze kommt ein Linker Error

Dann laß sie weg, paßt trotzdem:
1
AVR Memory Usage
2
----------------
3
Device: atmega8
4
5
Program:    6698 bytes (81.8% Full)
6
(.text + .data + .bootloader)
7
8
Data:        138 bytes (13.5% Full)
9
(.data + .bss + .noinit)


Peter

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.