Forum: Mikrocontroller und Digitale Elektronik Wie Assembler-Quellcode (.s) in C-Code ausführen?


von Sercan E. (ergansxn)


Lesenswert?

Hallo, ich habe einen Assembler-Quellcode Read_Fuses.s und möchte diesen 
in einem Quellcode .c einbinden und ausführen. Wie kann ich vorgehen? Im 
C-Code sind verschiedene einzelne Programmteile drin und ich möchte 
meinen Read_Fuses teil mit einbinden der aber in Assembler geschrieben 
ist. Vllt irgendwas mit " .global Hauptprogramm.c " ?

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Warum nimmst Du nicht die fuse.h?

von Oliver S. (oliverso)


Lesenswert?

Einfach getrennt übersetzen und dann zusammenlinken...

Genaueres könnte man sagen, wenn du den Code zeigen würdest, und dazu 
auch noch verrätst, für welchen Prozessor und welche tool chain das sein 
soll.

Oliver

von Sercan E. (ergansxn)


Lesenswert?

Wie genau könnte ich die  da einfügen?

von Peter D. (peda)


Lesenswert?

Sorry, die boot.h:
1
#include <avr/boot.h>
2
3
  uint8_t lo, hi, ex;
4
5
  lo = boot_lock_fuse_bits_get( GET_LOW_FUSE_BITS );
6
  hi = boot_lock_fuse_bits_get( GET_HIGH_FUSE_BITS );
7
  ex = boot_lock_fuse_bits_get( GET_EXTENDED_FUSE_BITS );

von Sercan E. (ergansxn)


Lesenswert?

das ist der Assembler Code:
1
#include <avr\io.h>
2
3
/* Define the size of the flash page if not defined in the header files. */
4
#ifndef FLASH_PAGE_SIZE 
5
#define FLASH_PAGE_SIZE 512
6
#endif /*FLASH_PAGE_SIZE*/
7
8
#define NVM_CMD_READ_FUSES_gc (0x07<<0)  // Read fuse byte
9
10
.section .text
11
.global Hauptprogramm
12
ReadFuseByte:
13
  sts  NVM_ADDR0, r24        
14
  clr  r24                        
15
  sts  NVM_ADDR1, r24              
16
  sts  NVM_ADDR2, r24              
17
  ldi  r20, NVM_CMD_READ_FUSES_gc  
18
  rcall  CommonCMD               
19
  movw  r24, r22                
20
  ret
21
22
.section .text    
23
CommonCMD:
24
  sts  NVM_CMD, r20        
25
  ldi  r18, CCP_IOREG_gc      
26
  ldi  r19, NVM_CMDEX_bm      
27
  sts  CCP, r18          
28
  sts  NVM_CTRLA, r19        
29
  lds  r22, NVM_DATA0        
30
  lds  r23, NVM_DATA1        
31
  lds  r24, NVM_DATA2        
32
  clr  r25          
33
  ret
34
; END OF FILE
Diesen würde ich gerne in einem C-Quellcode ausführen mit void 
READ_FUSES()

: Bearbeitet durch User
von Daniel A. (daniel-a)


Lesenswert?

Dazu muss der verwendete Compiler und der Assembler bekannt sein, diese 
müssen kompatible object files erstellen können. Im assemblerkode müssen 
"Funktionen" den selben Aufrufkonventionen wie der C-Code folgen, das 
heisst alle verwendeten register sichern, reihenfolge der Argumente im 
stack beachten, ob der Aufrufer oder die Funktion die Argumente aus dem 
stack entfernt, etc. Ist alles im ABI Des Compilers zu finden.
Im grunde genommen muss dann nurnoch ein Objektfile aus dem 
Assemblercode erstellt werden, in diesem muss das Funktionssymbol 
vorhanden sein, und dann linkt man es einfach mit Objektfiles zusammen 
die aus dem C-Code generiert worden sind.

von Sercan E. (ergansxn)


Lesenswert?

Das ist mir zu kompliziert erklärt versteh ich leider nicht :(

von Axel S. (a-za-z0-9)


Lesenswert?

Sercan E. schrieb:
> Das ist mir zu kompliziert erklärt versteh ich leider nicht :(

Geh zu http://www.nongnu.org/avr-libc/user-manual/index.html

Lies dort. Am besten alles, mindestens aber:

http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_reg_usage
und http://www.nongnu.org/avr-libc/user-manual/group__asmdemo.html

Jetzt weißt du in welchen Registern der C-Compiler Argumente an 
Funktionen übergibt und in welchen Registern er Rückgabewerte erwartet. 
Jetzt kannst du den Assembler-Code entsprechend schreiben (sofern er 
nicht bereits korrekt ist - auf den ersten Blick sieht er so aus).

Ferner mußt du alle Symbole (Variablen, Funktionen) die von C aus 
sichtbar sein sollen, im Assembler-Code als .global deklarieren. Und du 
mußt dir ein .h File schreiben, in dem die Assembler-Funktionen 
deklariert sind (also Name, Aufrufparameter und Returntyp).

Und wenn du das alles hast, brauchst du in C einfach nur das .h File zu 
#includen und kannst die Funktion von C aus aufrufen. Das Assembler-File 
mußt du natürlich mit übersetzen und das entstandene Objectcode-File mit 
dem Rest zusammenlinken. Nicht trivial, aber machbar.

Wesentlich weniger aufwendig wird das ganze, wenn du den Rumpf der 
Assembler-Funktion einfach als Inline-Assembler in eine C-Funktion 
kopierst. Auch dazu mußt du etwas Doku lesen.

von Sercan E. (ergansxn)


Lesenswert?

OK danke :)

von Oliver S. (oliverso)


Lesenswert?

Sercan E. schrieb:
> Diesen würde ich gerne in einem C-Quellcode ausführen mit void
> READ_FUSES()

Wie Peter schon schrieb, warum?

Peter D. schrieb:
> Sorry, die boot.h:

Wenn du nicht nur einfach die Fuse auslesen möchtest, sondern lernen 
möchtest, wie man Assembler-und C-Programme mixt, dann kannst du das 
gerne tun.

Axel S. schrieb:
> Sercan E. schrieb:
>> Das ist mir zu kompliziert erklärt versteh ich leider nicht :(

Da musst du in dem Fall durch.

Oliver

von Thomas E. (picalic)


Lesenswert?

Also, im AVR-Studio fügst Du die .s-Datei erstmal Deinen 
Projekt-Sourcecodes hinzu. Dann nennst Du das Label genau wie die 
C-Funktion, also hier könntest Du den Code "ReadFuseByte:" mit einer 
C-Funktion "ReadFuseByte()" aufrufen. Die Parameter-Übergabe geht bei 
AVR/GCC über die Register (int16 Über-/Rückgabe hier über R24/R25). Ein 
Byte wird trotzdem in einem Registerpaar übergeben (LSB in R24)
Dann brauchst Du im C-Code noch einen Funktions-Prototypen, z.B.:
"uint8_t ReadFuseByte (void);"
Und mit ".global" gibst Du in der ASM-Datei die Labels an, die Du Global 
bekanntmachen willst, hier also z.B.
".global ReadFuseByte"

Viel Erfolg!

von Bernd K. (prof7bit)


Lesenswert?

Sercan E. schrieb:
> Das ist mir zu kompliziert erklärt versteh ich leider nicht :(

Das liegt daran daß der ganze Sachverhalt und die Zusammenhänge nicht 
ganz trivial sind und sich sicher nicht erschöpfend mit 3 Sätzen 
erklären lässt.

Wenn die Motivation Dich mit dieser Aufgabenstellung zu befassen darin 
besteht das zukünftig als Hobby zu betreiben und auch Freude daran haben 
zu wollen dann wirst Du nicht umhin kommen Dir einen Überblick zu 
verschaffen und wenigstens in groben Zügen zu verstehen was alles vor 
sich geht und wie die Zusammenhänge sind wenn aus all den einzelnen C- 
und Assembler-Dateien am Ende ein lauffähiges Binary herauskommt.

Das lernt man nicht an einem einzigen Nachmittag. Da ist viel Lesen 
angesagt, ausprobieren des gelesenen und beobachten, mal gezielt Fehler 
einbauen oder Dinge weglassen und sehen ob der daraus resultierende 
Fehler mit Deinem bisherigen Verständnis der Zusammenhänge in Einklang 
steht, wenn nicht: weiterlesen, gezielt weiterfragen, etc.

Das dauert Monate (oder Jahre, je nachdem wie tief Du einsteigen 
willst).

Aber bei einem Hobby ist Zeit kostenlos und reichlich vorhanden und der 
Weg ist das Ziel.

von Peter D. (peda)


Lesenswert?

Man kann sich auch ganz einfach eine Dummyfunktion in C schreiben und 
dann nach Assembler compilieren lassen.
Dann hat man ein Beispiel, wie Funktionsaufrufe, Argumente und 
Returnwerte zu deklarieren sind.

avrgcc -Os -S -c foo.c

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Peter D. schrieb:
> avrgcc -Os -S -c foo.c

-S oder -c ?

@Sercan: Die Registerverwendung von avr-gcc wird erklärt in

http://gcc.gnu.org/wiki/avr-gcc#Register_Layout

Dort wird auch ausführlich beschrieben, wie Parameter und Rückgabewerte 
von Funktionen übergeben werden.

Für jede Funktion, die von C aus aufzurufen ist:

1) Für jedes Assembler-Modul, z.B. foo.asm, lege einen C-Header an, z.B 
foo.h.

2) Das Modul foo.asm wird genauso compiliert wie ein C-Modul auch, d.h. 
mit avr-gcc und nicht mit avr-as.  avr-gcc ist ein Treiberprogramm, 
das anhand der Endungen erkennt, wie die Datei zu handhaben ist. 
Assembler-Code, über den vor dem Assemblieren der C-Präprozessor laufen 
soll, hat Endung .S oder .sx.  Bei anderen Endungen wie .asm verwendet 
man -x assembler-with-cpp foo.asm.

3) Für jede Funktion im Modul, die von C aus aufgerufen werden soll, 
kommt in die foo.h ein Prototyp:
1
#ifndef FOO_H
2
#define FOO_H
3
4
#include <stdint.h> // C99
5
6
extern uint16_t /* R23:22 */ ReadFuseByte (uint8_t addr0 /* R24 */);
7
8
#endif // FOO_H

4) In C-Modulen, welche die Funktion verwenden, includen vor der 
Verwendung foo.h und rufen die Funktionen so auf wie andere C-Funktionen 
auch.

5) Jede Funktion, die von C aus aufgerufen wird, sieht ungefähr so aus:
1
.text
2
3
;; Dokumentation der Funktion
4
;; Dokumentation ihres Interfaces
5
.global func
6
.type func,@function
7
8
func:
9
    ;; Code
10
11
.size func, . - func
.size unf .type sind lediglich Meta-Info für avr-size, avr-nm etc.

von Peter D. (peda)


Lesenswert?

C-Dummy foo.c:
1
#include <avr/io.h>
2
3
uint8_t foo( uint8_t val )
4
{
5
  PORTB = val;
6
  return PINB;
7
}

avr-gcc.exe -Os -S -c foo.c -mmcu=attiny25

Assembler Output foo.s:
1
  .file  "foo.c"
2
__SREG__ = 0x3f
3
__SP_H__ = 0x3e
4
__SP_L__ = 0x3d
5
__CCP__  = 0x34
6
__tmp_reg__ = 0
7
__zero_reg__ = 1
8
  .text
9
.global  foo
10
  .type  foo, @function
11
foo:
12
/* prologue: function */
13
/* frame size = 0 */
14
  out 56-32,r24
15
  in r24,54-32
16
/* epilogue start */
17
  ret
18
  .size  foo, .-foo

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.