Forum: Mikrocontroller und Digitale Elektronik MPLAB X: Assembler -> C


von Talris (Gast)


Lesenswert?

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

: Verschoben durch Admin
von PittyJ (Gast)


Lesenswert?

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.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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.

von Ingo L. (corrtexx)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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.

von Kenner (Gast)


Lesenswert?

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.

von Ingo L. (corrtexx)


Lesenswert?

Kenner schrieb:
> Assemblercode ist immer besser
Bitte nicht schon wieder... Das hatten wir bereits unendlich mal.

von Kenner (Gast)


Lesenswert?

Das ist aber die Tatsache, dass Assemblercode besser ist. In Assembler 
kannst du alles machen was die CPU unterstützt.

von Karl H. (kbuchegg)


Lesenswert?

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.

: Bearbeitet durch User
von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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.

von Amateur (Gast)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

: Bearbeitet durch User
von Kenner (Gast)


Lesenswert?

Ja, wie gesagt, das ist gar nicht so einfach. Es ist so ählich wie ein 
Program geschrieben in  z.B Basic ins C umzuschreiben, nur noch 
schwieriger.

von Stephan W. (sir_wedeck)


Lesenswert?

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.

von Markus F. (mfro)


Lesenswert?

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.

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

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.

: Bearbeitet durch User
von Amateur (Gast)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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.

von Talris (Gast)


Lesenswert?

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
btfss  PIR1,RCIF        ; Receive Int. Flag gesetzt?
2
  goto  schreibe_EEPROM3    
3
  call  getRx_data        
4
  
5
  bank2              
6
  movfw  RX_DATA          
7
;  movwf  DUMMY          
8
  movwf  EEDATL          
9
10
  bank3              
11
  bcf  INTCON,GIE        
12
  bcf  EECON1, EEPGD      
13
        bsf      EECON1, WREN           
14
  movlw  0x55                   
15
        movwf  EECON2                
16
        movlw  0xaa        
17
        movwf  EECON2                 
18
  bsf  EECON1, WR             
19
  bsf  INTCON,GIE  
20
  bcf      EECON1, 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 :)

von Volker S. (vloki)


Lesenswert?

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.

: Bearbeitet durch User
von Markus F. (mfro)


Lesenswert?

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

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

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.

von Volker S. (vloki)


Lesenswert?

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:
1
EECON1bits.WREN = 0;   // bcf -> bit clear (in) file

von Markus F. (mfro)


Lesenswert?

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.

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.