# # AWL simulator - Operator translator # # Copyright 2012-2018 Michael Buesch # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # from __future__ import division, absolute_import, print_function, unicode_literals #from awlsim.common.cython_support cimport * #@cy from awlsim.common.compat import * from awlsim.common.sources import AwlSource import math import re #from awlsim.core.statusword cimport * #@cy from awlsim.common.cpuspecs import * #+cimport from awlsim.common.cpuconfig import * from awlsim.common.datatypehelpers import * #+cimport from awlsim.core.operators import * #+cimport from awlsim.core.util import * from awlsim.core.parameters import * #+cimport from awlsim.core.memory import * #+cimport from awlsim.core.offset import * #+cimport from awlsim.core.operatortypes import * #+cimport from awlsim.core.identifier import * __all__ = [ "AwlOpTranslator", ] class OpDescriptor(object): "Instruction operator descriptor" # operator => AwlOperator or AwlIndirectOp # fieldCount => Number of consumed tokens def __init__(self, operator, fieldCount, stripLeadingChars=0): self.operator = operator self.fieldCount = fieldCount self.stripLeadingChars = stripLeadingChars # Make a deep copy def dup(self): return OpDescriptor(operator = self.operator.dup(), fieldCount = self.fieldCount, stripLeadingChars = self.stripLeadingChars) _AwlOpTranslator_mnemonics2constOperTab = None _AwlDataType = None def _getAwlDataTypeClass(): global _AwlDataType AwlDataType = _AwlDataType if AwlDataType is None: from awlsim.core.datatypes import AwlDataType _AwlDataType = AwlDataType return AwlDataType class AwlOpTranslator(object): "Instruction operator translator" CALC_OFFS = -1 NO_OFFS = -2 __english2german = { "I" : "E", "IB" : "EB", "IW" : "EW", "ID" : "ED", "Q" : "A", "QB" : "AB", "QW" : "AW", "QD" : "AD", "C" : "Z", "BR" : "BIE", "PIB" : "PEB", "PIW" : "PEW", "PID" : "PED", "PQB" : "PAB", "PQW" : "PAW", "PQD" : "PAD", } __german2english = pivotDict(__english2german) @classmethod def __getConstOperTable(cls): global _AwlOpTranslator_mnemonics2constOperTab if _AwlOpTranslator_mnemonics2constOperTab is not None: # Return the cached table. return _AwlOpTranslator_mnemonics2constOperTab operPi = make_AwlOperator(AwlOperatorTypes.IMM_REAL, 32, None, None) operPi.immediate = pyFloatToDWord(math.pi) operE = make_AwlOperator(AwlOperatorTypes.IMM_REAL, 32, None, None) operE.immediate = pyFloatToDWord(math.e) operPInf = make_AwlOperator(AwlOperatorTypes.IMM_REAL, 32, None, None) operPInf.immediate = floatConst.posInfDWord operNInf = make_AwlOperator(AwlOperatorTypes.IMM_REAL, 32, None, None) operNInf.immediate = floatConst.negInfDWord operPNaN = make_AwlOperator(AwlOperatorTypes.IMM_REAL, 32, None, None) operPNaN.immediate = floatConst.pNaNDWord operNNaN = make_AwlOperator(AwlOperatorTypes.IMM_REAL, 32, None, None) operNNaN.immediate = floatConst.nNaNDWord # Build the constant operator table for german mnemonics constOperTab_german = { "B" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.UNSPEC, 8, make_AwlOffset(cls.CALC_OFFS, cls.CALC_OFFS), None), 2), "W" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.UNSPEC, 16, make_AwlOffset(cls.CALC_OFFS, cls.CALC_OFFS), None), 2), "D" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.UNSPEC, 32, make_AwlOffset(cls.CALC_OFFS, cls.CALC_OFFS), None), 2), "==0" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_STW_Z, 1, make_AwlOffset(0, 0), None), 1), "<>0" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_STW_NZ, 1, make_AwlOffset(0, 0), None), 1), ">0" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_STW_POS, 1, make_AwlOffset(0, 0), None), 1), "<0" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_STW_NEG, 1, make_AwlOffset(0, 0), None), 1), ">=0" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_STW_POSZ, 1, make_AwlOffset(0, 0), None), 1), "<=0" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_STW_NEGZ, 1, make_AwlOffset(0, 0), None), 1), "OV" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_STW, 1, make_AwlOffset(0, 5), None), 1), "OS" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_STW, 1, make_AwlOffset(0, 4), None), 1), "UO" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_STW_UO, 1, make_AwlOffset(0, 0), None), 1), "BIE" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_STW, 1, make_AwlOffset(0, 8), None), 1), "E" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_E, 1, make_AwlOffset(cls.CALC_OFFS, cls.CALC_OFFS), None), 2), "EB" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_E, 8, make_AwlOffset(cls.CALC_OFFS, 0), None), 2), "EW" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_E, 16, make_AwlOffset(cls.CALC_OFFS, 0), None), 2), "ED" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_E, 32, make_AwlOffset(cls.CALC_OFFS, 0), None), 2), "A" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_A, 1, make_AwlOffset(cls.CALC_OFFS, cls.CALC_OFFS), None), 2), "AB" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_A, 8, make_AwlOffset(cls.CALC_OFFS, 0), None), 2), "AW" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_A, 16, make_AwlOffset(cls.CALC_OFFS, 0), None), 2), "AD" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_A, 32, make_AwlOffset(cls.CALC_OFFS, 0), None), 2), "L" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_L, 1, make_AwlOffset(cls.CALC_OFFS, cls.CALC_OFFS), None), 2), "LB" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_L, 8, make_AwlOffset(cls.CALC_OFFS, 0), None), 2), "LW" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_L, 16, make_AwlOffset(cls.CALC_OFFS, 0), None), 2), "LD" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_L, 32, make_AwlOffset(cls.CALC_OFFS, 0), None), 2), "M" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_M, 1, make_AwlOffset(cls.CALC_OFFS, cls.CALC_OFFS), None), 2), "MB" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_M, 8, make_AwlOffset(cls.CALC_OFFS, 0), None), 2), "MW" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_M, 16, make_AwlOffset(cls.CALC_OFFS, 0), None), 2), "MD" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_M, 32, make_AwlOffset(cls.CALC_OFFS, 0), None), 2), "T" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_T, 16, make_AwlOffset(cls.CALC_OFFS, 0), None), 2), "Z" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_Z, 16, make_AwlOffset(cls.CALC_OFFS, 0), None), 2), "FC" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.BLKREF_FC, 16, make_AwlOffset(cls.CALC_OFFS, 0), None), 2), "SFC" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.BLKREF_SFC, 16, make_AwlOffset(cls.CALC_OFFS, 0), None), 2), "FB" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.BLKREF_FB, 16, make_AwlOffset(cls.CALC_OFFS, 0), None), 2), "SFB" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.BLKREF_SFB, 16, make_AwlOffset(cls.CALC_OFFS, 0), None), 2), "UDT" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.BLKREF_UDT, 16, make_AwlOffset(cls.CALC_OFFS, 0), None), 2), "DB" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.BLKREF_DB, 16, make_AwlOffset(cls.CALC_OFFS, 0), None), 2), "DI" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.BLKREF_DI, 16, make_AwlOffset(cls.CALC_OFFS, 0), None), 2), "OB" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.BLKREF_OB, 16, make_AwlOffset(cls.CALC_OFFS, 0), None), 2), "VAT" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.BLKREF_VAT, 16, make_AwlOffset(cls.CALC_OFFS, 0), None), 2), "DBX" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_DB, 1, make_AwlOffset(cls.CALC_OFFS, cls.CALC_OFFS), None), 2), "DBB" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_DB, 8, make_AwlOffset(cls.CALC_OFFS, 0), None), 2), "DBW" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_DB, 16, make_AwlOffset(cls.CALC_OFFS, 0), None), 2), "DBD" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_DB, 32, make_AwlOffset(cls.CALC_OFFS, 0), None), 2), "DIX" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_DI, 1, make_AwlOffset(cls.CALC_OFFS, cls.CALC_OFFS), None), 2), "DIB" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_DI, 8, make_AwlOffset(cls.CALC_OFFS, 0), None), 2), "DIW" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_DI, 16, make_AwlOffset(cls.CALC_OFFS, 0), None), 2), "DID" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_DI, 32, make_AwlOffset(cls.CALC_OFFS, 0), None), 2), "DBLG" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_DBLG, 32, make_AwlOffset(0, 0), None), 1), "DBNO" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_DBNO, 32, make_AwlOffset(0, 0), None), 1), "DILG" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_DILG, 32, make_AwlOffset(0, 0), None), 1), "DINO" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_DINO, 32, make_AwlOffset(0, 0), None), 1), "PEB" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_PE, 8, make_AwlOffset(cls.CALC_OFFS, 0), None), 2), "PEW" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_PE, 16, make_AwlOffset(cls.CALC_OFFS, 0), None), 2), "PED" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_PE, 32, make_AwlOffset(cls.CALC_OFFS, 0), None), 2), "PAB" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_PA, 8, make_AwlOffset(cls.CALC_OFFS, 0), None), 2), "PAW" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_PA, 16, make_AwlOffset(cls.CALC_OFFS, 0), None), 2), "PAD" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_PA, 32, make_AwlOffset(cls.CALC_OFFS, 0), None), 2), "STW" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_STW, 16, make_AwlOffset(0, 0), None), 1), "AR2" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_AR2, 32, make_AwlOffset(2, 0), None), 1), "__STW" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_STW, 1, make_AwlOffset(0, cls.CALC_OFFS), None), 2), "__ACCU" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.VIRT_ACCU, 32, make_AwlOffset(cls.CALC_OFFS, 0), None), 2), "__AR" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.VIRT_AR, 32, make_AwlOffset(cls.CALC_OFFS, 0), None), 2), "__DBR" : OpDescriptor(make_AwlOperator(AwlOperatorTypes.VIRT_DBR, 16, make_AwlOffset(cls.CALC_OFFS, 0), None), 2), "__CNST_PI" : OpDescriptor(operPi, 1), "__CNST_E" : OpDescriptor(operE, 1), "__CNST_PINF" : OpDescriptor(operPInf, 1), "__CNST_NINF" : OpDescriptor(operNInf, 1), "__CNST_PNAN" : OpDescriptor(operPNaN, 1), "__CNST_NNAN" : OpDescriptor(operNNaN, 1), } # Create a constOperTab for english mnemonics constOperTab_english = {} for name, type in dictItems(constOperTab_german): try: name = cls.__german2english[name] except KeyError: pass constOperTab_english[name] = type # Mnemonics identifier to constOperTab lookup. mnemonics2constOperTab = { S7CPUConfig.MNEMONICS_DE : constOperTab_german, S7CPUConfig.MNEMONICS_EN : constOperTab_english, } # Cache the table globally for performance reasons. # This is safe, because the table entries are dup()ed # before being used. _AwlOpTranslator_mnemonics2constOperTab = mnemonics2constOperTab return mnemonics2constOperTab def __init__(self, insn=None, mnemonics=None): self.insn = insn if mnemonics is None and insn is not None: mnemonics = insn.getCpu().getConf().getMnemonics() assert(mnemonics is not None) self.mnemonics = mnemonics self.__mnemonics2constOperTab = self.__getConstOperTable() def __translateIndirectAddressing(self, opDesc, rawOps): #@cy cdef uint64_t area_u64 # rawOps starts _after_ the opening bracket '[' try: if rawOps[0].upper() in ("AR1", "AR2"): AwlDataType = _getAwlDataTypeClass() # Register-indirect access: "L W [AR1, P#0.0]" ar = { "AR1" : AwlIndirectOpConst.AR_1, "AR2" : AwlIndirectOpConst.AR_2, }[rawOps[0].upper()] if rawOps[1] != ',': raise AwlSimError("Missing comma in register-indirect " "addressing operator") offsetPtr, fields = AwlDataType.tryParseImmediate_Pointer(rawOps[2:]) if fields != 1: raise AwlSimError("Invalid offset pointer in " "register indirect addressing operator") if offsetPtr.width != 32: raise AwlSimError("Only plain pointers allowed as " "indirect addressing offset pointer.") if offsetPtr.getArea(): raise AwlSimError("Area internal pointer not " "allowed as indirect addressing offset pointer.") if rawOps[3] != ']': raise AwlSimError("Missing closing brackets in " "register indirect addressing operator") offsetOp = make_AwlOperator(operType=AwlOperatorTypes.IMM_PTR, width=32, offset=None, insn=opDesc.operator.insn) offsetOp.pointer = offsetPtr area = AwlIndirectOpConst.optype2area(opDesc.operator.operType) if area < 0: raise AwlSimError("Invalid memory area type in " "register indirect addressing operator") indirectOp = make_AwlIndirectOp(area=area, width=opDesc.operator.width, addressRegister=ar, offsetOper=offsetOp, insn=opDesc.operator.insn) fieldCount = 4 # ARx + comma + P# + ] else: # Indirect access: "L MW [MD 42]" # Find the end of the brackets. i, lvl = 0, 1 for i, tok in enumerate(rawOps): if tok == ']': lvl -= 1 elif tok == '[': lvl += 1 if lvl == 0: end = i break else: raise AwlSimError("Missing closing brackets in " "indirect addressing operator") # Translate the offset operator offsetOpDesc = self.translateOp(None, rawOps[:end]) if offsetOpDesc.fieldCount != end: raise AwlSimError("Invalid indirect addressing " "operator format. AR1/AR2 missing?") offsetOp = offsetOpDesc.operator if offsetOp.operType == AwlOperatorTypes.INDIRECT: raise AwlSimError("Only direct operators supported " "inside of indirect operator brackets.") area = AwlIndirectOpConst.optype2area(opDesc.operator.operType) if area < 0: raise AwlSimError("Invalid memory area type in " "indirect addressing operator") area_u64 = area if area_u64 == PointerConst.AREA_NONE_S: raise AwlSimError("No memory area code specified in " "indirect addressing operator") if area_u64 in (AwlIndirectOpConst.EXT_AREA_T_S, AwlIndirectOpConst.EXT_AREA_Z_S, AwlIndirectOpConst.EXT_AREA_BLKREF_DB_S, AwlIndirectOpConst.EXT_AREA_BLKREF_DI_S, AwlIndirectOpConst.EXT_AREA_BLKREF_FC_S, AwlIndirectOpConst.EXT_AREA_BLKREF_FB_S): expectedOffsetOpWidth = 16 else: expectedOffsetOpWidth = 32 if offsetOp.operType != AwlOperatorTypes.NAMED_LOCAL and\ offsetOp.width != expectedOffsetOpWidth: #TODO: We should also check for NAMED_LOCAL raise AwlSimError("Offset operator in " "indirect addressing operator has invalid width. " "Got %d bit, but expected %d bit." %\ (offsetOp.width, expectedOffsetOpWidth)) indirectOp = make_AwlIndirectOp(area=area, width=opDesc.operator.width, addressRegister=AwlIndirectOpConst.AR_NONE, offsetOper=offsetOp, insn=opDesc.operator.insn) fieldCount = offsetOpDesc.fieldCount + 1 # offsetOperator + ] except IndexError: raise AwlSimError("Invalid indirect addressing operator") # Adjust the operator descriptor opDesc.operator = indirectOp opDesc.fieldCount += fieldCount def __translateAddressOperator(self, opDesc, rawOps): if len(rawOps) < 1: raise AwlSimError("Missing address operator") if rawOps[0] == '[': # Indirect addressing self.__translateIndirectAddressing(opDesc, rawOps[1:]) return if opDesc.operator.operType == AwlOperatorTypes.UNSPEC: raise AwlSimError("No memory area specified in operator") # Direct addressing if opDesc.operator.width == 1: if opDesc.operator.offset.byteOffset == 0 and\ opDesc.operator.offset.bitOffset == self.CALC_OFFS: try: opDesc.operator.offset.bitOffset = int(rawOps[0], 10) except ValueError as e: if opDesc.operator.operType == AwlOperatorTypes.MEM_STW: opDesc.operator.offset.bitOffset =\ S7StatusWord.getBitnrByName(rawOps[0], self.mnemonics) else: raise AwlSimError("Invalid bit address") else: assert(opDesc.operator.offset.byteOffset == self.CALC_OFFS and\ opDesc.operator.offset.bitOffset == self.CALC_OFFS) offset = rawOps[0].split('.') if len(offset) != 2: raise AwlSimError("Invalid bit address") try: opDesc.operator.offset.byteOffset = int(offset[0], 10) opDesc.operator.offset.bitOffset = int(offset[1], 10) except ValueError as e: raise AwlSimError("Invalid bit address") elif opDesc.operator.width == 8: assert(opDesc.operator.offset.byteOffset == self.CALC_OFFS and\ opDesc.operator.offset.bitOffset == 0) try: opDesc.operator.offset.byteOffset = int(rawOps[0], 10) except ValueError as e: raise AwlSimError("Invalid byte address") elif opDesc.operator.width == 16: assert(opDesc.operator.offset.byteOffset == self.CALC_OFFS and\ opDesc.operator.offset.bitOffset == 0) try: opDesc.operator.offset.byteOffset = int(rawOps[0], 10) except ValueError as e: raise AwlSimError("Invalid word address") elif opDesc.operator.width == 32: assert(opDesc.operator.offset.byteOffset == self.CALC_OFFS and opDesc.operator.offset.bitOffset == 0) try: opDesc.operator.offset.byteOffset = int(rawOps[0], 10) except ValueError as e: raise AwlSimError("Invalid doubleword address") else: assert(0) if opDesc.operator.operType != AwlOperatorTypes.MEM_STW and\ opDesc.operator.offset.bitOffset > 7: raise AwlSimError("Invalid bit offset %d. " "Biggest possible bit offset is 7." %\ opDesc.operator.offset.bitOffset) def __transVarIdents(self, tokens): tokens, count = AwlDataIdentChain.expandTokens(tokens) # Parse DBx.VARIABLE or "DBname".VARIABLE adressing offset = None if len(tokens) >= 3 and\ tokens[1] == '.': if tokens[0].startswith("DB") and\ isdecimal(tokens[0][2:]): # DBx.VARIABLE offset = make_AwlOffset(self.NO_OFFS, self.NO_OFFS) offset.dbNumber = int(tokens[0][2:]) elif tokens[0].startswith('"') and\ tokens[0].endswith('"'): # "DBname".VARIABLE offset = make_AwlOffset(self.NO_OFFS, self.NO_OFFS) offset.dbName = tokens[0][1:-1] if offset: # Parse the variable idents. offset.identChain = AwlDataIdentChain.parseTokens(tokens[2:]) if not offset and\ len(tokens) >= 1 and\ tokens[0].startswith('#'): tokens[0] = tokens[0][1:] # Strip the '#' from the first token offset = make_AwlOffset(self.NO_OFFS, self.NO_OFFS) offset.identChain = AwlDataIdentChain.parseTokens(tokens) if not offset: count = 0 return offset, count def __doTrans(self, rawInsn, rawOps): assert(len(rawOps) >= 1) token0 = rawOps[0] token0Upper = token0.upper() if rawInsn and rawInsn.block.hasLabel(token0): # Label reference oper = make_AwlOperator(AwlOperatorTypes.LBL_REF, 0, None, None) oper.immediateStr = token0 return OpDescriptor(oper, 1) # Constant operator (from table) operTable = self.__mnemonics2constOperTab[self.mnemonics] opDesc = operTable.get(token0Upper, None) if opDesc is not None: return opDesc.dup() # Bitwise indirect addressing if token0Upper == '[': # This is special case for the "U [AR1,P#0.0]" bitwise addressing. # Create a descriptor for the (yet) unspecified bitwise access. opDesc = OpDescriptor(make_AwlOperator(AwlOperatorTypes.UNSPEC, 1, make_AwlOffset(self.CALC_OFFS, self.CALC_OFFS), None), 1) # And hand over to indirect address parsing. self.__translateIndirectAddressing(opDesc, rawOps[1:]) assert(opDesc.operator.operType != AwlOperatorTypes.UNSPEC) return opDesc # Local variable if token0Upper.startswith('#'): offset, count = self.__transVarIdents(rawOps) if not offset: raise AwlSimError("Failed to parse variable name: %s" %\ "".join(rawOps)) return OpDescriptor(make_AwlOperator(AwlOperatorTypes.NAMED_LOCAL, 0, offset, None), count) # Pointer to local variable if token0Upper.startswith("P##"): offset = make_AwlOffset(self.NO_OFFS, self.NO_OFFS) # Doesn't support struct or array indexing. # Parse it as one identification. offset.identChain = AwlDataIdentChain( [ AwlDataIdent(token0[3:]), ] ) return OpDescriptor(make_AwlOperator(AwlOperatorTypes.NAMED_LOCAL_PTR, 0, offset, None), 1) # Symbolic name if token0Upper.startswith('"') and token0Upper.endswith('"'): offset = make_AwlOffset(self.NO_OFFS, self.NO_OFFS) offset.identChain = AwlDataIdentChain( [ AwlDataIdent(token0[1:-1], doValidateName = False), ] ) return OpDescriptor(make_AwlOperator(AwlOperatorTypes.SYMBOLIC, 0, offset, None), 1) AwlDataType = _getAwlDataTypeClass() # Immediate boolean immediate = AwlDataType.tryParseImmediate_BOOL(token0) if immediate is not None: immediate &= 1 oper = make_AwlOperator(AwlOperatorTypes.IMM, 1, None, None) oper.immediate = immediate return OpDescriptor(oper, 1) # Immediate integer immediate = AwlDataType.tryParseImmediate_INT(token0) if immediate is not None: immediate &= 0xFFFF oper = make_AwlOperator(AwlOperatorTypes.IMM, 16, None, None) oper.immediate = immediate return OpDescriptor(oper, 1) # Immediate float immediate = AwlDataType.tryParseImmediate_REAL(token0) if immediate is not None: immediate &= 0xFFFFFFFF oper = make_AwlOperator(AwlOperatorTypes.IMM_REAL, 32, None, None) oper.immediate = immediate return OpDescriptor(oper, 1) # S5Time immediate immediate = AwlDataType.tryParseImmediate_S5T(token0) if immediate is not None: immediate &= 0xFFFF oper = make_AwlOperator(AwlOperatorTypes.IMM_S5T, 16, None, None) oper.immediate = immediate return OpDescriptor(oper, 1) # Time immediate immediate = AwlDataType.tryParseImmediate_TIME(token0) if immediate is not None: immediate &= 0xFFFFFFFF oper = make_AwlOperator(AwlOperatorTypes.IMM_TIME, 32, None, None) oper.immediate = immediate return OpDescriptor(oper, 1) # TIME_OF_DAY immediate immediate = AwlDataType.tryParseImmediate_TOD(token0) if immediate is not None: immediate &= 0xFFFFFFFF oper = make_AwlOperator(AwlOperatorTypes.IMM_TOD, 32, None, None) oper.immediate = immediate return OpDescriptor(oper, 1) # DATE immediate immediate = AwlDataType.tryParseImmediate_DATE(token0) if immediate is not None: immediate &= 0xFFFF oper = make_AwlOperator(AwlOperatorTypes.IMM_DATE, 16, None, None) oper.immediate = immediate return OpDescriptor(oper, 1) # DATE_AND_TIME immediate immediate = AwlDataType.tryParseImmediate_DT(rawOps) if immediate is not None: oper = make_AwlOperator(AwlOperatorTypes.IMM_DT, len(immediate) * 8, None, None) oper.immediateBytes = immediate return OpDescriptor(oper, 5) # Pointer immediate pointer, fields = AwlDataType.tryParseImmediate_Pointer(rawOps) if pointer is not None: oper = make_AwlOperator(AwlOperatorTypes.IMM_PTR, pointer.width, None, None) oper.pointer = pointer return OpDescriptor(oper, fields) # Binary immediate immediate = AwlDataType.tryParseImmediate_Bin(token0) if immediate is not None: immediate &= 0xFFFFFFFF size = 32 if (immediate > 0xFFFF) else 16 oper = make_AwlOperator(AwlOperatorTypes.IMM, size, None, None) oper.immediate = immediate return OpDescriptor(oper, 1) # Byte array immediate immediate, fields = AwlDataType.tryParseImmediate_ByteArray(rawOps) if immediate is not None: size = 32 if fields == 10 else 16 oper = make_AwlOperator(AwlOperatorTypes.IMM, size, None, None) oper.immediate = immediate return OpDescriptor(oper, fields) # Hex byte immediate immediate = AwlDataType.tryParseImmediate_HexByte(token0) if immediate is not None: immediate &= 0xFF oper = make_AwlOperator(AwlOperatorTypes.IMM, 8, None, None) oper.immediate = immediate return OpDescriptor(oper, 1) # Hex word immediate immediate = AwlDataType.tryParseImmediate_HexWord(token0) if immediate is not None: immediate &= 0xFFFF oper = make_AwlOperator(AwlOperatorTypes.IMM, 16, None, None) oper.immediate = immediate return OpDescriptor(oper, 1) # Hex dword immediate immediate = AwlDataType.tryParseImmediate_HexDWord(token0) if immediate is not None: immediate &= 0xFFFFFFFF oper = make_AwlOperator(AwlOperatorTypes.IMM, 32, None, None) oper.immediate = immediate return OpDescriptor(oper, 1) # Long integer immediate immediate = AwlDataType.tryParseImmediate_DINT(token0) if immediate is not None: immediate &= 0xFFFFFFFF oper = make_AwlOperator(AwlOperatorTypes.IMM, 32, None, None) oper.immediate = immediate return OpDescriptor(oper, 1) # BCD word immediate immediate = AwlDataType.tryParseImmediate_BCD_word(token0) if immediate is not None: immediate &= 0xFFFF oper = make_AwlOperator(AwlOperatorTypes.IMM, 16, None, None) oper.immediate = immediate return OpDescriptor(oper, 1) # String immediate immediate = AwlDataType.tryParseImmediate_STRING(token0) if immediate is not None: oper = make_AwlOperator(AwlOperatorTypes.IMM_STR, len(immediate) * 8, None, None) oper.immediateBytes = immediate return OpDescriptor(oper, 1) # DBx.DBX/B/W/D addressing match = re.match(r'^DB(\d+)\.DB([XBWD])$', token0) if match: dbNumber = int(match.group(1)) width = { "X" : 1, "B" : 8, "W" : 16, "D" : 32, }[match.group(2)] offset = make_AwlOffset(self.CALC_OFFS, self.CALC_OFFS if (width == 1) else 0) offset.dbNumber = dbNumber return OpDescriptor(make_AwlOperator(AwlOperatorTypes.MEM_DB, width, offset, None), 2) # Try to parse DBx.VARIABLE or "DBname".VARIABLE adressing offset, count = self.__transVarIdents(rawOps) if offset: return OpDescriptor(make_AwlOperator(AwlOperatorTypes.NAMED_DBVAR, 0, offset, None), count) # Try convenience operators. # A convenience operator is one that lacks otherwise required white space. # We thus actively support lazy programmers, yay. # For example: # = M0.0 # (Note the missing white space between M and 0.0) for name, opDesc in dictItems(operTable): if opDesc.operator.offset is not None and\ opDesc.operator.offset.byteOffset >= 0 and\ opDesc.operator.offset.bitOffset >= 0: # Only for operators with bit/byte addresses. continue try: # Try convenience operator if token0Upper.startswith(name) and\ token0Upper[len(name)].isdigit(): opDesc = opDesc.dup() opDesc.stripLeadingChars = len(name) opDesc.fieldCount -= 1 return opDesc except IndexError: pass raise AwlSimError("Cannot parse operator: " +\ str(token0)) def translateOp(self, rawInsn, rawOps): """Translate operator tokens. Returns an OpDescriptor(). """ if not rawOps: raise AwlSimError("Cannot parse operator: Operator is empty") opDesc = self.__doTrans(rawInsn, rawOps) if opDesc.operator.offset is not None and\ (opDesc.operator.offset.byteOffset == self.CALC_OFFS or\ opDesc.operator.offset.bitOffset == self.CALC_OFFS): if opDesc.stripLeadingChars: strippedRawOps = rawOps[:] strippedRawOps[0] = strippedRawOps[0][opDesc.stripLeadingChars : ] self.__translateAddressOperator(opDesc, strippedRawOps) else: self.__translateAddressOperator(opDesc, rawOps[1:]) assert(opDesc.operator.operType != AwlOperatorTypes.UNSPEC) opDesc.operator.setExtended(rawOps[0].startswith("__")) opDesc.operator.setInsn(self.insn) return opDesc def __translateParameterList(self, rawInsn, rawOps): while rawOps: if len(rawOps) < 3: raise AwlSimError("Invalid parameter assignment") if rawOps[1] != ':=': raise AwlSimError("Missing assignment operator (:=) " "in parameter assignment") # Extract l-value and r-value commaIdx = listIndex(rawOps, ',', 2) lvalueName = rawOps[0] if commaIdx < 0: rvalueTokens = rawOps[2:] else: rvalueTokens = rawOps[2:commaIdx] if not rvalueTokens: raise AwlSimError("No R-Value in parameter assignment") # Translate r-value opDesc = self.translateOp(None, rvalueTokens) # Create assignment param = AwlParamAssign(lvalueName, opDesc.operator) if self.insn: self.insn.params = self.insn.params + (param,) rawOps = rawOps[opDesc.fieldCount + 2 : ] if rawOps: if rawOps[0] == ',': rawOps = rawOps[1:] else: raise AwlSimError("Missing comma in parameter list") def translateFromRawInsn(self, rawInsn): """Translate operators from rawInsn and add them to self.insn. """ rawOps = rawInsn.getOperators() while rawOps: opDesc = self.translateOp(rawInsn, rawOps) if self.insn: self.insn.ops.append(opDesc.operator) if len(rawOps) > opDesc.fieldCount: if rawInsn.name.upper() == "CALL" and\ rawOps[opDesc.fieldCount] == '(': try: endIdx = rawOps.index(')', opDesc.fieldCount) except ValueError: raise AwlSimError("Missing closing parenthesis") # Translate the call parameters self.__translateParameterList(rawInsn, rawOps[opDesc.fieldCount + 1: endIdx]) # Consume all tokens between (and including) parenthesis. opDesc.fieldCount = endIdx + 1 if len(rawOps) > opDesc.fieldCount: raise AwlSimError("Trailing character after closing parenthesis") elif rawOps[opDesc.fieldCount] == ',': opDesc.fieldCount += 1 # Consume comma if len(rawOps) <= opDesc.fieldCount: raise AwlSimError("Trailing comma") else: raise AwlSimError("Missing comma in operator list") rawOps = rawOps[opDesc.fieldCount : ] def __tokenizeString(self, string): """Split a string into operator tokens. """ tokens, s, inSingleQuote, inDoubleQuote = [], [], False, False for c in string: if inSingleQuote or inDoubleQuote: if (inSingleQuote and c == "'") or\ (inDoubleQuote and c == '"'): inSingleQuote, inDoubleQuote = False, False s.append(c) tokens.append("".join(s)) s = [] else: s.append(c) else: if c in {'"', "'"}: inSingleQuote, inDoubleQuote = (c == "'"), (c == '"') if s: tokens.append("".join(s)) s = [c] elif c.isspace(): if s: tokens.append("".join(s)) s = [] else: s.append(c) if s: tokens.append("".join(s)) return tokens def translateFromString(self, string): """Translate an operator string. Returns an OpDescriptor(). """ return self.translateOp(rawInsn=None, rawOps=self.__tokenizeString(string))