Forum: Mikrocontroller und Digitale Elektronik Absolute Funktionsadressen in Tabelle ablegen


von A.S. (Gast)


Lesenswert?

Hallo Leute,

Ich benötige in meinem Design eine Tabelle mit den absoluten Adressen 
von mehreren Funktionen. Die Tabelle selbst soll konstant sein und wird 
im ROM abgelegt.

Beispiel:

funcA() // Funktion liegt z.B. auf 0x1000 im ROM
funcB() // Funktion liegt z.B. auf 0x2001 im ROM


const unsigned char table [][] = {{0x10, 0x00};
                                  {0x20, 0x01};

Ich würde nun die Tabelle gerne folgendermaßen anlegen:

const unsigned char table [][] = {{&funcA + 1, &funcA},
                                  {&funcB + 1, &funcB};

Das "+ 1" soll das high - byte der adresse darstellen (ist mir schon 
klar dass sich obiges nicht übersetzen lässt).

Da sich die Adressen nach jedem Linker - vorgang ändern würde ich schon 
gern irgendwelche Makros oder #defines verwenden und die Funktionen 
nicht auf feste adressen legen.

Ich habs schon versucht wenn ich den daten - typen der tabelle weglasse, 
aber das ist nicht akzeptabel.
const table [][] = {{&funcA}, {&funcB};


Kennt jemand eine brauchbare oder ähnliche lösung?

PS: Ich verwende Keil und einen 80C51.

lg. _AS

von Peter D. (peda)


Lesenswert?

Erzähl dochmal, was das werden soll.

Klingt stark nach Sackgasse.

Du kannst nicht erwarten, wenn Du 2 verschiedene Programme schreibst, 
daß die sich gegenseitig aufrufen können ohne zu crashen. Denn das eine 
kennt nicht die SRAM-Benutzung des anderen.

Wenn es aber nur ein Programm ist, dann interessiert keine Sau die 
absolute Adresse, denn der Linker löst alles auf.


Peter


P.S.:
Wer arbeitet denn heutzutage noch mit ROM ?

von Karl H. (kbuchegg)


Lesenswert?

> Kennt jemand eine brauchbare oder ähnliche lösung?

Jedes C-Buch kennt eine: Funktionszeiger.

Falls du kein C-Buch hast, kannst du alternativ auch mit
der Kurzfassung vorlieb nehmen (*)
http://www.mikrocontroller.net/articles/FAQ#Funktionszeiger


(*) und dir dann anschliessend ein C-Buch kaufen gehen.

von A.S. (Gast)


Lesenswert?

Hallo,

@Peter:
Wer spricht denn hier von gegenseitig aufrufen ?

Ich hätte gerne eine Tabelle mit folgendem Eintrag:

const unsigned char table [][] = {{0x10, 0x00}; ... // Für die 1. Fkt 
z.B.

Natürlich könnte ich den Funktionpointer selbst auch ablegen, nur dann 
würde die tabelle so aussehen:

const unsigned char table [][] = {{0x1000}; ...

Nur - das will ich nicht.
Sinn der Sache ist, dass ich diese Tabelle danach in Assembler anspringe 
und da diverse register (DPH und DPL) mit dem high - und low byte 
befüllen muss.
Darum ja auch meine Frage, ob ich die konstanten ausdrücke (die sich 
nach jeder veränderung des programms ändern) durch entsprechende Makros 
ersetzen lassen.

> Wenn es aber nur ein Programm ist, dann interessiert keine Sau die absolute 
Adresse, denn der Linker löst alles auf.

Das seh ich etwas anders.

> Wer arbeitet denn heutzutage noch mit ROM ?
Es hat nicht jeder einen ATMega128 mit unendlich viel Flash zur 
verfügung.

@Karl Heinz:
Also ich glaube nicht, dass deine Antwort irgendetwas mit meiner Frage 
zu tun hat. Hätte man sich auch sparen können.

Trotzdem, danke für die Antworten.

lg. AS

von Karl H. (kbuchegg)


Lesenswert?

A.S. wrote:

> @Karl Heinz:
> Also ich glaube nicht, dass deine Antwort irgendetwas mit meiner Frage
> zu tun hat. Hätte man sich auch sparen können.

Dann formulier die Frage um.
Ich denke nämlich immer noch, dass eine Tabelle
aus Funktionspointern genau das ist, wonach du suchst.

Zumindest passt sie wie die Faust aufs Auge auf
dein bisheriges Anforderungsprofil:

void funcA()
{
}

void funcB()
{
}

typedef void (*Fnct)();

const Fnct table[] = { funcA, funcB };

und schon hast du eine schöne Tabelle, in der die
Speicheradressen der Funktionen funcA und funcB gesammelt
sind.

Aber bitte: Wenn du was anderes willst, dann sags ruhig.

von A.S. (Gast)


Lesenswert?

Hi

Ich möchte nicht den Funktionspointer selbst in der Tabelle speichern, 
sondern die absolute Adresse der Funktion - aufgeteilt in high - und low 
byte.
Wie gesagt, ich verwende die Tabelle später in Assembler und nicht in C.


Bitte keine Diskussionen ob das Sinn macht oder nicht - wenn einer 
irgendwelche Präprozessor/Compiler/Linker - Makros kennt, die mir helfen 
- bitte gerne. Alles andere ist nutzlos.

Vielen dank.

von Falk B. (falk)


Lesenswert?

@ A.S.

>Ich möchte nicht den Funktionspointer selbst in der Tabelle speichern,
>sondern die absolute Adresse der Funktion

Ist das nicht das Gleiche?

>Wie gesagt, ich verwende die Tabelle später in Assembler und nicht in C.

Assembler-Teile in einem C-Programm?

MFG
Falk

von A.S. (Gast)


Lesenswert?

@ Falk

> Ist das nicht das Gleiche?
Nein, denn ob ich in der Tabelle: 0x1000 oder 0x10,0x00 stehen habe, 
macht einen Unterschied -> zumindest in Assembler.


von Falk B. (falk)


Lesenswert?

@ A.S.

>> Ist das nicht das Gleiche?
>Nein, denn ob ich in der Tabelle: 0x1000 oder 0x10,0x00 stehen habe,
>macht einen Unterschied -> zumindest in Assembler.

Bisschen mädchenhaft heute?
DAS sollte sich wohl leicht herausfinden lassen und entsprechend 
handhaben. Das händische Gemurkse mit irgendwelchen absoluten 
Speicheradressen von Funktionen wird dich wochenlang beschäftigen.

MFG
Falk



von Karl H. (kbuchegg)


Lesenswert?

Falk Brunner wrote:
> @ A.S.
>
>>Ich möchte nicht den Funktionspointer selbst in der Tabelle speichern,
>>sondern die absolute Adresse der Funktion
>
> Ist das nicht das Gleiche?

Ein Funktionspointer IST die absolute Adresse einer Funktion.
Zumindest auf einem AVR.
Die Tabelle, so wie ich sie konstruiert habe, enthält
die absoluten Adressen der Funktionen und nachdem auf einem
AVR eine Adresse aus 2 Bytes besteht enthält sie High-Byte
und Low-byte.

> Nein, denn ob ich in der Tabelle: 0x1000 oder 0x10,0x00 stehen habe,
> macht einen Unterschied -> zumindest in Assembler.

Du wirst ja wohl noch in Assembler 2 Bytes aus dem
Speicher lesen können, wenn du die Adresse der Tabelle
hast.

> wenn einer irgendwelche Präprozessor/Compiler/Linker - Makros kennt,
> die mir helfen - bitte gerne. Alles andere ist nutzlos.

Warum willst du immer auf irgendwelche Makros hinaus?
Ein Funktionspointer-Tabelle ist genau das was du willst:
Eine Tabelle in der die Startadressen der Funktionen stehen
die du in der Initialisierung anführst. Lass dich doch nicht
so zu deinem Glück zwingen. Noch einfacher und simpler geht
es nicht in C.

von Andreas K. (oldcoolman)


Lesenswert?

Hallo A.S.

falls das so funzt bei dir, denn erklär mir mal wie das so
geht. ich möchte nämlich bestehende assembler routinen in
c linken und weiss nicht wie man parameter hin und her übergibt.
bin in c Anfänger.

Danke im voraus.

nette Grüße

Andreas

von Peter D. (peda)


Lesenswert?

A.S. wrote:

> const unsigned char table [][] = {{0x10, 0x00}; ... // Für die 1. Fkt
> z.B.
>
> Natürlich könnte ich den Funktionpointer selbst auch ablegen, nur dann
> würde die tabelle so aussehen:
>
> const unsigned char table [][] = {{0x1000}; ...

Wenn schon denn schon:
1
unsigned int code table[] = { 0x1000, 0x2000 ... };

Wenn Du nicht mal weißt, daß der Compiler intern 16 Bit Werte als 2 
Bytes ablegt, dann wird das eh nichts, egal, was es werden soll.

Wenn Du die Tabelle aber eh in Assembler benutzen willst, leg sie doch 
auch in Assembler an:
1
table: dw 01000h, 02000h usw.

Wie man dann die beiden Bytes nach DPTR schaufelt, dürfte für jeden 
Assemblerfreak klar sein.


> Darum ja auch meine Frage, ob ich die konstanten ausdrücke (die sich
> nach jeder veränderung des programms ändern) durch entsprechende Makros
> ersetzen lassen.

Was denn nun, erst konstante Zahlen, nun wieder veränderbar ?

Veränderbare Dinge macht der Linker für Dich, Du mußt ihn einfach bloß 
lassen.


>> Wer arbeitet denn heutzutage noch mit ROM ?
> Es hat nicht jeder einen ATMega128 mit unendlich viel Flash zur
> verfügung.

Hä ?
Ich denke Du nimmst nen 8051, was soll nun der AVR dabei ?
Dein Worte werden immer verwirrter.
Ich setze auch 8051 ein, aber eben Flash und nicht ROM. Und vorzugsweise 
die mit internem Bootloader (AT89C51CC01).


Mein Tip:

Wenn Du keine Lust hast, das Compilerhandbuch und die Keil Knowledgebase 
zu lesen, wie und in welchem Speichermodell Aufrufe und 
Parameterübergaben von Assembler nach C und umgekehrt zu erfolgen haben, 
dann laß es lieber. Es wird nicht das geringste dabei rauskommen.

Außerdem müssen die Assemblerobjekte C-freundlich geschrieben sein 
(keine festen Adressen und Variablen, Export aller Namen nach den 
Linker-Regeln, gleiche Byteorder wie im C-Programm).


Meine Erfahrung:

Die Fälle, wo Mixen Assembler mit C bei mir was gebracht hat, kann ich 
an null Fingern abzählen.
Ich hatte es mal probiert und dann später auf nur C geändert ohne den 
geringsten Nachteil.
Ich hatte es dann auch so gemacht, wie in der Knowledgebase empfohlen, 
d.h. eine Dummyfunktion von C nach Assembler compilieren lassen und dann 
den Assembler dort eingefügt.
Dann sind alle Funktionen und Variablen schon C-konform.


Peter

von Peter D. (peda)


Lesenswert?

A.S. wrote:
> Nein, denn ob ich in der Tabelle: 0x1000 oder 0x10,0x00 stehen habe,
> macht einen Unterschied -> zumindest in Assembler.

Darf ich mal laut lachen ?

In Assembler ist es nämlich kein Unterschied:
1
LOC  OBJ            LINE     SOURCE
2
3
                       1
4
                       2
5
0000 1020              3             db 010h, 020h
6
0002 1020              4             dw 01020h

Bevor man Unsinn behauptet, sollte man es sich erstmal anschauen.

Peter

von A.S. (Gast)


Lesenswert?

guten abend,

Wie ich sehe wurde hier schon ziemlich viel gepostest, aber leider wurde 
meine Frage immer noch nicht beantwortet.

Ich wills also nochmal versuchen.
Ich will auch noch mal klar stellen, dass ich hier nicht auf 
Verbesserungsvorschläge aus bin. Klar gibt es bessere Controller mit 
Flash statt ROM, klar kann ich den Funktionspointer (= absolute Adresse) 
direkt in der Tabelle ablegen,klar kann ich in Assembler danach die 
Bytes der Adresse aus der Tabelle lesen, klar kann man eine Mischung aus 
C und Assembler hinterfragen uvw. Da könnte man ewig weitermachen, ist 
aber absolut sinnlos und verschwendet Zeit. Wer auch immer antwortet, 
bitte nehmt meine Aufgaben - bzw. Problemstellung als gegeben hin.


Tatsache ist, es gibt in der Keil - Knowledge base das folgende Makro 
bzw. #define.

#define l_byte(v)  (*(((unsigned char *) (&v))))
#define h_byte(v)  (*(((unsigned char *) (&v) + 1)))

Mit dieser anweisung kann in C während der laufzeit den inhalt eines 
pointers in 8 - bit variablen aufteilen. (Ohne casten mit zusätzlichen 
schiebeoperationen). Das funktioniert allerdings nur für pointer die auf 
daten zeigen, d.h. nicht auf funktionen im code - bereich.

Ich brauch nun sowas für funktionen um zur compile - zeit bzw. während 
des linkens eine Tabelle zu befüllen (einzelne 8 - bit werte, nicht die 
ganze adresse auch wenn unterm strich das gleiche rauskommt). Ich möchte 
nur wissen, ob es diese möglichkeit gibt. Falls ja - toll, wenn nicht - 
pech gehabt.

@Peter
> Wenn Du keine Lust hast, das Compilerhandbuch und die Keil Knowledgebase
>zu lesen, wie und in welchem Speichermodell Aufrufe und
>Parameterübergaben von Assembler nach C und umgekehrt zu erfolgen haben,
>dann laß es lieber. Es wird nicht das geringste dabei rauskommen.

> A.S. wrote:
> Nein, denn ob ich in der Tabelle: 0x1000 oder 0x10,0x00 stehen habe,
> macht einen Unterschied -> zumindest in Assembler.

> Darf ich mal laut lachen ?

Ich mein, was soll das ? Ich stell hier nur eine Frage, wenn für dich 
das ganze lächerlich ist - dann laß es bitte sein. Es hat hier 
wahrscheinlich nicht jeder so einen enormen Wissensstand wie du. Die 
Keil - Knowledgebase ist sicher extrem umfangreich, wie auch das Keil - 
Handbuch selbst. Aber in diesem Fall hab ich keine Lösung gefunden, 
darum hab ich mich auch an das Forum gewendet. Könnte ja sein, dass 
irgend jemand irgendeinen Trick kennt, um so eine Aufgabenstellung wie 
ich sie habe, zu lösen.


Bitte nochmal, ich weiss dass man das ganze auch anders machen kann - 
aber ich will es eben so wie oben beschrieben machen.

lg.
A.S

von Karl H. (kbuchegg)


Lesenswert?

Wem nicht zu helfen ist ...

von fnah (Gast)


Lesenswert?


von Roland P. (pram)


Lesenswert?

ich behaupte jetzt mal das geht nicht
(ok ich kenn mich nicht sooo toll aus, falls das nicht stimmt was ich 
schreibe, dürft ihr mich korrigieren),
aber die Tabelle willst du in C definieren und die Adressen müssten 
somit schon zur Compilezeit bekannt sein, was aber nicht der Fall ist, 
weil diese u.U. erst beim Linken bekannt werden und der Linker ja keine 
Daten ändert.

Du könntest jetzt entweder mit irgendwelchen Makros dem Linker die 
Adressen der einzelnen Funktionen vorschlagen (keine Ahnung obs dafür 
Makros gibt, ausserdem wär das eine ziemliche Holzhammer-Methode)

eine weitere Möglichkeit die mir noch einfällt, wäre eine Sprungtabelle 
an einer definierten Stelle abzulegen, z.B. mit _attribute_ ( 
section... ) (Wieder keine Anhung ob das geht) und von dieser Tabelle 
dann die einzelnen Funktionen anspringen.
Allerdings hat man dann bei einem Funktionsaufruf einen longjmp mehr

Gruß
Roland

von Peter D. (peda)


Lesenswert?

A.S. wrote:

> Ich mein, was soll das ? Ich stell hier nur eine Frage, wenn für dich
> das ganze lächerlich ist - dann laß es bitte sein.

Ich wollte Dir nur aufzeigen, daß der große Unterschied, auf dem Du die 
ganze Zeit drauf rumreitest, garkeiner ist.

Darum dürfte sich auch keiner die Mühe gemacht haben, für eine 
Nichts-tun-Funktion ein extra Macro zu schreiben.

Das einzige, was man mit dieser Funktion erreichen könnte:
Man könnte damit die Byteorder gegenüber der Defaultvorgabe des 
Assemblers und Compilers umdrehen, damit man noch mehr Fehlerquellen in 
seinem Programm hat.


Und wenn Du nicht mal das Assemblerlisting verstehst, solltest Du umso 
mehr die Finger davon lassen.


Peter

von Uhu U. (uhu)


Lesenswert?

Du brauchst keine Macros, um die Funktionsadresse in eine Tabelle zu 
speichern - das ist das täglich Brot eines jeden Compilers und das 
beherrscht der sozusagen im Schlaf.

Nimm einfach ein Array von Funktionspointern, wie oben schon 
beschrieben, oder ein Array von void pointern, falls die Funktionen 
unterschiedliche Signaturen haben und initialisiere es mit den 
Funktionspointern.

Jedesmal, wenn sich durch Änderungen an Deinem Programm irgendwelche 
Funktionsadressen ändern, trägt der Compiler automatisch die neue 
Adresse in die Tabelle ein.

In Deinem Assemblerprogramm greifst Du dann einfach indiziert in die 
Tabelle, um die jeweils gültige Funktionsadresse zu bekommen.

Solange die Reihenfolge der Funktionen in der Tabelle nicht verändert 
wird, findet das Assemblerprogramm immer den richtigen Wert - 
vorausgesetzt es weiß, wo es die Tabelle findet.

@ Alle, die meinen, das sei absoluter Murks, was A.S. da vor hat:
Ihr seid auf dem Holzweg. Das ist eine Methode, wie sie auf Eurem PC 
täglich Millionenfach angewandt wird: Nämlich bei jedem Funktionsaufruf 
in eine DLL.

von Peter D. (peda)


Lesenswert?

Uhu Uhuhu wrote:

> @ Alle, die meinen, das sei absoluter Murks, was A.S. da vor hat:

Nö, Funktionspointer sind kein Murx, nehme ich oft, z.B. für nen 
Kommandointerpreter.

Bloß die sture Rumreiterei auf der Schreibweise als einzelne Bytes ist 
Murx.
In ner 8Bit-Maschine sind alles nur Bytes.
Und wie nun 2 oder mehrere aufeinanderfolgende Bytes interpretiert 
werden, hängt allein vom Programm ab und nicht davon, wie man sie 
hinschreibt.


Peter

von Falk (Gast)


Lesenswert?

@Uhu Uhuhu

>Nimm einfach ein Array von Funktionspointern, wie oben schon
>beschrieben, oder ein Array von void pointern, falls die Funktionen
>unterschiedliche Signaturen haben und initialisiere es mit den
>Funktionspointern.

Das wollten ihm ein halbes Dutzend Leute bereits verklickern. Er ist 
aber etwas begriffstutzig.

>@ Alle, die meinen, das sei absoluter Murks, was A.S. da vor hat:
>Ihr seid auf dem Holzweg. Das ist eine Methode, wie sie auf Eurem PC
>täglich Millionenfach angewandt wird: Nämlich bei jedem Funktionsaufruf
>in eine DLL.

Na ein Glück dass DU uns aufklärst!

MFG
Falk

von Uhu U. (uhu)


Lesenswert?

> Bloß die sture Rumreiterei auf der Schreibweise als einzelne Bytes ist
> Murx.

Finde ich nicht. A.S. ist weniger ein Murkser, als ein Anfänger, der 
eben mit gewissen Problemen zu kämpfen hat...

Murkser sind Fortgeschrittene, die eigentlich keine Lust haben.

von Peter D. (peda)


Lesenswert?

Uhu Uhuhu wrote:

> Finde ich nicht. A.S. ist weniger ein Murkser, als ein Anfänger, der
> eben mit gewissen Problemen zu kämpfen hat...

Das war auch mein Eindruck und genau deshalb rate ich ab, Assembler und 
C zu mixen.
Wer das vorhat, muß beides gut beherrschen.

Wer aber irgendwann C leidlich gut kann, merkt plötzlich, daß Mixen mit 
Assembler nicht mehr nötig ist.


Peter

von Uhu U. (uhu)


Lesenswert?

> Wer aber irgendwann C leidlich gut kann, merkt plötzlich, daß Mixen mit
> Assembler nicht mehr nötig ist.

Daß das nicht mehr nötig ist, kann man so allgemein nicht sagen.

Auf jeden Fall ist es für jeden C-Programmierer eine sehr wertvolle 
Erfahrung, wenn er das Assemblerhandwerk gut beherrscht.

Deswegen A.S.: Laß Dich nicht ins Bockshorn jagen! - Aber nimm Dir auch 
nicht gleich zu Beginn zu viel vor!

von Karl H. (kbuchegg)


Lesenswert?

Peter Dannegger wrote:

> Bloß die sture Rumreiterei auf der Schreibweise als einzelne Bytes ist
> Murx.

Wobei sich natürlich die Frage stellt: Warum will er das
überhaupt unbedingt als einzelne Bytes in die Tabelle
schreiben? Den einzigen Grund den ich mir vorstellen könnte,
ist es die Byteorder zu kontrollieren. Auf der anderen
Seite: Wenn ich das Auslesen sowieso unter Kontrolle habe,
noch dazu in Assembler, in dem die Reihenfolge nun wirklich
keine Rolle spielen sollte, ist das ein 0-Argument.

von Uhu U. (uhu)


Lesenswert?

Nachdem jetzt die Frage, wie mit Funktionspointern umgegangen werden 
kann, geklärt ist, ist noch eine weitere offen:

Wie kann man einen Funktionspointer in einzelne Bytes zerlegen?

Auch wenn man das in der Praxis eigentlich nie braucht, kann man aus der 
Antwort etwas über C lernen.

Für Datenadressen funktionieren die von A.S. geposteten Macros

#define l_byte(v)  (*(((unsigned char *) (&v))))
#define h_byte(v)  (*(((unsigned char *) (&v) + 1)))

Warum gehen sie mit Funktionsadressen nicht?

Ganz einfach: Wenn v ein Funktionspointer ist, dann ist das eine 
Konstante, wie z.B. 43 - und Konstanten haben keine Adressen.

    l_byte(43)

erzeugt denselben Syntaxfehler, wie

    l_byte(funcA)

Man muß also den Adressoperator & vor dem v weglassen, denn der gibt die 
Adresse - ebenfalls eine Konstante - seines Operanden zurück:

    #define const_l_byte(v)  (*(((unsigned char *) (v))))

von Peter D. (peda)


Lesenswert?

Hier mal ein Beispiel für Funktionspointer aufm 8051:
1
typedef unsigned char u8;
2
3
typedef u8(code *u8funcv)(void);        // function pointer,
4
                                        // input: void,
5
                                        // return: unsigned char
6
7
8
u8 func0(void);                         // some external functions
9
u8 func1(void);
10
u8 func2(void);
11
12
13
u8funcv code func_table[] = {           // table of function pointers
14
        func0,                          // in code memory segment
15
        func1,
16
        func2
17
};
18
19
20
u8 execute( u8 func_no )
21
{
22
  if( func_no >= sizeof(func_table) / sizeof(u8funcv))
23
    return 255;                                         // out of table
24
25
  return func_table[func_no]();                         // do it
26
}


Dazu muß man wissen, daß der 8051 verschiedene Memory-Segmente hat.
Pointer ohne Memory Specifier werden als generic Pointer angelegt 
(3Byte).
Daten ohne Memory Specifier werden im default Data Memory abgelegt (je 
nach Memory Model).

Das "code" im typedef definiert einen Pointer auf den Code-Memory.
Und das "code" in der Tabelle legt die Tabelle nicht im SRAM, sondern im 
Flash an.

Funktionieren tuts auch ohne beides, braucht aber mehr SRAM und Flash.


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.