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?
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.
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.
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)?
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 ;-)
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
typedefvoid(*func_ptr)(void);
war ein Zeiger auf eine Funktion, die keine Argumente übernimmt und
nichts liefert.
Das hier
1
typedefint(*int_func_ptr)(void);
wäre ein Zeiger auf eine Funktion die keine Argumente übernimmt und
einen int liefert.
1
typedefvoid(*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
typedefchar(*char_func_void)(void);
2
typedefvoid(*void_func_char)(char);
3
4
structBIOS_Func_Entries
5
{
6
char_func_voidfunc_getc;
7
void_func_charfunc_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.
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
typedefint(*func_ptr_i_v)(void);/* i_v: int raus, void rein */
2
typedefint(*func_ptr_i_ii)(int,int);/* i_ii: int raus, zwei ints rein */
3
typedefvoid(*func_ptr_v_i)(int);/* usw... */
4
typedefvoid(*func_ptr_v_v)(void);
5
6
unionfunc_ptr{
7
func_ptr_i_vi_v;
8
func_ptr_i_iii_ii;
9
func_ptr_v_iv_i;
10
func_ptr_v_vv_v;
11
};
12
13
func_ptrsprungleiste[]={
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.
Alternativer Ansatz: Gib dem Userprogramm eine normale Headerdatei mit:
1
voidfoo(intblub);
2
intbar(charx,floaty);
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.
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.
Dr. Sommer schrieb:> Alternativer Ansatz: Gib dem Userprogramm eine normale Headerdatei mit:>
1
voidfoo(intblub);
2
>intbar(charx,floaty);
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.
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.