Guten Tag zusammen,
ich beschäftige mich nun seit einiger Zeit mit dem programmieren von
Displays im Assembler Code. Das Ganze funktioniert auch soweit ganz gut,
also die grundlegenden Befehle habe ich soweit drauf.
Nun zum eigentlichen Thema:
Ich habe die Aufgabe erhalten ein vorgegebenes Programm von Assembler
nach C zu übersetzen (Umfang ~1500 Zeilen Code). Da ich mich noch nicht
genug mit Assembler auseinandergesetzt habe um das Programm zu
verstehen, fällt es mir ziemlich schwer den Code nach C umzusetzen.
Die Funktion des Programms soll, nach der Umsetzung in C, exakt gleich
funktionieren. Angesprochene Bedienelemente und/oder Displays etc.
sollen ohne Probleme, genau wie vorher, funktionieren.
Diese Aufgabe ist aus meiner Sicht ziemlich unmöglich oder es dauert
unverhältnismäßig lange. Gibt es die Möglichkeit gegebenen
Assembler-Code in C umzusetzen, sodass die Funktionalität weiterhin
gegeben ist?
Es wird MPLAB X und ein PIC18F46K80 verwendet
Ohne Verständnis für die Funktion des Programmes wird es wohl kaum
gehen.
Also Assembler lernen und das Programm analysieren. Es wird ja
hoffentlich schon Sourcecode sein, so dass man mit Labelm und
Kommentaren etwas anfangen kann.
1500 Zeilen ist ja auch recht harmlos, das bekommt man in 14 Tagen hin.
Alternativ kann man die CPU in C nachbilden und das Programm dann in
dieser Simulation laufen lassen. Dann wäre zumindest die Aufgabe formal
erfüllt.
Talris schrieb:> Die Funktion des Programms soll, nach der Umsetzung in C, exakt gleich> funktionieren.
Wenn die Funktion des Assembler-Programms genügend gut dokumentiert ist,
lege den Assembler-Code beiseite und programmiere es neu in C - ohne in
den Assember-Code zu schauen.
Frank M. schrieb:> lege den Assembler-Code beiseite und programmiere es neu in C - ohne in> den Assember-Code zu schauen.
So würde ich es auch machen. Aufschreiben, wann das Programm was und wie
macht und dann in C neu aufsetzen.
Talris schrieb:> Ich habe die Aufgabe erhalten ein vorgegebenes Programm von Assembler> nach C zu übersetzen
Schmeiß den Assemblercode komplett weg.
Du mußt die Funktion der Software kennen, mehr nicht.
Dann erstelle einen PAP danach und wenn der PAP logisch richtig
funktioniert, setze ihn in C um.
Assemblercode ist immer besser, weil es effizienter ist, aber es ist
schwerer das Programm zu erweitern. 1 zu 1 Umsetzung von ASM zu Compiler
ist nicht möglich weil jeder Compiler anderen Code erzeugt.
Kenner schrieb:> Das ist aber die Tatsache, dass Assemblercode besser ist. In Assembler> kannst du alles machen was die CPU unterstützt.
Ja schön. Da hast du was davon, wenn du dir mit Vollgas und mit allem
was die CPU kann an allen Ecken und Enden selbst in den Fuss schiesst,
weil du wieder mal irgendwo einen Pop zu einem Push vergessen hast oder
in einer Subroutine übersehen hast, dass du ein Register, dass 3
Aufrufebenen weiter draussen gebraucht wird, nicht benutzen darfst.
Letzten Endes schenkst sich hinreichend komplexer Code in Assembler
nichts mehr gegen einen gleichwertigen C Code. Und zwar deshalb, weil
man als Programmierer all die Details gar nicht mehr überlicken kann,
die man aber wissen müsste um wirklich jeden überflüssigen Taktzyklus
loszuwerden. Von der ausufernden Entwicklungszeit reden wir mal gar
nicht. Was bei 200 Zeilen Assembler Programmen noch funktioniert,
funktioniert bei 200-tausend Zeilen so nicht mehr.
Die werden schon wissen, warum sie das Programm neu geschrieben haben
wollen. Die meisten Profis hier im Forum haben auch einen hinreichend
begründeten Verdacht, was das eigentliche Problem sein wird:
Code-Wartung. Die meisten von uns waren alle schon an dem Punkt, an dem
ein 3 Jahre alter Assembler-Code hätte erweitert werden sollen und kein
Schwein blickt mehr durch.
Kenner schrieb:> Das ist aber die Tatsache, dass Assemblercode besser ist.
Das ist aber nicht Thema dieses Threads. Die Aufgabe ist hier:
Assembler-Programm neu zu schreiben in C. Das wird schon seinen Grund
haben.
Was nützt hier Deine reingekippte "Tatsache"? Nichts. Aber Hauptsache,
Du hast es erwähnt.
Ich würde folgendermaßen vorgehen.
Den Assemblercode zeilenweise analysieren.
Lassen sich Zeilen zusammenfassen, so fügst Du den C-Code in Form eines
Kommentars ein.
Geht das nicht, so musst Du den Assemblercode in C nachbilden.
Anfangen würde ich mit den Unterfunktionen. Sinnvolle Namen wären nicht
schlecht. Eine Unterfunktion gleich eine C-Funktion.
Im zweiten Schritt würde ich alle Zeilen des Assemblercodes, die ich
ersetzt habe, ihrerseits Ausklammern.
Am Ende sollte die Datei funktionsleer sein.
Den ganzen Kram kannst Du dann in eine C-Datei überführen, die
auskommentierten C-Zeilen zu C-Befehlen freigeben und glücklich werden.
Natürlich bleibt noch die Möglichkeit, den gesamten Code zu erfassen und
direkt in C nachzubilden. Die Chance ist dann aber groß, dass dann der
Schlenker in Zeile 122 nicht mehr vorhanden ist.
Der Weg führt über das Verständnis der Funktion, des grundsätzlichen
Aufbaus des Programms, dessen funktionale Gruppen und wie und mit
welchen Algorithmen Dinge gelöst werden.
Das ist wie die Arbeitsweise einer Industrieanlage verstehen zu wollen,
indem man sich jede einzelne Schraube und jede Beilagscheibe ansieht.
Das bringt nichts. Verständnis kriegt man erst, wenn man all diese
Details beiseite lässt und sich erst mal die groben Gruppen ansieht, das
Zusammenspiel der Komponenten und erst dann, wenn man weiss, welcher
Funktionsblock welche Aufgabe hat, erst dann macht es Sinn sich
anzusehen, wie das im Inneren funktioniert - durchaus dann auch runter
bis zu der Auslegunsebene auf der man dann auch versteht, warum an
dieser Stelle eine M12 Schraube benutzt wurde. Aber mit der M12 Schraube
anzufangen bringt dich nicht weiter, wenn du rausfinden willst, wie eine
Raffinerie aus Röhol Benzin gewinnt.
Das große Problem bei einem Assembler Programm ist natürlich, dass du
kein Flugzeug hast um damit ein paar mal über die Raffinierie zu
fliegen, sondern immer nur einen kleinen Ausschnitt siehst. Nichts desto
trotz musst du es schaffen, aus diesen kleinen Ausschnitten das große
Ganze der Maschine zusammenzusetzen, vor der du gerade stehst.
Gottlob sind allerdings auch die meisten Assembler Programme nicht bis
aufs letzte ausgereizt, so dass man auch dort einzelne funktionale
Gruppen recht schnell erkennen kann. So zb wird es mit Sicherheit eine
Textausgabe Funktion geben, und auch für den Aufbau eines Menüs und die
Auswertung von Eingabeelementen gibt es nicht unendlich viele
verschiedene Möglichkeiten. Kennt man die prinzipielle Bedienung, dann
findet man vieles davon auch im Code wieder.
Schwierig zu erkennen sind des öfteren Querzusammenhänge, wenn zb eine
Operation davon abhängt, ob 5 Zeilen davor ein Carry Flag gesetzt wurde
oder nicht. Das kann dann schon recht trickreich sein. Dann ist es recht
hilfreich, wenn man im Vorfeld schon eine gewisse Vorstellung hat, was
in diesem Code Abschnitt eigentlich geschehen müsste. Womit wir wieder
beim groben Überblick wären, der schon vorhanden sein sollte.
Talris schrieb:> Ich habe die Aufgabe erhalten ein vorgegebenes Programm von Assembler> nach C zu übersetzen (Umfang ~1500 Zeilen Code). Da ich mich noch nicht> genug mit Assembler auseinandergesetzt habe um das Programm zu> verstehen, fällt es mir ziemlich schwer den Code nach C umzusetzen.
Hey lass dich nicht entmutigen! Das scheint noch machbar zu sein.
Wie die anderen schon sagten, DU brauchst den BIG VIEW auf die Software.
Wie du das Programm übersetzt ist dir überlassen, mache direkten C-Code
daraus oder einen PAP, den man später in C-Code übersetzt.
Aus eigenen Erfahrungen würde ich den PAP Weg wählen, denn so kannst du
oder ein anderer später deinen Weg besser nach vollziehen.
Der Weg über den PAP sollte auch einfacher sein, da du dann den ASM-Code
nicht mehr brauchst, wenn du den C-Code erstellst.
Nutze SVN oder GIT (usw), damit du eine History hast, wann und wie du
den Code(PAP) bearbeitest hast.
Das geht schon, man braucht nur 'ne Weile. Hab' ich früher (m68k) öfter
gemacht.
Meine Vorgehensweise war eigentlich immer die, Zeile für Zeile in C
umzuschreiben.
Das geht m.E. am besten, wenn man einen Compiler hat, der Inline
Assembler unterstützt. Damit bringt man das Programm (in Assembler mit
geschweiften Klammern und einem
1
main()
drumrum) erst mal zum Laufen und kann dann im nächsten Schritt Zeile
für Zeile nach C umsetzen.
Damit erhält man sich die Möglichkeit, zwischendurch immer mal wieder zu
testen, ob es noch dasselbe macht wie vorher. Ein Testplan (oder gar
Unit Tests) sind in der Phase sehr sinnvoll.
Das Ergebnis des ersten "Durchlaufs" ist dann zwar formal C, aber meist
noch kein vernünftiges, wartbares Programm. Jetzt kann man aber
klassisch Refactoring betreiben und hat trotzdem jederzeit die
Möglichkeit zu testen (ein Editor, der Refactoring vernünftig
unterstützt, ist dabei Gold wert).
Nach ausreichend vielen Wiederholungen kommt irgendwann ein einigermaßen
gescheites Programm dabei raus.
Mit anderen Ansätzen bin ich regelmäßig gescheitert, wenn ich zu große
Brocken auf einmal angefaßt habe, war plötzlich ein Fehler drin und die
Fehlersuche dauerte überproportional lang.
Markus F. schrieb:> wenn ich zu große> Brocken auf einmal angefaßt habe, war plötzlich ein Fehler drin
Der Trick an C ist nicht in großen Brocken zu denken, sondern in kleinen
Modulen.
Wenn ein Brocken nicht funktioniert, mußt Du ihn noch weiter unterteilen
und die Teile testen, bis sie funktionieren.
Wenn Du einen großen Brocken 1:1 umschreibst, bleibt es ein großer
unwartbarer und nicht erweiterbarer Brocken, daran kann C nichts ändern.
Wenn Du z.B ein LCD über I2C anschließt, machst Du keinen Brocken daraus
sondern schreibst eine I2C-Treiber und einen LCD-Treiber. Und erst wenn
Du beide für sich getestest hast, ruft der eine nur noch den anderen
auf.
Meinen Ansatz fußte natürlich darauf, dass kein optimal kommentierter
Code vorhanden ist.
Bei sauber kommentiertem Assembler sieht die Sache ganz schon ganz
anders aus.
Das letzte Mal, dass ich sowas gemacht habe, waren es ein ausgelesener
Flash bzw. ein disassembliertes Programm. Lang, lang, ist’s her.
Da braucht man, am Anfang, in der Hauptsache die Such- und
Ersetzfunktionen, um ein paar, halbwegs kommentierbare Sprungmarken zu
bekommen, bzw. so etwas wie eine Struktur.
Amateur schrieb:> Bei sauber kommentiertem Assembler sieht die Sache ganz schon ganz> anders aus.
Ja, es gibt auch solche Ausnahmen, aber die Praxis sieht oft anders aus.
Insbesondere beim PIC mit anfangs stark limitiertem HW-Stack wurden nur
ungern Calls verwendet und lieber ellenlang Code hintereinander gepappt.
Die ersten AVRs (AT90S1200) hatten auch nur einen HW-Stack, man was hab
ich da geflucht.
Hallo und vielen für die zahlreichen Tipps :)
Ich bin soweit dass ich die CALL Anweisungen erstmal in kleine
C-Funktionen gefasst habe, um dann Stück für Stück das Assembler
Programm nach C umzusetzen.
Klappt auch ganz gut :D
Bei einigen Befehlen komme ich allerdings noch ins straucheln. Wenn man
das Ganze zum ersten mal macht, scheint das eine sehr komplizierte
Angelegenheit zu sein (und mir wurde gesagt das dieses Programm das
leichteste / simpelste ist^^).
Aber zum Beispiel bei dieser Funktion hier (schreibe EEPROM):
1
btfssPIR1,RCIF;ReceiveInt.Flaggesetzt?
2
gotoschreibe_EEPROM3
3
callgetRx_data
4
5
bank2
6
movfwRX_DATA
7
;movwfDUMMY
8
movwfEEDATL
9
10
bank3
11
bcfINTCON,GIE
12
bcfEECON1,EEPGD
13
bsfEECON1,WREN
14
movlw0x55
15
movwfEECON2
16
movlw0xaa
17
movwfEECON2
18
bsfEECON1,WR
19
bsfINTCON,GIE
20
bcfEECON1,WREN
21
bank0
Da fällt es mir noch schwer sowas umzusetzen bzw. wie sieht sowas in C
aus? Brauch ich das in C überhaupt? Denn C arbeitet nicht mit z.B
bank0..bank4.
Aber alles in allem ist es doch schon ganz ok. Vielen Dank euch allen :)
Talris schrieb:> Denn C arbeitet nicht mit z.B> bank0..bank4.
Das macht dann eben der Compiler für dich.
Im Assembler Code hätte man aber auch besser so was wie
BANKSEL RX_DATA als bank2 (auf der vermutlich RX_DATA liegt)
benutzt.
Das Beispiel ist jetzt aber so nicht in einem Stück kopiert, oder?
Sieht irgendwie sehr seltsam aus. Was macht getRx_data?
Der Ausschnitt ist zu kurz, du müsstest die komplette Funktion posten.
Talris schrieb:> Aber zum Beispiel bei dieser Funktion hier (schreibe EEPROM):
...
Die Codesequenz sieht für mich erst mal einigermaßen sinnvoll aus (ich
nehme an, hier werden Daten am seriellen Port empfangen und ins EEPROM
geschrieben?). Bei mir würde das (im ersten Ansatz) so aussehen:
1
if(!(PIR1&RCIF))
2
{
3
/* nothing on serial port */
4
return;/* nehme an, schreibe_eeprom3 ist der Fehlerausgang? */
5
}
6
7
EEDATL=get_rx_data();/* fetch a byte from RXDATA (serial port) */
8
9
INTCON&=~GIE;/* disable all interrupts */
10
EECON1&=~EEPGD;
11
EECON1|=WREN;/* enable write to EEPROM */
12
13
EECON2=0x55;/* data EEPROM write sequence init */
14
EECON2=0xaa;
15
16
EECON1|=WR;/* initiate write */
17
INTCON|=GIE;/* enable interrupts */
18
19
EECON1&=~WREN;/* deny EEPROM writes */
Da mögen noch einige Fehler drin sein, PIC ist nicht gerade meine
Stärke. Jetzt würde ich das mal compilieren und testen.
Schöner machen kann man's immer noch.
P.S.: ich hab' dafür übrigens ungefähr 5 Minuten gebraucht, obwohl PIC's
mir einigermaßen fremd sind. Sagen wir mit Testen zehn, dann müsstest Du
nach 2 Arbeitstagen mit 1500 Zeilen durch sein ;)
Markus F. schrieb:> EECON1 &= ~WREN; /* deny EEPROM writes */
Vorsicht bei solchen Sachen.
Es kommt drauf an, ob man bei
1
bcf EECON1, WREN
die Bitnummer angibt oder eine Bitmaske. In C (ohne Erweiterungen) gibt
man immer eine Bitmaske an. WEnn das also im Assembler eine Bitnummer
ist, dann schreibt sich das als
1
EECON1&=~(1<<WREN);/* deny EEPROM writes */
Ob die Angabe WREN im Assemblercode eine Bitnummer oder eine Bitmaske
ist, lässt sich am einfachsten feststellen, indem man in der Assembler
Doku den Abschnitt über BCF aufschlägt und ganz einfach nachliest.
Karl H. schrieb:> Vorsicht bei solchen Sachen.>> Es kommt drauf an, ob man bei bcf EECON1, WREN> die Bitnummer angibt ...
Ja, das ist eine Nummer. Bei den aktuellen MCHP Compilern wäre das in C:
Karl H. schrieb:> Es kommt drauf an, ob man bei bcf EECON1, WREN> die Bitnummer angibt oder eine Bitmaske.
Stimmt, da muß man aufpassen.
Die MPLAB-Compiler verwenden Bitfelder. Ich verwende für PIC den SCC und
habe eigene Includes, die Bitmasken definieren.