Stacknutzung bei Ellipsen erzwingen (PowerPC)

OP #4101005
Lesenswert?

Hallo,

beim Versuch, ein funktionierendes printf für den VLE-Port zu schreiben, 
bin ich auf folgendes Problem gestoßen:
Die Parameter werden alle in Registern übergeben aber va_arg greift auf 
den Stack zu, was irgendwelche Werte zur Ausgabe bringt, nur nicht die 
übergebenen. Das passiert auch bei -O0. Meine gegenwärtige Lösung ist 
ein kleines "Scratch RAM" oberhalb des Stacks, in das ich blind eine 
Anzahl von Registern per Inline-ASM schreibe und dann dort meinen 
Parameter-Pointer hinsetze. Das funktioniert auch und mit einer 
maximalen Anzahl von Parametern kann ich auch leben. Aber eine elegante 
Lösung ist es nicht gerade.
Gibt es eine Möglichkeit, den GCC zur Parameterübergabe per Stack in 
solchen Situationen zu zwingen? Oder lässt sich das irgendwie in den GCC 
reinpatchen?

Jörg
OP #4101133
Lesenswert?

Beispiel:
1
unsigned int x,y,z;
2
x=0x5555;
3
y=0x6666;
4
z=0x7777;
5
printf("values = %x %x %x",x,y,z);
Das aufrufende Programm packt den Pointer auf den String in R3 und die 
Parameter 0x5555, 0x6666, 0x7777 in die Register R4,R5 und R6.
1
void printf (const char *format, ...)
2
{
3
    va_list         pointer;
4
    unsigned char   signed_argument;
5
    value_t         value;
6
    unsigned char   mode;
7
    char            c;
8
    int             temp;
9

10
    va_start(pointer,format);
Hier wird auch format in R3 übernommen, nach va_start zeigt aber der 
Pointer auf den Stack und will mit va_arg auch die Parameter vom Stack 
holen, was natürlich schief geht. Ich helfe mir jetzt mit:
1
void printf (const char *format, ...)
2
{
3
    va_list         pointer;
4
    unsigned char   signed_argument;
5
    value_t         value;
6
    unsigned char   mode;
7
    char            c;
8
    int             temp;
9

10
    pointer=0x40007fc0;
11

12
    asm volatile("e_lis      28,0x4000");
13
    asm volatile("e_or2i     28,0x7FC0");
14
    asm volatile("se_stw     4,0(28)");
15
    asm volatile("se_stw     5,4(28)");
16
    asm volatile("se_stw     6,8(28)");
17
    asm volatile("se_stw     7,12(28)");
18
    asm volatile("e_stw      8,16(28)");
19
    asm volatile("e_stw      9,20(28)");
20
    asm volatile("e_stw      10,24(28)");
21
    asm volatile("e_stw      11,28(28)");
wobei ich im Startup den Stack unterhalb von 0x40007FC0 initialisiere. 
Ab dieser Stelle kann ich mir dann auch die Parameter mittels 
va_arg(pointer,xxx) holen, halt maximal 8 Stück (sofern man keine 
Doubles nutzt).

Jörg
OP #4101201
Lesenswert?

Ja, den kennt es, wenn ich den Prototypen im Header auskommentiere, 
bekomme ich die übliche "implicit declaration..." Warnung.

Der Compiler ist ein 4.8.0 mit diversen Patches aus dem Internet, ein 
offizielles Release für PowerPC it VLE habe ich nirgendwo gefunden. 
Ähnliche Probleme hatte ich auch beim Renesas 78K0R und beim Freescale 
S12X. Um den konstanten Format-String NUR im Flash zu haben, scheinen 
auf den meisten Plattformen irgendwelche "Verrenkungen" notwendig zu 
sein.

Ziel ist eine Bibliothek, die auf allen möglichen Plattformen das selbe 
oder zmindest nachvollziehbar ähnliches Verhalten zeigt, der folgende 
Codeausschnitt ist nur ein Beispiel:
1
    set_printf_output(OUTPUT_UART0);
2
    printf("VAL = %i",var);

Jörg
#4101355
Lesenswert?

Viele Compiler versuchen, zu optimieren und nicht alles stur über 
Pointer zu übergeben. Speziell bei RISC kann ja nicht direkt im RAM 
gerechnet werden, sondern muß erstmal in Register zurück geladen werden.
Oft landen daher die ersten 3 Argumente direkt in Registern und nur 
darüber wird ein Pointer bemüht.
Das printf der Standard-Lib des Compilers weiß das und verhält sich 
entsprechend.
OP #4101535
Lesenswert?

Mein stdarg.h sieht so aus, das sollte eigentlich passen:
1
typedef unsigned char * va_list;
2
#define va_start(marker, last)  { marker = (va_list)&last + sizeof(last); }
3
#define va_arg(marker, type)    *((type *)((marker += sizeof(type)) - sizeof(type)))
4
#define va_copy(dest, src)      { dest = src; }
5
#define va_end(marker)          { marker = (va_list) 0; };

_cdecl kennt der Compiler nicht und __attribute__((stdcall)) behebt das 
Problem auch nicht. Es funktioniert ja so, anders wäre es mir aber 
lieber gewesen.

Eine Standard-Lib gibt es nicht, da es bis dato noch nicht einmal ein 
funktionierendes Compiler-Binary zum Download gab. Mein printf ist eine 
Variante, die für die Ausgabe einen Funktionspointer nutzt. Damit kann 
ich dieselben Routinen für UART, SPI, Speicher (Strings), LCD, Video 
etc. verwenden.

Jörg
Persönliche Seite #4101945
Lesenswert?

Joerg Wolfram schrieb:
> Mein stdarg.h sieht so aus, das sollte eigentlich passen:
>
1
> typedef unsigned char * va_list;
2
> #define va_start(marker, last)  { marker = (va_list)&last + 
3
> sizeof(last); }
4
> #define va_arg(marker, type)    *((type *)((marker += sizeof(type)) - 
5
> sizeof(type)))

Mit Verlaub, das ist Hack mit Soße...  wie wär's z.B. mit
1
#define va_start(v,l)  __builtin_va_start(v,l)
usw.?

Joerg Wolfram schrieb:
> Der Compiler ist ein 4.8.0 mit diversen Patches aus dem Internet,

Und die Autoren wissen was sie tun?
OP #4102316
Lesenswert?

Die stdarg.h ist vom SDCC, vielleicht ist das das Problem. Aber damit 
war es mir immerhin möglich, Funktionen plattformübergreifend zu 
implementieren.

>Und die Autoren wissen was sie tun?

Welche Autoren? Wie ich schon weiter oben geschrieben habe, habe ich 
keinen fertigen GCC für diese Architektur (Powerpc-VLE) gefunden. Anhand 
von Patches von u.a. James Lemke sowie zig SVN checkouts habe ich mir 
dann einen funktionierenden Compiler selbst "zusammengebaut". Damit habe 
ich allerdings kein lauffähiges Programm zusammenbekommen. Also: zuerst 
eigenes Startupfile mit den notwendigen Initialisierungen in ASM 
erstellt und mit -nostartfiles dazugelinkt. Und so habe ich mich Schritt 
für Schritt "vorgetastet".

Jörg
Persönliche Seite #4102685
Lesenswert?

Joerg Wolfram schrieb:
> Johann L. schrieb:
>>Und die Autoren wissen was sie tun?
>
> Welche Autoren?

Die Autoren der entsprechenden Patches bzw. der entsprechenden 
Erweiterungen des Compilers und der Bibliotheken und was sonst noch 
alles dazu gehört und benötigt wird.

> einen funktionierenden Compiler selbst "zusammengebaut"

Was sagen denn die Ergebnisse der Testsuite?  Da werden ja auch 
variadische Funktionen getestet.  Wenn du das halbwegs ernsthaft 
betreibst und dich entschieden hast einen entsprechenden GCC-Port zu 
maintainen (auch wenn nur für dich selbst) gehört Testing ja definitiv 
dazu.

Oder ist es lediglich ein Hobbyprojekt?

> Die stdarg.h ist vom SDCC

stdarg.h ist hoch gradig System- und ABI-abhängig.  Du kannst nicht 
erwarten dass die generisch für alle Compiler / ABIs funktioniert.
OP #4102793
Lesenswert?

Also vielen Dank für die Hinweise, mit der stdarg.h vom GCC hat es dann 
funktioniert. Das werde ich demnächst auch bei den anderen 
Nicht-SDCC-Plattformen ausprobieren.

Das Ganze ist nur ein Hobbyprojekt und ein bisschen "Denkübung". Ziel 
ist eine Art "Universalbibliothek", mit der man auch I/O lastige 
Anwendungen problemlos und ohne größeren Aufwand zwischen verschiedenen 
Mikrocontrollerplattformen (momentan:10) portieren kann. Dazu gehören 
Clock-Einstellungen, IO-Ports, UART, SPI, ADC, PWM, effizientes 
Fliesskomma... Einen Teil davon schreibe ich in ASM, dafür mache ich 
dann auch Tests.
Vor einiger Zeit habe ich im Forum nachgefragt, ob Interesse an so einer 
Lösung besteht, aber nur eine einzige Antwort bekommen. Von daher habe 
ich erstmal von einer Veröffentlichung abgesehen.

Jörg

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren