# Nochmals umwandlung von MC7 Code in Calls...



## Jochen Kühner (10 Januar 2013)

Ich bin jetzt in meiner Bibliothek nochmals dran UC in Calls umzuwandeln.

Jezd ist das ganze bei den FBs schon etwas komplizierter und schlägt mit meiner bisherigen Methode öfters Fehl.


```
BLD   3
      =     L23.0
      TDB   
      L     DBW[AR2,P#0.0]
      T     LW24
      AUF   DI[LW24]
      TAR2  LD19
      L     DBW[AR2,P#2.0]
      T     DIW10
      CLR   
      =     DIX12.1
      CLR   
      =     DIX12.2
      SET   
      =     DIX12.3
      U     DBX[AR2,P#20.1]
      =     DIX12.4
      U     DBX[AR2,P#21.1]
      =     DIX12.5
      L     #h.Bits[1]
      T     DIW16


      L     #h.Bits[2]
      T     DIW18
      L     0
      T     DIW20
      L     P#V 4.0
      T     DID22
      L     0
      T     DIW26
      L     P#V 0.0
      T     DID28
      UC    "blabla"
      L     DIW16
      T     #h.Bits[1]
      L     DIW18
      T     #h.Bits[2]
      TDB   
      BLD   4
```

Jetzt muss z.B. aus diesem Code ausgelesen werden, das der aktuelle DI in DB umgespeichert wird, über " L     DBW[AR2,P#0.0]" indirekt der erste Parameter ausgelesen wird. Dieser wird in ein Lokalwort gespeichert und das dann als DI geöffnet.

D.h. der erste an den FB übergebene Parameter ist der DI für diesen Call.

Nun hatte ich mir gedacht einen Parser für jede Zeile ab dem BLD bis zum UC zu bauen, und immer die aktuellen werte der Register zu speichern. z.b.

Register DI = DI
Register DB = ""
dann kommt TDB
Register DI = ""
Register DB = DI
dann kommt 
L     DBW[AR2,P#0.0]
d.h. 
Akku1 = DBW[AR2,P#0.0] da aber in DB Register DI steht muss ich in die Parameter des DI schauen...

oder hat jemand andere gute Ideen das zu lösen?


----------



## LowLevelMahn (10 Januar 2013)

*wie wärs mit einem Control/Data Flow Graph?*

Ich hab mir deinen http://www.sps-forum.de/showthread.php/41550-CALLs-aus-AWL-Code-erzeugen... Post angeschaut und ich denke du solltest das ein wenig mehr abstrahieren - wenn ich das richtig verstehe machst du die Fluss und Befehlsanalyse alles irgendwie zusammen - das macht es nicht wirklich leichter/stabiler weil du viel Code dafür brauchst

ich würde aus dem ganzen AWL-Code erst mal einen AST machen (oder hast du den schon?), und dann vielleicht darauf einen Control und Data-Flow Graphen setzen - dann weisst du was was ist, an wem es hängt, und welche Daten da rumfließen
das vereinfacht deinen Code schon mal weil Konvertierungen und Detailbeurteilungen kleiner Ausfallen - leichter ist das ganze aber nur am Ende - der Anfang macht sicher keinen Spass 

btw: hat mal eine darüber nachgedacht einen LLVM AWL(eher SCL)->MC7 Kompiler zu bauen - oder wir der fehlende Stack ein Abbildungsproblem in LLVM?


----------



## Jochen Kühner (10 Januar 2013)

Flussanalyse brauch Ich ja nicht wirklich zu machen, Sprünge oder ähnliches kommen ja nicht vor!

AWL in MC7 muss ja nicht wirklich compiliert werden, da der AWL Code ja 1=1 dem MC7 Code entspricht (außer bei den Calls) von daher wäre der Compiler für AWL unnötig.


----------



## LowLevelMahn (10 Januar 2013)

> Flussanalyse brauch Ich ja nicht wirklich zu machen



ich meinte auch Datenfluss-Analyse - also wo geht was hin


----------



## Thomas_v2.1 (10 Januar 2013)

Hallo,
ich bin zur Zeit dran einen SCL Decompiler zu schreiben. Da habe ich genauso wie von Jochen vorgeschlagen eine Analyse der Registerinhalte pro Anweisung drin, denke mal das ist der richtige Weg.
Die Zurückübersetzung von Funktionsaufrufen lasse ich mir aber bis zum Schluss übrig, das wird nämlich ziemlich noch ein gutes Stück Arbeit.

Bei einem MC7 nach AWL Übersetzer ist bis auf das Problem bei den Aufrufmakros meiner Meinung nach keine großartige Abstraktionsebene notwendig. Das Decompilieren beschränkt sich ja auf die Anweisungen zwischen den entsprechenden BLD Anweisungen. Und sind diese nicht wohlgeformt so streikt der Step7 Editor ebenfalls und zeigt dann MC7 an.

Mein Decompiler hat hingegen mehrere Zwischenebenen, so ganz grob:
1. Einlesen, Registerverfolgung, aus AWL die grundlegende Statements in Form eines AST bilden
2. Basic Blocks finden
3. Kontrollflussanalyse, Schleifenerkennung
4. Umstrukturierung und auflösen von Gotos


----------



## Blockmove (10 Januar 2013)

Thomas_v2.1 schrieb:


> ich bin zur Zeit dran einen SCL Decompiler zu schreiben.



Fingerübung oder hast du dafür einen praktischen Einsatzzweck?

Gruß
Dieter


----------



## Thomas_v2.1 (10 Januar 2013)

Blockmove schrieb:


> Fingerübung oder hast du dafür einen praktischen Einsatzzweck?


Beweislastumkehr falls Siemens mal wieder zickt ;-) Das Programm malt auch schöne Grafiken der Kontrollstrukturen (s. Anhang).

Ansonsten mehr oder weniger nur weil mich das interessiert, und hatte mitte letzten Jahres etwas Zeit mich da einzulesen.
Einen rudimentären SCL Compiler habe ich mit Coco/R als Parser Generator mal programmiert, der kann aber wirklich nur die elementaren Anweisungen.


----------



## Jochen Kühner (11 Januar 2013)

Thomas_v2.1 schrieb:


> Beweislastumkehr falls Siemens mal wieder zickt ;-) Das Programm malt auch schöne Grafiken der Kontrollstrukturen (s. Anhang).
> 
> Ansonsten mehr oder weniger nur weil mich das interessiert, und hatte mitte letzten Jahres etwas Zeit mich da einzulesen.
> Einen rudimentären SCL Compiler habe ich mit Coco/R als Parser Generator mal programmiert, der kann aber wirklich nur die elementaren Anweisungen.



Wenn dus später als Open Source veröffentlichst, kann Ichs ja vielleicht in meine ToolBox einbauen ;-)


----------



## Thomas_v2.1 (11 Januar 2013)

Mit der Registerverfolgung habe ich das beim Einlesen so in der Art gelöst.
Schnippsel:

```
if (instr.op == "U" or instr.op == "O" or instr.op == "X" or
	instr.op == "UN" or instr.op == "ON" or instr.op == "XN"):
	if not er_instr_flag:
		left = Identifier(instr.address, instr.arg)
	else:
		right = Identifier(instr.address, instr.arg)
		left = BinExpr(instr.address, left, instr.op, right)
		er_instr_flag = True
elif instr.op == "SET":
	er_instr_flag = False
	left = Identifier(instr.address, "TRUE")
elif instr.op == "TAK":
	helpvar = akku1
	akku1 = akku2
	akku2 = helpvar
elif instr.op == "L":
	akku2 = akku1
	akku1 = Identifier(instr.address, instr.arg)	
elif instr.op == "T":
	arg = Identifier(instr.address, instr.arg)
	assign = Assign(instr.address, arg, akku1)
	ast.append(assign)
```

Eleganter wäre es wohl für jede Operation eine eigene Klasse anzulegen, die eine Methode bekommt in der die Verarbeitung der Register hinterlegt wird.


----------



## Thomas_v2.1 (11 Januar 2013)

Jochen Kühner schrieb:


> Wenn dus später als Open Source veröffentlichst, kann Ichs ja vielleicht in meine ToolBox einbauen ;-)



Ist momentan noch in Python geschrieben.
Aber da ich ganz ohne GUI wohl nicht auskomme werde ich es evtl. nochmal nach C# umschreiben.
Z.B. gibt es einige Dinge für die ein manueller Eingriff notwendig wird weil sich diese nicht mehr automatisch zurückübersetzen lassen (AT-Sichten, Switch/Case).


----------



## bo1986 (11 Januar 2013)

Komme aus: Hobby / Zeitbeschäftigung gesucht...
C# Können ist relativ bei deinem Problem ich kenne zwar AWL ein wenig aber kein MC7...

Aber  so wie ich es lese ist das was du benötigst ein Verständnis von C und  C# bildet die Oberfläche und die Klassen/Methodenstruktur drumherum...
Eventuell wäre es eine Lösung, wenn du ein Case abbildest, was anhand der AWL befehle deine Aktionen ausführt. und das in einer Schleife - solange Textzeilen vorhanden sind...

dann gibt es Ausnahmen, die entscheiden, entweder Register DB oder Register DI...
Aber wie gesagt, ich kenn mich mit MC7 nicht aus...


----------

