Nochmals umwandlung von MC7 Code in Calls...

Jochen Kühner

Well-known member
Beiträge
4.211
Punkte Reaktionen
484
Zuviel Werbung?
->Hier kostenlos registrieren
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.

Code:
      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

Well-known member
Beiträge
766
Punkte Reaktionen
90
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?
 
Zuletzt bearbeitet:
OP
Jochen Kühner

Jochen Kühner

Well-known member
Beiträge
4.211
Punkte Reaktionen
484
Zuviel Werbung?
->Hier kostenlos registrieren
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.
 

Thomas_v2.1

Well-known member
Beiträge
8.821
Punkte Reaktionen
2.705
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
 

Thomas_v2.1

Well-known member
Beiträge
8.821
Punkte Reaktionen
2.705
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.
 

Anhänge

  • graph-cont-x-small.jpg
    graph-cont-x-small.jpg
    23,7 KB · Aufrufe: 46
OP
Jochen Kühner

Jochen Kühner

Well-known member
Beiträge
4.211
Punkte Reaktionen
484
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

Well-known member
Beiträge
8.821
Punkte Reaktionen
2.705
Zuviel Werbung?
->Hier kostenlos registrieren
Mit der Registerverfolgung habe ich das beim Einlesen so in der Art gelöst.
Schnippsel:
Code:
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

Well-known member
Beiträge
8.821
Punkte Reaktionen
2.705
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

Well-known member
Beiträge
54
Punkte Reaktionen
8
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...
 
Oben