Forum: Compiler & IDEs Vektor mit Inhalt von Vektoren auf Funktionen - wie könnte das gehen?


von Christian J. (Gast)


Lesenswert?

Hallo,

ich bekomme da einen Knoten im Hirn aber ich bin mir sicher, dass es 
irgendwie geht:

Benötigt:

Eine Sprungtabelle an einer fixen Adresse (Ende des ROMs), in welcher 
schön sauber hintereinander die Zeiger auf ausgewählte im Programm 
verwendeten Funktionen stehen. Diese Tabelle wird für ein Userprogramm 
dann immer an der gleichen Stelle stehen, aber die eigentlichen Ziele 
könnten sich wegen bearbeitung des OS verändern, was ja weiter 
entwickelt wird.

So ungefähr

ptr_to_myfunctions[] = {
    Zeiger auf Funktion 1
    Zeiger auf Funktion 2
    Zeiger auf Funktion 3
}

Das Ganze festgenagelt im Flash, d.h. .text Segment

Und der Traum wäre, wenn vor dem Zeiger noch ein 'JMP" als Zahl 
eingebaut werden könnte, denn dann könnte ich aus dem Userprogramm 
direkt weiter. Im Userprogramm werden Prototypen definiert, die auf 
diese Tabelle gelegt werden, so dass auch der Stackframe gleich richtig 
ist, s.h. ich kann aus dem RAM Funktionen im ROM anspringen und habe die 
Daten gleich mit im Gepäck.

int (* Func1) (int) = 0xfff0;
int (* Func2) (int) = 0xfff3;

usw.







Geht das überhaupt?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Christian J. schrieb:
> Geht das überhaupt?

Die Zieladressen in der Tabelle sind zur Linkzeit bekannt?  Dann ist's 
easy:  Schreib die Tabelle in Assembler.

Das Layout der Tabelle wird sich nicht sooo oft ändern ändern.

Alternative ist wie immer Inline Assembler, komfortable Makros sind nur 
was aufwändiger.

von Christian J. (Gast)


Lesenswert?

Johann L. schrieb:
> Schreib die Tabelle in Assembler.

Nee..... :-) Soll C sein, alles einheitlich. In Assembler ist das nicht 
schwer.

 ; Tabelle der RST Vektoren für Software Interrupts auf RAM umlegen
     .org 0x08
     jp ram_prog+0x0008
     .org 0x10
     jp ram_prog+0x0010
     .org 0x18
     jp ram_prog+0x0018
     .org 0x20
     jp ram_prog+0x0020
     .org 0x28
     jp ram_prog+0x0028
     .org 0x30
     jp ram_prog+0x0030
     .org 0x38
     jp ram_prog+0x0038

und

.org adr_vec_table
    _vector_table:
    .dw (_int_sti_gpi_0)
    .dw (_int_sti_gpi_1)
    .dw (_int_sti_gpi_2)
    .dw (_int_sti_gpi_3)
    .dw (_int_sti_timer_d)
    .dw (_int_sti_timer_c)
    .dw (_int_sti_gpi_4)
    .dw (_int_sti_gpi_5)
    .dw (_int_sti_timer_b)
    .dw (_int_sti_transmit_error)
    .dw (_int_sti_transmit_buffer_empty)
    .dw (_int_sti_receive_error)
    .dw (_int_sti_receive_buffer_full)
    .dw (_int_sti_timer_a)
    .dw (_int_sti_gpi_6)
    .dw (_int_sti_gpi_7)


>>Die Zieladressen in der Tabelle sind zur Linkzeit bekannt?

Nach dem Linken auf jeden Fall.

von Markus F. (mfro)


Lesenswert?

So zum Beispiel:
1
/*
2
 * Funktionen
3
 */
4
void funktion1(void)
5
{
6
    ...
7
}
8
9
...
10
11
12
typedef void (*func_ptr)(void);
13
14
func_ptr sprungleiste[] = {
15
    funktion1,
16
    funktion2,
17
    funktion3,
18
    funktion4
19
};

und Aufruf dann (z.B.) so:
1
#define MY_FUNCTION1  do { sprungleiste[0](); } while (0)
2
3
MY_FUNCTION1();

Wenn man unterschiedliche Funktionsparameter braucht, kann
1
func_ptr
 aber auch eine union sein.

von Christian J. (Gast)


Lesenswert?

Markus F. schrieb:

> sprungleiste[0]();

Das initiiert einen Sprung auf die Funktion? Ok... ausprobieren.

Ich glaube das klappt ganz gut wenn de Funktionen keinen Rückgabewert 
habe und keine Parameter aber wenn, dann muss das ja im Aufruf drin 
stehen.

rueck = sprungleiste[0](var1, var2)

wäre auch möglich? Und wie sage ich dem GCC dass er das nicht ins Ram 
legen soll sondern im Flash (.text)?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Christian J. schrieb:
> Johann L. schrieb:
>> Schreib die Tabelle in Assembler.
>
> Nee..... :-) Soll C sein,

Da wirst du aber probleme mit deinem JMP oder JP bekommen, weil du das 
in C nicht erzwingen kannst.  Oder du brauchst irgendwelche Annahmen 
darüber, wie welche Optimierung den Code übersetzen muss

> In Assembler ist das nicht schwer.
>
>      .org 0x08

.org ist seit ca. 20 Jahren obsolet ;-)

von Karl H. (kbuchegg)


Lesenswert?

Christian J. schrieb:
> Markus F. schrieb:
>
>> sprungleiste[0]();
>
> Das initiiert einen Sprung auf die Funktion? Ok... ausprobieren.
>
> Ich glaube das klappt ganz gut wenn de Funktionen keinen Rückgabewert
> habe und keine Parameter aber wenn, dann muss das ja im Aufruf drin
> stehen.
>
> rueck = sprungleiste[0](var1, var2)
>
> wäre auch möglich?

natürlich.

Du definierst dir eben entsprechende Funktionspointer.
Das hier
1
typedef void (*func_ptr)(void);
war ein Zeiger auf eine Funktion, die keine Argumente übernimmt und 
nichts liefert.

Das hier
1
typedef int (*int_func_ptr)(void);
wäre ein Zeiger auf eine Funktion die keine Argumente übernimmt und 
einen int liefert.
1
typedef void (*func_ptr_int)(int);
wäre ein Zeiger auf eine Funktion, die einen int als Argument übernimmt 
und nichts liefert.

Ganz normale Funktionspointer-Datentyp Syntax eben, wie sie in jedem C 
Buch beschrieben sein sollte.

Und daraus baust du dir dann eben eine Struktur mit den 
Funktionspointern zusammen (Struktur deswegen, weil in einem Array ja 
alle Datentypen gleich sein müssen was du hier natürlich nicht brauchen 
kannst)
1
typedef char (*char_func_void)(void);
2
typedef void (*void_func_char)(char);
3
4
struct BIOS_Func_Entries
5
{
6
  char_func_void   func_getc;
7
  void_func_char   func_putc;
8
  ...
9
};

Jetzt legst du dir noch ein Object von dieser Struktur an und versorgst 
die entsprechenden Funktionspointer mit den Adressen der Einsprungpunkte 
und du bist auf dem Weg.

> Und wie sage ich dem GCC dass er das nicht ins Ram
> legen soll sondern im Flash (.text)?

So wie man das eben immer macht. Mit der enstprechenden Attributierung 
bei der Definition der Variablen.

von Markus F. (mfro)


Lesenswert?

Christian J. schrieb:
> Markus F. schrieb:
>
>> sprungleiste[0]();
>
> Das initiiert einen Sprung auf die Funktion? Ok... ausprobieren.
>
Jupp.

> Ich glaube das klappt ganz gut wenn de Funktionen keinen Rückgabewert
> habe und keine Parameter aber wenn, dann muss das ja im Aufruf drin
> stehen.
>
> rueck = sprungleiste[0](var1, var2)
>
> wäre auch möglich?

klar doch. Wird halt mit jedem zusätzlichen Feature ein klein wenig 
komplexer. Wie oben schon geschrieben, kann deine Sprungleiste auch aus 
einem Array aus Unions bestehen:
1
typedef int (*func_ptr_i_v)(void);       /* i_v: int raus, void rein */
2
typedef int (*func_ptr_i_ii)(int, int);  /* i_ii: int raus, zwei ints rein */
3
typedef void (*func_ptr_v_i)(int);       /* usw... */
4
typedef void (*func_ptr_v_v)(void);
5
6
union func_ptr {
7
    func_ptr_i_v i_v;
8
    func_ptr_i_ii i_ii;
9
    func_ptr_v_i v_i;
10
    func_ptr_v_v v_v;
11
};
12
13
func_ptr sprungleiste[] = {
14
    { .i_v = f1 },
15
    { .i_ii = f2 }
16
};

Aufruf dann mit:
1
    res = sprungleiste[0].i_v();
2
    res = sprungleiste[1].i_ii(1, 2);

besonders viel Typprüfung ist allerdings so nicht mehr vorhanden, wenn 
Du also deine Funktionen mit falschen Parameterklammern aufrufst, 
macht's eben bumms, ohne daß dein Compiler dir dabei helfen kann.

von Dr. Sommer (Gast)


Lesenswert?

Alternativer Ansatz: Gib dem Userprogramm eine normale Headerdatei mit:
1
void foo (int blub);
2
int bar (char x, float y);
3
...
Sowie ein Linkerscript, das dem Userprogramm beim Linken mit "-T foo.ld" 
übergeben wird, in dem steht:
1
foo = 0xfff0;
2
bar = 0xfff3;
3
...
Vorteil: Der C-Code bleibt sauber, man sieht dort nichts davon wie die 
Funktionen tatsächlich angesprochen werden, und es funktioniert 
wunderbar mit unterschiedlichen Rückgabetypen und Parametertypen.

von Christian J. (Gast)


Lesenswert?

Karl Heinz schrieb:
> Du definierst dir eben entsprechende Funktionspointer.
> Das hiertypedef void (*func_ptr)(void);
> war ein Zeiger auf eine Funktion, die keine Argumente übernimmt und
> nichts liefert.

>>Jetzt legst du dir noch ein Object von dieser Struktur an und versorgst
>>die entsprechenden Funktionspointer mit den Adressen der Einsprungpunkte
>>und du bist auf dem Weg.

Das "Versorgen" soll automatisch sein, d.h. wenn es geht soll das Objekt 
gleich mit erzeugt werden und die richtigen Adressen enthalten. Das .hex 
File enthält dann direkt die brennfertige Tabelle.

Dank dir erstmal herzlich!

Nicht vergessen: Es handelt sich hier um zwei völlig unabhängige 
Projekte:
Das ROM Projekt, was das Interface zu seinen Routinen bereit stellt und 
das User Projekt, was das Interface nutzen muss (Einbinden eines Headers 
wie eine API). Und diese beiden Projekte will ich eben über diese 
Verrenkungen zusammenbringen.

Syntax für ROM:
(Sprungtabelle)

Syntax für Userprogramm:
(Benutzung der Tabelle, Adressen kann ich händisch raussuchen)

Der Sprung aus dem Userprogramm ins ROM wird indirekt durchgeführt, in 
der Tabelle steht ja nur die Adresse der BIOS Funktion. Der Stack muss 
entsprechend geladen sein mit den Parametern.

Der Compiler ist der SDCC, hoffe mal der kann das.

@Markus.F: Dank Dir, probiere ich auch mal aus.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Dr. Sommer schrieb:
> Alternativer Ansatz: Gib dem Userprogramm eine normale Headerdatei mit:
>
1
void foo (int blub);
2
> int bar (char x, float y);
3
> ...
> Sowie ein Linkerscript, das dem Userprogramm beim Linken mit "-T foo.ld"
> übergeben wird, in dem steht:
>
1
foo = 0xfff0;
2
> bar = 0xfff3;
3
> ...

Das braucht's garnicht.

Das einzige, was notwendig ist, ist die Tabelle an die richtige Stelle 
(Section) zu lokatieren.  Die Symbole werden dann vom Linker zu 0xfff0 
etc. aufgelöst.

von Dr. Sommer (Gast)


Lesenswert?

Johann L. schrieb:
> Das einzige, was notwendig ist, ist die Tabelle an die richtige Stelle
> (Section) zu lokatieren.
Wenn alle Symbole direkt aufeinanderfolgen und dem User-Code-Linker die 
Größe der Sections bekannt sind, ja. Wenn man dem User die Binaries für 
die Funktionen aber nicht geben will, geht das nicht.

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.