Ich möchte in meinem uC Programm einen Jump machen, der an den absoluten
Anfang des Programmes geht (vor Main).
Wenn ich im Debugger schaue, dann gibt es eben vor Main noch das Label
"__program_start". -> siehe Bild im Anhang
Also müsste es doch so gehen, tut es aber nicht:
1
asm("JMP __program_start");
weil ja z.B. zum Main jumpen funktioniert so
1
asm("JMP main");
Wie könnte ich das in C realisieren, dass ich von einer beliebigen
Funktion her an den "Program_start" springen kann.
Hallo,
auf welche Startadresse ist das Programm denn gelinkt? Ist das E044?
Um welchen Prozessor und Compiler handelt es sich eigentlich?
Prinzipiell kannst du per JMP einfach an eine Adresse springen, wenn du
die nich selbst eingeben willst, muss der Compiler das verwendete Symbol
kennen. Main kennt er, weil es im Quelltext steht, __program_start ist
etwas, das nicht im Quelltext steht, sondern ein intern verwendetes
Symbol.
Bei einem AVR kann man einfach an Flashend+1 springen, das ist normal
der absolute Programmanfang.
Grüße,
Peter
Also Compiler ist IAR V4.11, Prozessor MSP430
Ja, ist E044, siehe Bild oben.
Also meinst du ich kann einfach die E044 Adresse angeben, in etwa so?
asm("JMP 00E044");
Alles was vor main() steht, sind Initialisierungen, die der compiler
automatisch einfügt (Stackpointer initialisieren, Variablen nullsetzen,
etc.).
Um genau dorthin zu springen, musst Du den Reset-Vektor anspringen, was
relativ einfach zu bewerkstelligen ist, wenn man einen PUC auslöst. Dazu
kannst Du z.B. einfach auf das WDT-Register ohne Passwort zugreifen!
Ach ja, noch was... hat zwar nichts mit Deinem Problem zu tun, ist aber
vielleicht für manche trotzdem interessant zu wissen.
In dem Assembler-Listing sieht man schön eine "Unart" des IAR-Compilers,
der main() per call aufruft. D.h. ohne dass man selbst irgendwas gemacht
hat, sind schon Mal 2Bytes vom Stack weg, weil die Rücksprungadresse
gesichert wird! Das kann bei kleineren MSPs mit nur 128 Byte RAM
manchmal schon weh tun!
Ausserdem haben die IAR's für ganz faule gleich noch eine Endlosschleife
in call #exit spendiert, falls man das nicht selbst in main() macht...
für mich eigentlich auch Codeverschwendung! Ich habe deshalb meinem IAR
durch ein benutzerdefiniertes _cstartup "beigebracht" diese
Verschwendungen zu unterlassen ;-)
Aber wenn man ja an diese Adresse 00E044h springt, dann werden dort die
Initialisierungen vorgenommen.
Werden da auch die Variablen alle nullgesetzt?
Weil die Variablen sollten nicht verändert werden.
Es geht einfach darum vor das main zu springen, damit der Program
Counter wieder am Anfang steht und damit 0 ist und der Stack neu
initialisiert wird.
Die Variablen aber sollen ihren Wert beibehalten.
Kann man denn nicht einfach irgendwie zur Adresse 00E044h springen?
asm("JMP 00E044h");
Das funktioniert leider nicht!
>Aber wenn man ja an diese Adresse 00E044h springt, dann werden dort die>Initialisierungen vorgenommen.>Werden da auch die Variablen alle nullgesetzt?
Schau Dir mal das Assembler-Listing an:
Zuerst wird der Stackpointer initialisiert
1
mov.w#0x400,SP
Danach werden die Variablen "genullt"
1
call#__data_16_memzero
Wenn Du Variablen hast, die nicht initialisiert werden sollen, dann
musst Du sie so definieren (oder deklarieren, oder was auch immer...)
1
__no_initintn_blablubb;
>Kann man denn nicht einfach irgendwie zur Adresse 00E044h springen?
Bringt ja nix, s.o.
Also macht man einfach vor die zu schützenden Variablen "__no_init" und
könnte dann ja zur Adresse 00E044h springen oder?
Doch wie sieht der Code dafür aus?
>Also macht man einfach vor die zu schützenden Variablen "__no_init"
JA
>könnte dann ja zur Adresse 00E044h springen oder?
würde ich nicht machen, da sich diese Adresse durch Programm- oder
Optimierungsänderungen verschieben könnte.
Wie schon gesagt, würde ich einen Software-Reset (PUC) auslösen, in dem
Du auf das WDT-Register ohne Passwort zugreifst.
Stefan wrote:
> In dem Assembler-Listing sieht man schön eine "Unart" des IAR-Compilers,> der main() per call aufruft.
Das ist keine ,,Unart'', sondern normales C. main() ist eine normale
Funktion, die gemäß dem Standard auch rekursiv aufrufbar ist. Wenn
die initial gerufene Funktion main() zurückkehrt, so ist laut Standard
mit ihrem Rückkehrwert als Parameter die Funktion exit() zu rufen.
Um dem Compiler mitzuteilen, dass in diesem Falle main() eine
Sonderfunktion einnimmt und nicht gerufen werden muss, gibt's
compilerspezifische Erweiterungen. Ich benutze den IAR zwar nicht,
kann mich aber daran erinnern, in IAR-Code schon mal das Schlüsselwort
__C_task (oder so ähnlich) in der Deklaration/Definition von main()
gesehen zu haben. Den Rest sollte dir dein freundliches Compiler-
Handbuch verraten.
Jörg schrieb:
>Das ist keine ,,Unart'', sondern normales C.
Deshalb hab ich ja auch Anführungszeichen benutzt!
Dass der Compiler C-konformen Code erzeugt, habe ich fast schon
befürchtet ;-)
Jedoch stellte sich bei mir bislang nie das Problem ein, dass ich main()
hätte rekursiv aufrufen müssen, noch wird es das in Zukunft tun.
Deshalb finde ich das Code- und RAM-Verschwendung und habe es eben nach
meinen Bedürfnissen abgeändert (was übrigens auch im Handbuch
beschrieben ist).
Aus reiner Neugier hab ich mal die extended keywords gesucht, die nach
Deiner Meinung helfen könnten:
__task:
"Functions declared __task do not save any registers, and therefore
require less stack space. Such functions should only be called from
assembler routines."
Heißt für mich nur, dass keine Register gesichert werden. Die
Rücksprungadresse des calls jedoch schon!
__noreturn:
"The __noreturn keyword can be used on a function to inform the compiler
that the function will not return."
Könnte vielleicht schon eher hinkommen, hab's aber nicht ausprobiert!
Im Übrigen ist es bei der µC-Programmierung mit der C-Konformität so
eine Sache... wenn ich schon nicht c-konforme extended keywords
verwenden kann ;-)
>Beim Debuggen "hängt" er sich bei dieser Zeile dann auf, bzw. bleibt im>data_memory (siehe Bild) stecken.
Nun, das Auslösen eines PUC ist für den Debugger sicher kein normaler
Betriebszustand, wodurch ein Absturz durchaus möglich, vermutlich sogar
sicher ist.
>Hier kann ich den Assembler Code leider nicht in meinem C-File einbinden.
Wie wär's mit
1
asm{"mov #0xFFFE,R12"}
2
asm{"mov @R12,PC"}
oder so ähnlich?
Oder:
ASM-Listing in ein .s43 File speichern und ins Projekt einbinden.
In main.c dann:
Vielen Dank das funktioniert.
Aber ich verstehe nicht ganz was denn jetzt gemacht wird (Habe null
Ahnung von Assembler).
Also es wird doch quasi einfach dem Program Counter einen neuen Wert
übergeben, oder?
Wenn ja, warum genau 0xFFFE?
An 0xFFFE-0xFFFF liegt der Reset-Vektor.
Diese Adresse wird in R12 geladen.
1
mov@R12,PC
indizierte Adressierung: Der Inhalt des Resetvektors wird dem
Programm-Counter übergeben. Das ist der Wicht, der bestimmt, an welcher
Stelle das Programm ausgeführt wird ;-)