Forum: Mikrocontroller und Digitale Elektronik Jump to Program Start - Assembler bzw. C


von K. Sehler (Gast)


Angehängte Dateien:

Lesenswert?

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.

von Peter Diener (Gast)


Lesenswert?

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

von K. Sehler (Gast)


Lesenswert?

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");

von Stefan (Gast)


Lesenswert?

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!

von Stefan (Gast)


Lesenswert?

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 ;-)

von K. Sehler (Gast)


Lesenswert?

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!

von Stefan (Gast)


Lesenswert?

>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_init  int   n_blablubb;

>Kann man denn nicht einfach irgendwie zur Adresse 00E044h springen?
Bringt ja nix, s.o.

von K. Sehler (Gast)


Lesenswert?

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?

von Stefan (Gast)


Lesenswert?

>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.
1
WDTCTL = 0;    // Software-Reset (PUC wg. fehlendem Passwort!)
Der PUC startet Dein Programm immer von der richtigen Startadresse.

Alternativ eine kleine ASM-Routine, die den Reset-Vektor aufruft:
1
  NAME    sw_reset         ; 
2
  
3
  PUBLIC   software_reset  ; Declare symbol to be exported
4
  RSEG CODE                ; Code is relocatable  
5
  
6
software_reset;            
7
      
8
  mov  #0xFFFE,R12         ; Reset-Vector in R12
9
  mov  @R12,PC             ; Load PC (indexed) with Reset-Vector
10
  
11
END

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Stefan (Gast)


Lesenswert?

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 ;-)

von K. Sehler (Gast)


Angehängte Dateien:

Lesenswert?

@stefan

Leider funktionieren beide varianten nicht wirklich, abgesehen vom 
__no_init, welches super funktioniert.

1
WDTCTL = 0;
Beim Debuggen "hängt" er sich bei dieser Zeile dann auf, bzw. bleibt im 
data_memory (siehe Bild) stecken.

1
  NAME    sw_reset         ; 
2
  
3
  PUBLIC   software_reset  ; Declare symbol to be exported
4
  RSEG CODE                ; Code is relocatable  
5
  
6
software_reset;            
7
      
8
  mov  #0xFFFE,R12         ; Reset-Vector in R12
9
  mov  @R12,PC             ; Load PC (indexed) with Reset-Vector
10
  
11
END
Hier kann ich den Assembler Code leider nicht in meinem C-File 
einbinden.
Ich habe es schon folgendermassen probiert:
1
asm("routine");
1
asm{routine}

Der Compiler meldet dann einen Fehler.

von Stefan (Gast)


Lesenswert?

>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:
1
extern void software_reset(void);
2
3
void main(void)
4
{
5
...
6
software_reset();
7
...
8
}

von K. Sehler (Gast)


Lesenswert?

Hallo Stefan
1
asm("mov  #0xFFFE,R12");
2
asm("mov  @R12,PC");

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?

von Stefan (Gast)


Lesenswert?

1
mov  #0xFFFE,R12
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 ;-)

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.