Forum: Compiler & IDEs printf-Alternative für AVR


von Herbert (Gast)


Lesenswert?

Hi,

gibt es eine kleine Alternative zu printf, die man bei AVR verwenden 
kann? Hat vielleicht jemand sogar eine Funktion gebastelt und auf Größe 
optimiert?

Ideal wäre es, wenn die Funktion gar keine Floats verwenden würde, 
sondern übergebene Variablen einfach als Vielfache einer Zehnerpotenz 
interpretiert. Also man ruft sie z.B. so auf:
foobar(-12345, 2, 8) für 2 Nachkommastellen und Padding auf 8 Stellen 
und die Funktion liefert den String " -123,45" zurück.

Wäre doch, denke ich, hilfreich.

von Bal (Gast)


Lesenswert?

Ich benutze sehr gern printf, in sämtlichen Variationen, zb vnprintf. 
Die eierlegende Wollmilchsau unter den Funktionen!

Hast du Probleme mit Flash oder CPU-Last, oder warum willst du eine 
Alternative?
Für nichtgenutzte Ressourcen gibts kein Geld zurück...

von printf (Gast)


Lesenswert?

Ich benutze in meinen Projekten als das hier:

http://www.menie.org/georges/embedded/printf.html

Ist sehr kompakt.

von Peter D. (peda)


Lesenswert?


von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Herbert schrieb:
> Ideal wäre es, wenn die Funktion gar keine Floats verwenden würde

Macht das avr-libc-printf() doch standardmäßig gar nicht.

Mich dünkt, du hast dir das einfach nur noch gar nicht angesehen und
willst es daher aus Prinzip nicht nehmen, oder?

von Bal (Gast)


Lesenswert?

Kann man dem TE, sofern er Anfänger ist, auch nicht verübeln.
Hier wird ja überall gepredigt printf() sei böse und der Untergang des 
Abendlandes...
Und dann wird irgendwelcher Bastelcode vorgestellt der alles viel 
umständlicher macht, unleserlich ist und trotzdem nur einen Bruchteil 
der Features eines printf bietet.

Nein, es gibt in 95% der Fälle keinen Grund, sich selbst dieser 
mächtigen Waffe zu berauben.

von Herbert (Gast)


Lesenswert?

Ehrlich gesagt hat der Jörg sogar recht. ;) Ich habe nach "const char 
*__fmt" schon aufgehört zu lesen. Ein Format-String ist etwas, das ich 
nicht brauche und was die Funktion einfach aufblähen muss.
Aktuell verwende ich eine selbst gestrickte Geschichte, die dem letzten 
Vorschlag ähnelt. Siehe auch die Parallelen beim Aufruf. Ich verwende 
aber div und keine Subtraktionsschleife. Dachte nur, dass es noch 
kleiner geht... eigentlich ist das doch etwas, was überall gebraucht 
wird. Rechnen in Hundertsteln oder Tausendsteln ist doch üblich.

von m.n. (Gast)


Lesenswert?

Bal schrieb:
> Kann man dem TE, sofern er Anfänger ist, auch nicht verübeln.
> Hier wird ja überall gepredigt printf() sei böse und der Untergang des
> Abendlandes...

Das gleiche Spiel läuft ja auch immer wieder mit float ab ;-)

Herbert schrieb:
> gibt es eine kleine Alternative zu printf, die man bei AVR verwenden

Welchen AVR verwendest Du denn?

von Herbert (Gast)


Lesenswert?

Ist ein ATMEGA168 und Flash ist sehr knapp, obwohl schon stark 
optimiert. Für zukünftige Funktionen wird immer etwas Platz gebraucht, 
je mehr desto besser. Momentan sind noch 62 Bytes frei. ;)

von Peter D. (peda)


Lesenswert?

Herbert schrieb:
> Ist ein ATMEGA168 und Flash ist sehr knapp, obwohl schon stark
> optimiert.

Wirklich?

Oft sieht man leider copy&paste Monster mit tausenden printf Aufrufen, 
das kostet natürlich massig Flash.
Man kann da sehr leicht optimieren, wenn man bei switch/case nicht allen 
100 cases sein eigenes printf spendiert, sondern einfach nur Pointer auf 
Formatstring und Argumente setzt und danach ein einziges printf 
plaziert.
Die cases ohne printf verläßt man z.B. mit return.

: Bearbeitet durch User
von m.n. (Gast)


Lesenswert?

Herbert schrieb:
> Ist ein ATMEGA168 und Flash ist sehr knapp, obwohl schon stark
> optimiert.

Gerüchten zu Folge soll es einen ATmega328 geben, der doppelt soviel 
Flash-Speicher hat.

von Herbert (Gast)


Lesenswert?

Nein, ich bin an diesen Chip gebunden. Das Projekt stammt aus einer 
Zeit, als der 328 noch selten war und die Baugruppen sind fertig und 
weit verteilt.

von m.n. (Gast)


Lesenswert?

Im Beispielprogramm ist eine Funktion zeige_fk_zahl(int32_t wert), wo 
'wert' mit Nachkommastellen interpretiert wird.
Unter Umständen kannst Du sie Dir entsprechend anpassen.
Beitrag "Frequenz / Drehzahl, 4-stell. 7-Segm.-LCD, ATtiny45"

von Jürgen S. (jurs)


Lesenswert?

Herbert schrieb:
> Ideal wäre es, wenn die Funktion gar keine Floats verwenden würde,
> sondern übergebene Variablen einfach als Vielfache einer Zehnerpotenz
> interpretiert. Also man ruft sie z.B. so auf:
> foobar(-12345, 2, 8) für 2 Nachkommastellen und Padding auf 8 Stellen
> und die Funktion liefert den String " -123,45" zurück.

Ich als Arduino-Programmierer mache mir sowas ja einfach selbst, wenn 
ich das brauche.

Beispielcode (sollte mit AVR GCC allgemein laufen, getestet habe ich es 
aber nur mit Arduino) für "int" Wertebereich:
1
char *itoaDigits(int i, int width, int digits)
2
{
3
#define INT_MAXWIDTH 7  // 5 digits + sign + dot
4
  if (width>INT_MAXWIDTH) width=INT_MAXWIDTH;
5
  static char buf[INT_MAXWIDTH + 1];  // Room for formatted number  and '\0'
6
  char *p = buf + INT_MAXWIDTH;       // points to terminating '\0'
7
  if (i<0) 
8
  {
9
    buf[0]='-'; 
10
    i=-i;
11
  }
12
  else buf[0]='+';
13
  do 
14
  {
15
      *--p = '0' + (i % 10);
16
      i /= 10;
17
      digits--;
18
      if (digits==0) *--p = '.';
19
  } while ((i!=0) || (digits>-1));
20
  if (buf[0]=='-') *--p = '-';
21
  while (p>buf+INT_MAXWIDTH-width) *--p = ' ';
22
  return p;
23
}

Denkbar wären aber auch Funktionsvarianten, denen man zur Formatierung 
einen Pointer auf ein (ausreichend großes) bereits existierendes 
char-Array übergibt, in dem die Formatierung erfolgt, falls man die 8 
Bytes RAM-Verbrauch einsparen möchte.

Denkbar sind auch Variationen, z.B. dass positive Zahlen stets mit einem 
führenden "+" Zeichen ausgegeben werden. Oder dass bei der Formatierung 
das Vorzeichen immer "ganz links" statt "vor der ersten Ziffer" steht.

Und auch die Ausgabe mit "Dezimalkomma" statt "Dezimalpunkt" wäre ein 
Klacks, wenn man es möchte.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Herbert schrieb:
> Momentan sind noch 62 Bytes frei. ;)

Wenn du derartige Randbedingungen mal gleich ins Eröffnungsposting
geschrieben hättest, hätte sich die Hälfte des Threads erledigt.

Für derart spezifische Forderungen sehe ich nur zwei Varianten:

. Eigenbau (hast du ja wohl schon)
. itoa() oder ltoa() aus der Bibliothek (je nach Wertebereich) und
  dann mit normalen String-Funktionen die beiden letzten Stellen
  abtrennen für das Dezimalzeichen bzw. bei Bedarf so umkopieren,
  dass linksseitig Leerzeichen gefüllt werden

von Herbert (Gast)


Lesenswert?

Steht doch oben:

Herbert schrieb:
> Hat vielleicht jemand sogar eine Funktion gebastelt und auf Größe
> optimiert?

Jürgens Algorithmus ist gut und so ähnlich mache ich das auch. Jürgen 
hat sogar den kleineren. Muss noch ergründen, warum.
Wenn man statt % und / gleich div() einsetzt, wird es noch etwas kleiner 
(sofern man div() schon woanders im Einsatz hat).
Kann es sein, dass du implizierst, dass lokale Variablen mit 0 (ich sehe 
kein \0) initialisiert sind?

von Jürgen S. (jurs)


Lesenswert?

Herbert schrieb:
> Kann es sein, dass du implizierst, dass lokale Variablen mit 0 (ich sehe
> kein \0) initialisiert sind?

Die "static char" variable "buf[INT_MAXWIDTH + 1]" ist als 
static-Variable eigentlich keine richtige lokale Variable, weil sie 
nicht auf dem Stack angelegt wird. Diese wird daher vor dem 
Programmstart genau so ausgenullt wie alle anderen globalen und static 
Variablen im Programm.

Unter AVR GCC kann man sicher sein, dass die ausgenullt ist.

Vom Algorithmus her muß bei meinem Code beim Aufruf der Funktion auch 
nicht die ganze Variable ausgenullt zu sein (ist sie auch nicht, wenn 
die Funktion vorher schon gelaufen ist), sondern es muss nur das 
allerletzte Zeichen ein Nullzeichen sein.

Wenn der Code auch zu anderen Compilern außerhalb der AVR GCC Welt 
kompatibel sein soll und man sich nicht sicher ist, ob "static char 
buf[]" wirklich beim Start ausgenullt ist, kann man zur Sicherheit am 
Anfang der Funktion das letzte Zeichen auf Nullzeichen setzen:
1
buf[INT_MAXWIDTH]='\0';

Aber bei AVR GCC ist das meines Erachtens nach immer überflüssig, auch 
wenn man nicht mit der Arduino-IDE programmiert und diverse Parameter 
und Optionen für Compiler und Linker anders setzen kann. Daher habe ich 
es weggelassen.

von Herbert (Gast)


Lesenswert?

Danke, habe das "static" eben erst bewusst wahrgenommen. Netter Trick. 
Spart Speicher. Dann kann die Geschichte mit dem '+' aber auch weg.

Habe oben noch ein
1
  char pad = ' ';
2
  if (digits == -1) pad = '0';

eingebaut. Gibt man -1 als Digits an, kriegt man Zero-Padding, z.B. für 
Uhrzeiten ganz nett. 12:03 sieht besser aus als 12: 3

von Herbert (Gast)


Lesenswert?

Ne, stimmt, das '+' muss bleiben. Ich muss Feierabend machen. ;)

von Yalu X. (yalu) (Moderator)


Lesenswert?

Jürgen S. schrieb:
> Wenn der Code auch zu anderen Compilern außerhalb der AVR GCC Welt
> kompatibel sein soll und man sich nicht sicher ist, ob "static char
> buf[]" wirklich beim Start ausgenullt ist,

Variablen mit "static storage duration" (dazu gehören auch lokale, als
"static" definierte Variablen) werden laut Standard vor dem Start des
eigentlichen C-Programms mit Nullen vorbelegt. Es ist also korrekt und
auch portabel, das explizite Schreiben des Nullzeichens wegzulassen.

von (prx) A. K. (prx)


Lesenswert?

Herbert schrieb:
> Ein Format-String ist etwas, das ich
> nicht brauche und was die Funktion einfach aufblähen muss.

Da ohne Formatstring nur Strings ausgegeben werden können suchst du 
vermutlich etwas wie fputs.

von oskar (Gast)


Lesenswert?

Peter Dannegger schrieb:
> an kann da sehr leicht optimieren, wenn man bei switch/case nicht allen
> 100 cases sein eigenes printf spendiert, sondern einfach nur Pointer auf
> Formatstring und Argumente setzt und danach ein einziges printf
> plaziert.
> Die cases ohne printf verläßt man z.B. mit return.

Kann dazu jemand ein Beispiel machen?
Habe den Kommentar nicht ganz verstanden!

LG
Oskar

von Herbert (Gast)


Lesenswert?

Du schreibst einfach deine Strings in einen Puffer und hast nur einen 
einzigen Funktionsaufruf ganz unten unter deinem switch-Gebilde. Ist 
eigentlich eine ganz übliche Technik.

von m.n. (Gast)


Lesenswert?

Herbert schrieb:
> Du schreibst einfach deine Strings in einen Puffer und hast nur einen
> einzigen Funktionsaufruf ganz unten unter deinem switch-Gebilde. Ist
> eigentlich eine ganz übliche Technik.

Das macht der Compiler doch schon von sich aus, auch bei vielfacher 
Wiederholung in jedem 'case'.

von Herbert (Gast)


Lesenswert?

Scheinbar nicht immer, sonst wäre es nicht oben bemängelt worden. ;)) 
Der Compiler ist teils sehr gerissen, teils strunzdoof. Oft kann man 
durch simples Vertauschen von Zeilen (wo keine Abhängigkeiten 
bestehen!!!) etliche Bytes rausholen.

von Peter D. (peda)


Lesenswert?

oskar schrieb:
> Kann dazu jemand ein Beispiel machen?
1
  do{
2
    char *s;
3
    int *val;
4
    switch( i ){
5
      case 0: s = "BLA", val = &x; break;
6
      case 1: continue; // nothing to print
7
      case 2: s = "BLUB", val = &y; break;
8
    // ... usw.
9
    }
10
    printf( "%s %d\n", s, *val );
11
  }while(0);

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Geht noch etwas kleiner:
1
  do{
2
    char *s;
3
    int *val;
4
    switch( i ){
5
      case 0: s = "BLA %d\n", val = &x; break;
6
      case 1: continue; // nothing to print
7
      case 2: s = "BLUB %d\n", val = &y; break;
8
      case 3: s = "PLOP %d\n", val = &z; break;
9
    // ... usw.
10
    }
11
    printf( s, *val );
12
  }while(0);

von oskar (Gast)


Lesenswert?

Danke
für die Erläuterung!

von Mux Matzke (Gast)


Lesenswert?

>s = "BLA"
geht das? ohne strcpy?

von Hans (Gast)


Lesenswert?

Mux Matzke schrieb:
>>s = "BLA"
> geht das? ohne strcpy?

Ja. s ist ein char-Pointer, also eine Variable, in der die 
Speicheradresse eines Zeichens steht. Durch die Zuweisung enthält sie 
die Adresse, an der das Stringliteral "BLA" im Speicher liegt.

Diese Adresse wird später an printf übergeben. Das ist nichts anderes, 
als wenn man direkt schreiben würde:
1
printf("BLA");
Hier wird auch die Adresse des Literals "BLA" übergeben.

Man darf nur nicht versuchen, den Inhalt des Speichers zu ändern, in dem 
das Stringliteral liegt. Deshalb wäre es so sauberer:
1
const char* s;

Man könnte natürlich auch strcpy nutzen. Dafür müsste man aber erstmal 
Speicher reservieren, etwa so:
1
char s[10];
Macht das ganze aber nur umständlicher, langsamer und fehleranfälliger 
(wenn der String mal länger als der reservierte Speicher sein sollte).

von Yalu X. (yalu) (Moderator)


Lesenswert?

Peter Dannegger schrieb:
> Geht noch etwas kleiner:
>   do{
>     char *s;
>     int *val;
>     switch( i ){
>       case 0: s = "BLA %d\n", val = &x; break;
>       case 1: continue; // nothing to print
>       case 2: s = "BLUB %d\n", val = &y; break;
>       case 3: s = "PLOP %d\n", val = &z; break;
>     // ... usw.
>     }
>     printf( s, *val );
>   }while(0);

Man muss mit solchen Handptimierungen  etwas vorsichtig sein. Sie können
in einigen Fällen Vorteile bringen, in anderen Fällen aber genau das
Gegenteil bewirken.

Folgendes Beispiel packt den obigen Code in die Funktion sub1, die
Variablen i, x, y und z werden als Funktionsargumente übergeben.
Die Funktion sub2 tut das Gleiche, aber mit jeweils einem printf in
jedem case-Zweig:
1
void sub1(unsigned char i, int x, int y, int z) {
2
  do{
3
    char *s;
4
    int *val;
5
    switch( i ){
6
      case 0: s = "BLA %d\n", val = &x; break;
7
      case 1: continue; // nothing to print
8
      case 2: s = "BLUB %d\n", val = &y; break;
9
      case 3: s = "PLOP %d\n", val = &z; break;
10
    }
11
    printf( s, *val );
12
  } while(0);
13
}
14
15
void sub2(unsigned char i, int x, int y, int z) {
16
  do{
17
    switch( i ){
18
      case 0: printf("BLA %d\n", x); break;
19
      case 1: continue; // nothing to print
20
      case 2: printf("BLUB %d\n", y); break;
21
      case 3: printf("PLOP %d\n", z); break;
22
    }
23
  } while(0);
24
}

Mit AVR-GCC 4.7.2 ist sub1 228 Bytes und sub2 112 Bytes groß. Die
vermeintliche Optimierung hat die Codegröße also mehr als verdoppelt.

Das liegt u.a. daran, dass in sub1 die Variablen x, y und z, die in
Registern übergeben werden, erst mühevoll ins RAM geschrieben werden
müssen, da nur so ein Pointer darauf erzeugt werden kann.

Auch in sub2 stellt der Compiler den printf-Aufruf inkl. dem Push des
zweiten Arguments und dem anschließenden Aufräumen des Stacks ans Ende
der Funktion.

Der Code in den einzelnen case-Zweigen ist in beiden Fällen gleich lang,
wenn auch unterschiedlich.

Aber selbst wenn der Compiler in sub2 drei separate printf-Aufrufe
erzeugen würde, wäre das Ergebnis in diesem Fall immer noch kürzer als
in sub1.

Wenn man Handoptimierungen macht, sollte man also immer den erzeugten
Assembler-Code vorher und nachher miteinander vergleichen.

P.S.: Warum ist val überhaupt ein Pointer? Nimmt man stattdessen eine
int-Variable und passt den Rest entsprechend an, schrumpft sub1 auf 128
Bytes und ist damit nur noch unwesentlich länger als sub2.

von Peter D. (peda)


Lesenswert?

Bei nur 3 printf lohnt sich das natürlich noch nicht. Das Beispiel soll 
ja nur das Prinzip verdeutlichen.

Und z.B. 10 Variablen übergibt man besser nicht als 10 Argumente, 
sondern als Pointer auf eine Struct oder ein Array.

Yalu X. schrieb:
> P.S.: Warum ist val überhaupt ein Pointer?

Einen Pointer laden kostet 2 Words.
Eine globale Variable laden aber 2 DWords.
Somit sparen wir bei 10 Cases nochmal 40 Byte.

von Johann L. (gjlayde) Benutzerseite


Angehängte Dateien:

Lesenswert?

Jürgen S. schrieb:
>
> Ich als Arduino-Programmierer mache mir sowas ja einfach selbst, wenn
> ich das brauche.
>
> Beispielcode (sollte mit AVR GCC allgemein laufen, getestet habe ich es
> aber nur mit Arduino) für "int" Wertebereich:
>
1
> char *itoaDigits(int i, int width, int digits)
2
> {
3
> #define INT_MAXWIDTH 7  // 5 digits + sign + dot
4
>   if (width>INT_MAXWIDTH) width=INT_MAXWIDTH;
5
>   static char buf[INT_MAXWIDTH + 1];  // Room for formatted number  and 
6
> '\0'
7
>   char *p = buf + INT_MAXWIDTH;       // points to terminating '\0'
8
>   if (i<0)
9
>   {
10
>     buf[0]='-';
11
>     i=-i;
12
>   }
13
>   else buf[0]='+';
14
>   do
15
>   {
16
>       *--p = '0' + (i % 10);
17
>       i /= 10;
18
>       digits--;
19
>       if (digits==0) *--p = '.';
20
>   } while ((i!=0) || (digits>-1));
21
>   if (buf[0]=='-') *--p = '-';
22
>   while (p>buf+INT_MAXWIDTH-width) *--p = ' ';
23
>   return p;
24
> }
25
>

hmmm... Zusammen mit den benötigten Bibliotheksroutinen belegt die 
Routine rund 220 Bytes Flash (vergliechen mit einer Anwendung mit leerer 
main, Compiler avr-gcc 4.9.2).

Verwendung von utoa + strlen erlaubt eine Routine, die inclusive 
Bibliotheks-Geraffel und verglichen mit einer leeren main "nur" 200 
Bytes lang ist:
 
1
#include <stdlib.h>
2
#include <stdint.h>
3
#include <string.h>
4
5
char *my (int i, int8_t width, int8_t digits)
6
{
7
#define INT_MAXWIDTH  8 /* -0.12345 */
8
    static char buf[INT_MAXWIDTH + 1];
9
    unsigned len, u = i;
10
    char *str, *end, sign = '+';
11
    int8_t len8;
12
13
    if (i < 0)
14
        u = -u, sign = '-';
15
16
    utoa (u, buf, 10);
17
    len8 = len = strlen (buf);
18
    end = buf + len;
19
    str = buf + sizeof (buf);
20
21
    do
22
    {
23
        char c = ' ';
24
        if (--digits == -1)
25
            c = '.';
26
        else if (--len8 >= 0)
27
            c = *--end;
28
        else if (digits >= -2)
29
            c = '0';
30
        else if (sign)
31
            c = sign, sign = 0;
32
        *--str = c;
33
    } while (--width);
34
    
35
    return str;
36
}
 
In anbetracht der oben genannten 62 Bytes freiem Flash gewinnt man aber 
auch damit keinen Blumentopf...

Eigentlich sollte es nicht allzu schwer sein, in eine bestehende, kleine 
utoa-ähnliche Routine ein '.' und evtl. ein paar Nullen reinzufutscheln.

Kramen im Eingemachten lieferte folgende Routine, die einen unsigned 
analog zu utoa(.,10) in einen String umwandelt.  Die Funktion liefert 
die Adresse der abschließenden '\0' zurück, was sich bei 
Weiterverarbeitung der Strings u.U. als günstig herausstellt:
 
1
#include <stdint.h>
2
3
static const __flash uint16_t pows10[] =
4
{
5
    10000, 1000, 100, 10
6
};
7
8
char* u16_to_string (char *str, uint16_t n)
9
{
10
    const __flash uint16_t *p = pows10;
11
    uint8_t not0 = 0;
12
    uint16_t pow10;
13
14
    do
15
    {
16
        char c = '0';
17
        pow10 = *p++;
18
        
19
        while (n >= pow10)
20
            not0 = 1, n -= pow10, c++;
21
            
22
        if (not0)
23
            *str++ = c;
24
            
25
    } while (! (pow10 & 2)); // pow10 != 10
26
    
27
    // Einer
28
    *str++ = n+'0';
29
    *str   = '\0';
30
    
31
    return str;
32
}
 
Transkripiert nach GNU-Assembler für den ATmega168 ergibt sich ein Code, 
der noch 56 Bytes Flash belegt; siehe Anhang.

Dem TO verbleibt also die Hausaufgabe, zum Einfügen von Dezimalpunkt 
sowie für Nullen und Leerzeichen zum Auffüllen nicht mehr als 3 
Instruktionen zu verbraten.  Zumindest unter diesem Aspekt erscheint die 
Frage des TO schon recht trollig.

Ergo: Die Lösung des Problems liegt nicht (nur) in einer smarten 
Konvertierung, sondern auch in Review und Straffung der Anwendung.  Laut 
Moores 2. Gesetz gibt es ja kein Programm, das nicht optimiert werden 
kann!

Übrigens ist die Routine nicht nur klein sondern auch schnell: Weder die 
signed- noch die unsigned-Variante dauern länger als 320 Ticks (incl. 
CALL + RET).

Wem das langsam erscheint: Eine einzige Division durch 10 im o.g. Code 
kostet mindestens 215 Ticks.  Über die bis zu 5 Ziffern summiert sind 
das zu schlappen 1000 Ticks aufwärts.  Ausgabe- und Konvertierroutinen 
müssen zwar nicht schnell sein, aber mancher µC hat mehr zu erledigen 
als unentwegt zu dividieren...

von Herbert (Gast)


Lesenswert?

Hi,

die 62 Bytes gelten für die jetzige Fassung, mit meiner eigenen 
(größeren) Routine.
Wenn ich in die 62 Bytes noch eine Ausgabe-Routine einbauen müsste und 
keinen Platz für die Aufrufe selbst mehr hätte, wär's ja auch irgendwie 
merkwürdig. ;))
Ich wollte damit nur verdeutlichen, dass momentan jedes Byte zählt, was 
irgendwie durch Optimierung frei wird.

Ich teste gerade noch ein bisschen. Es ist phänomenal, wie man durch 
Umsortieren von nicht von einander abhängigen Programmzeilen kleineren 
Code erhält.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Johann L. schrieb:
> Transkripiert nach GNU-Assembler für den ATmega168 ergibt sich ein Code,
> der noch 56 Bytes Flash belegt; siehe Anhang.

Und gleich nen Fehler in der signed-Version.  Im Anhang oben fehlte das 
abschließende cpse 0,0:
 
1
;; char* s16_to_string (char *buf, int n)
2
s16_to_string:
3
  tst  r23
4
  brpl u16_to_string
5
6
  neg  r23
7
  neg  r22
8
  sbci r23, 0
9
  movw r26, r24
10
  ldi  r18, '-'
11
  st   X+, r18
12
  cpse 0, 0

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.