Hallo Leute,
bin grad dabei ein Assembler-Projekt in ein C-Projekt umzuprogrammieren.
Da ich nicht so der Assemblerkünstler bin habe ich da meine probleme.
Ich bräuchte ein paar tipps wie ich am besten anfangen soll bzw. dann
auch weitermachen. Hab jetzt schon mittlerweile die ganzen .equ,...,
usw. als makros in C und fange jetzt an die ganzen Funktionen zu
übersetzen.
Das Assembler-Projekt läuft auf einem ATmega169 Controller und soll auch
in C auf diesem Controller laufen.
Jede art von Rat und Hilfe sind mir willkommen
ingmar
Ingmar H. schrieb:> Jede art von Rat und Hilfe sind mir willkommen
man sollte verstehen was Programm macht und es dann NEU in C schreiben.
eine umsetzung von ASM zu C macht wenig sinn auf codebasis.
was das programm macht versteh ich schon... ich hab jetzt z.B. probleme
den
code richtig zu übersetzen weil es doch so ein paar unterschiede gibt.
Beispiel:
PUSH AKKU_1
PUSH AKKU_2
LD AKKU_1, X
INC XL
LD AKKU_2, X
DEC XL
CALL DISP_HEX
POP AKKU_2
POP AKKU_1
********************************************************************
LDI ZL, LOW(ST_OFFSET*2)
LDI ZH, HIGH(ST_OFFSET*2)
***********************************************************************
PUSH und POP kann ich verachlässigen oder??
wie würde LD AKKU_1, X und
LDI ZL, LOW(ST_OFFSET*2) in C aussehen?
Ich schließe mich den Vorrednern an. In Assembler arbeitest du mit
Registern, dem Akku (sofern vorhanden) und ein paar Speicherstellen).
In C arbeitest du mit Variablen (= Speicherstellen), und nicht mit den
Registern. Das nimmt dir der C-Compiler ab.
Du sagst du weisst was das Programm macht. Dann notiere dir dazu den PAP
(Programm-Ablauf-Plan) oder etwas äquivalentes, was dir eben den Ablauf
passiert, aber auf Basis des "was passiert", nicht "wie's passiert".
An deinem o.g. Code-Auszug wäre das dann also nicht "Lade Akku nach...",
etc., sondern "Zeige auf Display in Hex-Darstellung" (dem Call nach zu
urteilen).
Wenn du das gemacht hast, dann hast du eine von der Sprache unabhängige
Beschreibung des Programms. Und das wiederum setzt du dann in C um:
Ingmar H. schrieb:> PUSH und POP kann ich verachlässigen oder??> wie würde LD AKKU_1, X und> LDI ZL, LOW(ST_OFFSET*2) in C aussehen?
Falls es dir noch nicht klar ist: Du musst die Funktionalität
nachbilden, nicht den Maschinencode. Der hier gezeigte Codeausschnitt
könnte mit der Standardfunktion printf() nachgebildet werden, odet du
linkst deine uart Bibliothek dazu und verwendest deine uart_putc()
Funktion oder ... du verstehst wie ich's meine?!
Wenn Du grundlegende Probleme mit Assembler hast, solltest Du Dich
vielleicht erst mal in einen Assembler einarbeiten, bevor Du C
programmierst. C ist keine Hochsprache, es ist eine Art
"Makro-Assembler". Arrays gibts in C nicht, das sind nur Zeiger, und
wenn Du über das Ende hinaus zugreifst, greifst Du auf was auch immer
dahinter liegt zu. Wenn Dein Array auf dem Stack liegt (z.Bsp. bei
lokalen Variablen) so liegt nach dem Array die Rücksprungadresse.
Falls Du aber wirklich eine 1:1 Umsetzung haben willst, dann scheibe Dir
doch einfach ein Programm, welches das macht.
Im Prinzip musst Du nur das Programm zeilenweise einlesen, und die
Kommentare entfernen, sowie den #includes folgen. #defines kannst Du als
Makros übersetzen.
Zeilen mit
.cseg
und
.dseg
Schalten zwischen Programmspeicher und Datenspeicher um. Abhängig davon
musst Du bei Labels entweder Sprunglabels in C oder Variablen anlegen.
Gut ist das dann nicht, aber die Aufgabe wäre erfüllt. Ich würde aber in
Deiner Situation mir 90% der Zeit nehmen den existierenden Code zu
verstehen, und ihn in der restlichen Zeit neu implementieren. Danach
wirst Du besseren C-Code schreiben als vorher.
Ingmar H. schrieb:> was das programm macht versteh ich schon... ich hab jetzt z.B. probleme> den> code richtig zu übersetzen weil es doch so ein paar unterschiede gibt.>> Beispiel:>> PUSH AKKU_1> PUSH AKKU_2> LD AKKU_1, X> INC XL> LD AKKU_2, X> DEC XL> CALL DISP_HEX> POP AKKU_2> POP AKKU_1
Das lässt sich so nicht "übersetzen". Hier wird ein 16-Bit Wert
indirekt über X geladen und vermutlich an DISP_HEX übergeben.
Ob der Wert übergeben wird — d.h. in DISP_HEX verwendet wird — ist an
dem Code nicht zu sehen, ebenso ob DISP_HEX etwas zurückliefert oder
Seiteneffekte hat.
Die Registerzuordnung in einem C-Programm erledigt der Compiler. Das
ist ja auch einer der Gründe, eine Compiler einzusetzen :-)
Einen 16-Bit Wert aus dem RAM zu lesen und damit eine Funktion
aufzurufen sieht in C so aus:
1
#include<stdint.h>
2
3
externvoiddisp_hex(uint16_t);
4
5
voidf1(uint16_t*addr)
6
{
7
disp_hex(*addr);
8
}
und avr-gcc übersetzt das zB so:
1
f1:
2
movw r30,r24 ; , addr
3
ld r24,Z ; , *addr_1(D)
4
ldd r25,Z+1 ; , *addr_1(D)
5
jmp disp_hex
Du kannst also versuchen, dieses kleine C-Beispiel zu verstehen und auf
deinen Fall anzupassen oder in eine größere Funktion einzubette.
> LDI ZL, LOW(ST_OFFSET*2)> LDI ZH, HIGH(ST_OFFSET*2)
Dieses Beipiel ist mindestend genauso übel.
Ist der nach Z geladene Wert eine Adresse?
Oder eine Konstante, die zur Compilezeit bekannt ist?
Oder der Index in ein Array aus 16-Bit Werten?
Im einfachsten Fall ist der C-Code
1
#define ST_OFFSET (12345-123)
2
3
uint16_tf2(void)
4
{
5
return2*ST_OFFSET;
6
}
und wird zu:
1
f2:
2
ldi r24,lo8(124)
3
ldi r25,lo8(95)
4
ret
Die Registerzuordnung ist wie gesagt Aufgabe des Compilers.
Ist ST_OFFSET ein Index in ein 16-Bit Array, und wird auf dem Array an
dieser Stelle gelesen, dann:
1
externuint16_tarray[];
2
3
uint16_tf3(void)
4
{
5
returnarray[ST_OFFSET];
6
}
und das compiliert zu
1
f3:
2
ldi r30,lo8(array+24444)
3
ldi r31,hi8(array+24444)
4
ld r24,Z ; , array
5
ldd r25,Z+1 ; , array
6
ret
Als erstes musst du also das Programm abstrahieren und von der konkreten
Registerverwendung, den Instruktionen, etc. loskommen und dir klarmachen
• Welche (globalen) Objekte und Datenstrukturen gibt es?
• Welche Funktion erfüllt welche Aufgaben?
• Welche Parameter bekommen die Funktionen?
• Und was geben sie zurückgeben?
• Welche globalen Variablen werden sie (falls überhaupt)
• Wie werden asynchrone Ereignisse (IRQs) verdaut?
• Was ist eine sinnvolle Strukturierung von alledem?
@ Christian Berger
Seit wann ist C keine Hochsprache ?
Eine Hochsprache ist jede Programmiersprache die über Assembler
hinausgeht.
Arrays als Datenstruktur gibt es sehr wohl in C.
Man kann aber beispielsweise einen C-String in Form eines Arrays anlegen
1
charhallo[]="halloWelt";
zum durchlaufen index inkrementieren bzw. dekrementieren (Index
entspricht Speicheradresse).
oder so:
1
char*hallo="halloWelt";
hier pointer inkrementieren bzw. dekrementieren.
über sizeof(hallo)/sizeof(char) überlauf abfangen.
gruß R2D2
Man kann wie oben schon geschrieben, einfach nur die Funktionalität
nachahmen, ohne groß auf den Assemblercode zu gucken.
Wenn man aber z.B. wissen möchte, welcher Algorithmus eingesetzt wird,
oder ob Hardware"tricksereien" (Hardwareverständnis) im Spiel sind o.ä.
dann sollte man möglichst den Algo kennen, wie auch Assemblergrundlagen
zum Lesen des Asm-Codes.
Ich würde die Gelegenheit beim Schopfe packen, und gleich
Assemblergrundlagen lernen und mit der Aufgabe vertiefen.
Und man versteht den Assemblercode oft sehr viel besser, wenn man weiß,
um was es geht. Assembler lernt sich normalerweise leicht und schnell,
weil ja kaum Abstrakta im Spiel sind. Nur ein wenig Zeit sollte man sich
geben und die ein oder andere, grundlegende Lowlevelsubroutine
verstanden haben.
Einzelne Funktionen kann man sich isoliert im Debugger angucken und
versuchen, besser zu verstehen.
Und auch im Manual des jeweiligen Assemblers lassen sich meist wertvolle
Hilfen zum Programmverständnis oder Programmiertechniken finden.
)Und wenn die Assemblerroutine sehr gut ist, warum dann nicht drinlassen
und von C aus aufrufen?
--> hier spielt auch das "Warum" von Asm nach C eine gewisse Rolle und
in dem gleichen Zusammenhang auch die Nützlichkeit von C zurück nach
Asm)
Peter II schrieb:> man sollte verstehen was Programm macht und es dann NEU in C schreiben.> eine umsetzung von ASM zu C macht wenig sinn auf codebasis.
Abgesehen von der Rechtschreibung, ist das die einzig richtige
Einschätzung der Sachlage.