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
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 ?
> 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.
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
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.
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.
@ 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
@ 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.
@ 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
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.
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
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
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
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
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
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
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.
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
@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
> 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.
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
> 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!
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.
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))))
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.