Commit bb4eb1ee authored by Michael Büsch's avatar Michael Büsch

Add support for symbolic fully qualified pointers.

This is an awlsim extension.
Signed-off-by: Michael Büsch's avatarMichael Buesch <m@bues.ch>
parent 8b97b4b2
......@@ -66,3 +66,23 @@ Awlsim extensions
characters. Only actual-parameters with exactly the specified max-size as
specified in the FC interface are allowed in the CALL assignment.
(One exception being STRING immediates. See above.)
- Awlsim supports pointer immediates to named DB variables.
Whether a 32 bit pointer (area spanning), a 48 bit DB pointer or a 80 bit
ANY pointer is generated, depends on the context.
For example:
// Load a pointer to VARIABLE with DBX area code into accu 1.
// Note that the DB number information is lost (32 bit pointer).
L P#DB1.VARIABLE
// Pass pointer immediates as actual values in calls.
// Values are passed as DB pointer or ANY pointer, according to the
// parameter type.
CALL FC 1 (
POINTER_VAR := P#DB1.VARIABLE,
ANY_VAR := P#DB1.VARIABLE,
)
However, for the pointer parameter passing in CALL you could just write it
in an S7 compatible way without the P# prefix.
......@@ -187,39 +187,76 @@ class S7CPU(object): #+cdef
yield block
def __allCallInsns(self, block):
resolver = AwlSymResolver(self)
for insn in block.insns:
if insn.insnType != AwlInsn.TYPE_CALL:
continue
# Get the code and DB blocks
# Get the DB block, if any.
if len(insn.ops) == 1:
dataBlock = None
elif len(insn.ops) == 2:
dataBlockOp = insn.ops[1]
if dataBlockOp.type == AwlOperator.SYMBOLIC:
blockIndex, symbol = resolver.resolveBlockName(
{AwlDataType.TYPE_FB_X,
AwlDataType.TYPE_SFB_X},
dataBlockOp.value.identChain.getString())
dataBlockOp = symbol.operator.dup()
dataBlockIndex = dataBlockOp.value.byteOffset
try:
if dataBlockOp.type == AwlOperator.BLKREF_DB:
dataBlock = self.dbs[dataBlockIndex]
else:
raise AwlSimError("Data block operand "
"in CALL is not a DB.",
insn=insn)
except KeyError as e:
raise AwlSimError("Data block '%s' referenced "
"in CALL does not exist." %\
str(dataBlockOp),
insn=insn)
# Get the code block, if any.
codeBlockOp = insn.ops[0]
if codeBlockOp.type == AwlOperator.SYMBOLIC:
blockIndex, symbol = resolver.resolveBlockName(
{AwlDataType.TYPE_FC_X,
AwlDataType.TYPE_FB_X,
AwlDataType.TYPE_SFC_X,
AwlDataType.TYPE_SFB_X},
codeBlockOp.value.identChain.getString())
codeBlockOp = symbol.operator.dup()
elif codeBlockOp.type == AwlOperator.NAMED_LOCAL:
codeBlockOp = resolver.resolveNamedLocal(block, insn, codeBlockOp)
if codeBlockOp.type in {AwlOperator.MULTI_FB,
AwlOperator.MULTI_SFB}:
codeBlockIndex = codeBlockOp.value.fbNumber
else:
codeBlockIndex = codeBlockOp.value.byteOffset
try:
if len(insn.ops) == 1:
dataBlock = None
elif len(insn.ops) == 2:
dataBlockOp = insn.ops[1]
if dataBlockOp.type != AwlOperator.BLKREF_DB:
raise ValueError
dataBlock = self.dbs[dataBlockOp.value.byteOffset]
else:
assert(0)
codeBlockOp = insn.ops[0]
if codeBlockOp.type == AwlOperator.BLKREF_FC:
codeBlock = self.fcs[codeBlockOp.value.byteOffset]
elif codeBlockOp.type == AwlOperator.BLKREF_FB:
codeBlock = self.fbs[codeBlockOp.value.byteOffset]
codeBlock = self.fcs[codeBlockIndex]
elif codeBlockOp.type in {AwlOperator.BLKREF_FB,
AwlOperator.MULTI_FB}:
codeBlock = self.fbs[codeBlockIndex]
elif codeBlockOp.type == AwlOperator.BLKREF_SFC:
codeBlock = self.sfcs[codeBlockOp.value.byteOffset]
elif codeBlockOp.type == AwlOperator.BLKREF_SFB:
codeBlock = self.sfbs[codeBlockOp.value.byteOffset]
elif codeBlockOp.type == AwlOperator.MULTI_FB:
codeBlock = self.fbs[codeBlockOp.value.fbNumber]
elif codeBlockOp.type == AwlOperator.MULTI_SFB:
codeBlock = self.sfbs[codeBlockOp.value.fbNumber]
codeBlock = self.sfcs[codeBlockIndex]
elif codeBlockOp.type in {AwlOperator.BLKREF_SFB,
AwlOperator.MULTI_SFB}:
codeBlock = self.sfbs[codeBlockIndex]
else:
raise ValueError
except (KeyError, ValueError) as e:
# This error is handled later in call-insn sanity checks
continue
raise AwlSimError("Code block operand "
"in CALL is not a valid code block "
"(FB, FC, SFB or SFC).",
insn=insn)
except KeyError as e:
raise AwlSimError("Code block '%s' referenced in "
"CALL does not exist." %\
str(codeBlockOp),
insn=insn)
yield insn, codeBlock, dataBlock
......@@ -242,8 +279,6 @@ class S7CPU(object): #+cdef
for insn, calledCodeBlock, calledDataBlock in self.__allCallInsns(block):
try:
for param in insn.params:
# Add interface references to the parameter assignment.
param.interface = calledCodeBlock.interface
# Final translation of parameter assignment operand.
translator.translateParamAssignOper(param)
except AwlSimError as e:
......@@ -258,10 +293,23 @@ class S7CPU(object): #+cdef
for block in self.__allUserCodeBlocks():
self.__finalizeCodeBlock(block)
# Assign call parameter interface reference.
def __assignParamInterface(self, block):
for insn, calledCodeBlock, calledDataBlock in self.__allCallInsns(block):
try:
for param in insn.params:
# Add interface references to the parameter assignment.
param.interface = calledCodeBlock.interface
except AwlSimError as e:
e.setInsn(insn)
raise e
# Resolve all symbols (global and local) on all blocks, as far as possible.
def __resolveSymbols(self):
resolver = AwlSymResolver(self)
for block in self.__allCodeBlocks():
# Add interface references to the parameter assignment.
self.__assignParamInterface(block)
# Check type compatibility between formal and
# actual parameter of calls.
self.__checkCallParamTypeCompat(block)
......@@ -290,7 +338,7 @@ class S7CPU(object): #+cdef
# Translate UDTs
for udtNumber, rawUDT in parseTree.udts.items():
udtNumber, sym = resolver.resolveBlockName((AwlDataType.TYPE_UDT_X,),
udtNumber, sym = resolver.resolveBlockName({AwlDataType.TYPE_UDT_X},
udtNumber)
if udtNumber in self.udts:
raise AwlSimError("Multiple definitions of "\
......@@ -304,7 +352,7 @@ class S7CPU(object): #+cdef
# Translate OBs
for obNumber, rawOB in parseTree.obs.items():
obNumber, sym = resolver.resolveBlockName((AwlDataType.TYPE_OB_X,),
obNumber, sym = resolver.resolveBlockName({AwlDataType.TYPE_OB_X},
obNumber)
if obNumber in self.obs and\
self.obs[obNumber].insns:
......@@ -322,7 +370,7 @@ class S7CPU(object): #+cdef
# Translate FBs
for fbNumber, rawFB in parseTree.fbs.items():
fbNumber, sym = resolver.resolveBlockName((AwlDataType.TYPE_FB_X,),
fbNumber, sym = resolver.resolveBlockName({AwlDataType.TYPE_FB_X},
fbNumber)
if fbNumber in self.fbs:
extra = ""
......@@ -339,7 +387,7 @@ class S7CPU(object): #+cdef
# Translate FCs
for fcNumber, rawFC in parseTree.fcs.items():
fcNumber, sym = resolver.resolveBlockName((AwlDataType.TYPE_FC_X,),
fcNumber, sym = resolver.resolveBlockName({AwlDataType.TYPE_FC_X},
fcNumber)
if fcNumber in self.fcs:
extra = ""
......@@ -376,9 +424,9 @@ class S7CPU(object): #+cdef
# Translate DBs
for dbNumber, rawDB in parseTree.dbs.items():
dbNumber, sym = resolver.resolveBlockName((AwlDataType.TYPE_DB_X,
dbNumber, sym = resolver.resolveBlockName({AwlDataType.TYPE_DB_X,
AwlDataType.TYPE_FB_X,
AwlDataType.TYPE_SFB_X),
AwlDataType.TYPE_SFB_X},
dbNumber)
if dbNumber in self.dbs:
raise AwlSimError("Multiple definitions of "\
......
......@@ -28,6 +28,7 @@ from awlsim.common.immutable import *
from awlsim.core.util import *
from awlsim.core.timers import *
from awlsim.core.offset import *
from awlsim.core.identifier import *
import datetime
......@@ -951,23 +952,33 @@ class AwlDataType(OptionalImmutable):
@classmethod
def tryParseImmediate_Pointer(cls, tokens):
prefix = tokens[0].upper()
if not prefix.startswith("P#"):
prefix = tokens[0]
if not prefix.upper().startswith("P#"):
return None, None
prefix = prefix[2:] # Strip P#
pointer = None
dotIdx = prefix.find(".")
if dotIdx > 0 and prefix[:dotIdx].startswith("DB") and\
if dotIdx >= 3 and prefix[:dotIdx].upper().startswith("DB") and\
prefix[2:dotIdx].isdecimal():
# Parse DB number prefix
dbNr = int(prefix[2:dotIdx])
prefix = prefix[dotIdx+1:]
pointer = DBPointer(0, dbNr)
else:
elif dotIdx >= 3 and prefix.startswith('"'):
endIdx = prefix[1:].find('".') + 1
if endIdx >= 2:
# Parse symbolic DB name prefix
dbSymbol = prefix[1:endIdx]
prefix = prefix[endIdx+2:]
pointer = SymbolicDBPointer(dbSymbol = dbSymbol)
if pointer is None:
# There is no DB number.
pointer = Pointer(0)
prefix = prefix.upper()
try:
if prefix == "P":
ptr = cls.__parsePtrOffset(tokens[1]) |\
......@@ -1005,6 +1016,17 @@ class AwlDataType(OptionalImmutable):
pointer.setDWord(ptr)
nrTokens = 2
else:
if isinstance(pointer, DBPointer) and\
prefix and not prefix[0].isdecimal():
# This is a named DB variable pointer.
# (awlsim extension).
if not isinstance(pointer, SymbolicDBPointer):
pointer = SymbolicDBPointer(pointer.toPointerValue(),
pointer.dbNr)
tokens = [ prefix ] + tokens[1:]
tokens, nrTokens = AwlDataIdentChain.expandTokens(tokens)
pointer.identChain = AwlDataIdentChain.parseTokens(tokens)
return pointer, nrTokens
# Area-internal pointer
ptr = cls.__parsePtrOffset(prefix)
pointer.setDWord(ptr)
......@@ -1389,6 +1411,11 @@ class Pointer(GenericDWord): #+cdef
def __init__(self, ptrValue = 0):
GenericDWord.__init__(self, ptrValue)
# Get the pointer (32 bit).
def toPointer(self): #@nocy
#@cy cpdef toPointer(self):
return Pointer(self.toPointerValue())
# Get the 32 bit pointer value.
toPointerValue = GenericDWord.get #@nocy
#@cy cpdef uint32_t toPointerValue(self):
......@@ -1424,6 +1451,11 @@ class Pointer(GenericDWord): #+cdef
def area(self):
return (self.toPointerValue() >> 24) & 0xFF
# Set the area code, as byte.
@area.setter
def area(self, newArea):
self.set((self.get() & 0x00FFFFFF) | (newArea << 24))
# Get the byte offset, as word.
@property
def byteOffset(self):
......@@ -1515,6 +1547,52 @@ class DBPointer(Pointer): #+cdef
return Pointer.toPointerString(self)
return "P#%s%d.%d" % (prefix, self.byteOffset, self.bitOffset)
class SymbolicDBPointer(DBPointer): #+cdef
"""Symbolic DB-Pointer value.
This is a non-standard awlsim extension.
Example:
P#DB100.ARRAYVAR[1].ELEMENT
Example with symbolic DB:
P#"MyData".ARRAYVAR[1].ELEMENT
"""
__slots__ = (
"identChain",
"dbSymbol",
)
#@cy cdef public object identChain
#@cy cdef public object dbSymbol
# Width, in bits.
width = -1 # Unknown width
def __init__(self, ptrValue = 0, dbNr = 0,
identChain = None, dbSymbol = None):
DBPointer.__init__(self, ptrValue, dbNr)
self.identChain = identChain
self.dbSymbol = dbSymbol
# Get a P#... string for this pointer.
def toPointerString(self):
if self.dbSymbol or self.dbNr:
if self.dbSymbol:
prefix = '"%s".' % self.dbSymbol
else:
prefix = "DB%d." % self.dbNr
if self.identChain:
return "P#%s%s" % (prefix,
self.identChain.getString())
else:
if self.area == self.AREA_DB:
prefix += "DBX "
else:
prefix += "(%02X) " % self.area
return "P#%s%d.%d" % (prefix,
self.byteOffset, self.bitOffset)
else:
assert(not self.identChain)
return Pointer.toPointerString(self)
class ANYPointer(DBPointer): #+cdef
"""ANY-Pointer value.
The basic data type (DBPointer) holds the pointer value,
......@@ -1559,10 +1637,60 @@ class ANYPointer(DBPointer): #+cdef
# Width, in bits.
width = 80
# Make an ANYPointer instance based on the data area width (in bits).
# Automatically selects an appropriate data type and count.
@classmethod
def makeByTypeWidth(cls, bitWidth, ptrValue = 0, dbNr = 0):
if bitWidth % 32 == 0:
dataType = AwlDataType.makeByName("DWORD")
count = bitWidth // 32
elif bitWidth % 16 == 0:
dataType = AwlDataType.makeByName("WORD")
count = bitWidth // 16
elif bitWidth % 8 == 0:
dataType = AwlDataType.makeByName("BYTE")
count = bitWidth // 8
else:
dataType = AwlDataType.makeByName("BOOL")
count = bitWidth
return cls(ptrValue = ptrValue,
dbNr = dbNr,
dataType = dataType,
count = count)
# Create an ANY pointer to a typed data field.
# Select the right ANY data type automatically.
@classmethod
def makeByAutoType(cls, dataType, ptrValue = 0, dbNr = 0):
if dataType.type == AwlDataType.TYPE_ARRAY and\
cls.dataTypeIsSupported(dataType.arrayElementType):
return cls(ptrValue = ptrValue,
dbNr = dbNr,
dataType = dataType.arrayElementType,
count = dataType.arrayGetNrElements())
elif cls.dataTypeIsSupported(dataType):
return cls(ptrValue = ptrValue,
dbNr = dbNr,
dataType = dataType,
count = 1)
else:
return cls.makeByTypeWidth(bitWidth = dataType.width,
ptrValue = ptrValue,
dbNr = dbNr)
# Check whether ANY supports the data type.
@classmethod
def dataTypeIsSupported(cls, dataType):
return dataType and\
dataType.type in cls.__typeId2typeCode
def __init__(self, ptrValue = 0, dbNr = 0, dataType = None, count = 1):
DBPointer.__init__(self, ptrValue, dbNr)
if not dataType:
dataType = AwlDataType.makeByName("NIL")
if not self.dataTypeIsSupported(dataType):
raise AwlSimError("Data type '%s' is not allowed "
"in ANY pointers." % str(dataType))
self.dataType = dataType
self.count = count
......
......@@ -211,6 +211,28 @@ class AwlDataIdentChain(object):
identChain.idents.append(AwlDataIdent(tokens[0], indices))
return identChain
# Expand an identifier token list.
# Returns a tuple (tokenList, countOfConsumedTokens).
@classmethod
def expandTokens(cls, tokens):
# Find the end of this identifier.
# Identifiers are delimited by ',' or '('
inBrackets, endIdx = False, 0
while endIdx < len(tokens) and\
((tokens[endIdx] != ',' and tokens[endIdx] != '(') or\
inBrackets):
if tokens[endIdx] == '[':
inBrackets = True
elif tokens[endIdx] == ']':
inBrackets = False
endIdx += 1
# Split all ops by '.'
tokens = listExpand(tokens[:endIdx],
lambda e: strPartitionFull(e, '.', keepEmpty=False))
return tokens, endIdx
def __init__(self, idents=None):
"""idents -> A list of AwlDataIdent instances."""
......
......@@ -183,6 +183,10 @@ class AwlOperator(DynAttrs):
# Set to true for accesses > 32 bit or
# arrays/structs or array/struct elements.
"compound" : False,
# The access data type (AwlDataType), if known.
# Only set for resolved symbolic accesses.
"dataType" : None,
}
def __init__(self, type, width, value, insn=None):
......@@ -350,26 +354,17 @@ class AwlOperator(DynAttrs):
# Make an ANY-pointer to this memory area.
# Returns an ANYPointer().
def makeANYPointer(self, areaShifted=None):
if self.width % 32 == 0:
dataType = AwlDataType.makeByName("DWORD")
count = self.width // 32
elif self.width % 16 == 0:
dataType = AwlDataType.makeByName("WORD")
count = self.width // 16
elif self.width % 8 == 0:
dataType = AwlDataType.makeByName("BYTE")
count = self.width // 8
else:
dataType = AwlDataType.makeByName("BOOL")
count = self.width
ptrValue = self.makePointerValue()
if areaShifted:
ptrValue &= ~Pointer.AREA_MASK_S
ptrValue |= areaShifted
return ANYPointer(ptrValue = ptrValue,
dbNr = self.value.dbNumber,
dataType = dataType,
count = count)
if ANYPointer.dataTypeIsSupported(self.dataType):
return ANYPointer.makeByAutoType(dataType = self.dataType,
ptrValue = ptrValue,
dbNr = self.value.dbNumber)
return ANYPointer.makeByTypeWidth(bitWidth = self.width,
ptrValue = ptrValue,
dbNr = self.value.dbNumber)
def __repr__(self):
if self.type == self.IMM:
......@@ -563,6 +558,9 @@ class AwlIndirectOp(AwlOperator):
# Map for converting operator type to area code
optype2area = pivotDict(area2optype_fetch)
optype2area[AwlOperator.MEM_PA] = AREA_P
optype2area[AwlOperator.MULTI_FB] = AREA_DI
optype2area[AwlOperator.MULTI_SFB] = AREA_DI
optype2area[AwlOperator.NAMED_DBVAR] = AREA_DB
optype2area[AwlOperator.UNSPEC] = AREA_NONE
def __init__(self, area, width, addressRegister, offsetOper, insn=None):
......
......@@ -394,21 +394,7 @@ class AwlOpTranslator(object):
opDesc.operator.value.bitOffset)
def __transVarIdents(self, tokens):
# Find the end of this operator.
# Operators are delimited by ',' or '('
inBrackets, endIdx = False, 0
while endIdx < len(tokens) and\
((tokens[endIdx] != ',' and tokens[endIdx] != '(') or\
inBrackets):
if tokens[endIdx] == '[':
inBrackets = True
elif tokens[endIdx] == ']':
inBrackets = False
endIdx += 1
# Split all ops by '.'
tokens = listExpand(tokens[:endIdx],
lambda e: strPartitionFull(e, '.', keepEmpty=False))
tokens, count = AwlDataIdentChain.expandTokens(tokens)
# Parse DBx.VARIABLE or "DBname".VARIABLE adressing
offset = None
......@@ -433,7 +419,8 @@ class AwlOpTranslator(object):
tokens[0] = tokens[0][1:] # Strip the '#' from the first token
offset = AwlOffset(None, None)
offset.identChain = AwlDataIdentChain.parseTokens(tokens)
count = endIdx if offset else 0
if not offset:
count = 0
return offset, count
def __doTrans(self, rawInsn, rawOps):
......
......@@ -337,6 +337,9 @@ class AwlTranslator(object):
width = ptr.width,
value = ptr,
insn = param.rvalueOp.insn)
if param.rvalueOp.value.area == Pointer.AREA_L:
# L-stack access must be translated to VL.
param.rvalueOp.value.area = Pointer.AREA_VL
elif param.lValueDataType.type == AwlDataType.TYPE_ANY:
# ANY-pointer parameter.
if param.rvalueOp.type == AwlOperator.IMM_PTR:
......@@ -359,6 +362,9 @@ class AwlTranslator(object):
width = ptr.width,
value = ptr,
insn = param.rvalueOp.insn)
if param.rvalueOp.value.area == Pointer.AREA_L:
# L-stack access must be translated to VL.
param.rvalueOp.value.area = Pointer.AREA_VL
# Translate STRING immediates.
# Converts STRING to CHAR, if required, or expands string lengths.
......@@ -532,16 +538,17 @@ class AwlSymResolver(object):
if dataType.type == AwlDataType.TYPE_ARRAY and\
not isWholeArrayAccess:
# This is an array element access.
# Store the element-access-width in the operator.
oper.width = dataType.arrayElementType.width
accessDataType = dataType.arrayElementType
elif dataType.type == AwlDataType.TYPE_STRING and\
not isWholeArrayAccess:
# This is a string single character access.
oper.width = 8
accessDataType = AwlDataType.makeByName("CHAR")
else:
# Non-array access or whole-array access.
# Store the field access width in the operator.
oper.width = dataType.width
accessDataType = dataType
# Store the access type and width in the operator.
oper.dataType = accessDataType
oper.width = accessDataType.width
assert(oper.width > 0)
# Store the sub-offset (might be zero).
......@@ -562,6 +569,7 @@ class AwlSymResolver(object):
assert(newOper.width > 0)
newOper.setInsn(oper.insn)
newOper.compound = oper.compound
newOper.dataType = oper.dataType
return newOper
else:
# This is an FC. Accesses to local symbols
......@@ -574,42 +582,34 @@ class AwlSymResolver(object):
oper.interfaceIndex = index
return oper
# Resolve named fully qualified accesses (DBx.VARx)
# If allowWholeArrayAccess is true, unsubscripted accesses
# to array variables are supported.
def __resolveNamedFullyQualified(self, block, insn, oper,
allowWholeArrayAccess=False):
if oper.type != AwlOperator.NAMED_DBVAR:
return oper
# Resolve the symbolic DB name, if needed
assert(oper.value.dbNumber is not None or\
oper.value.dbName is not None)
if oper.value.dbNumber is None:
symbol = self.cpu.symbolTable.findOneByName(oper.value.dbName)
if not symbol:
raise AwlSimError("Symbol \"%s\" specified as DB in "
"fully qualified operator not found." %\
oper.value.dbName)
if symbol.type.type != AwlDataType.TYPE_DB_X:
raise AwlSimError("Symbol \"%s\" specified as DB in "
"fully qualified operator is not a DB-symbol." %\
oper.value.dbName)
oper.value.dbNumber = symbol.operator.value.byteOffset
# Resolve a symbolic DB name. Returns DB index number.
def __resolveDBName(self, dbName):
symbol = self.cpu.symbolTable.findOneByName(dbName)
if not symbol:
raise AwlSimError("Symbol \"%s\" specified as DB in "
"fully qualified operator not found." %\
dbName)
if symbol.type.type != AwlDataType.TYPE_DB_X:
raise AwlSimError("Symbol \"%s\" specified as DB in "
"fully qualified operator is not a DB-symbol." %\
dbName)
return symbol.operator.value.byteOffset
# Get offset and width of a DB field.
def __dbVarToOffset(self, dbNumber, identChain, allowWholeArrayAccess=True):
# Get the DB
try:
db = self.cpu.dbs[oper.value.dbNumber]
db = self.cpu.dbs[dbNumber]
except KeyError as e:
raise AwlSimError("DB %d specified in fully qualified "
"operator does not exist." % oper.value.dbNumber)
"operator does not exist." % dbNumber)
# Get the data structure base field descriptor.
# For arrays, this is the base name of the array.
tmpIdentChain = oper.value.identChain.dup()
tmpIdentChain = identChain.dup()
tmpIdentChain[-1] = tmpIdentChain[-1].dup(withIndices = False)
field = db.struct.getField(tmpIdentChain.getString())
if oper.value.identChain[-1].indices is None:
if identChain[-1].indices is None:
# Access without array indices.
if field.dataType.type == AwlDataType.TYPE_ARRAY:
# This is a whole-array access.
......@@ -617,7 +617,7 @@ class AwlSymResolver(object):
if not allowWholeArrayAccess:
raise AwlSimError("Variable '%s' in fully qualified "
"DB access is an ARRAY." %\
oper.value.identChain.getString())
identChain.getString())
else:
# This is an array field access.
# (The original last ident chain field has indices.)
......@@ -628,10 +628,33 @@ class AwlSymResolver(object):
oper.value.identChain.getString())
# Get the actual data field.
# (Don't remove indices from the last chain element.)
field = db.struct.getField(oper.value.identChain.getString())
field = db.struct.getField(identChain.getString())
# Extract the offset data
offset = field.offset.dup()
width = field.bitSize
dataType = field.dataType
return offset, width, dataType
# Resolve named fully qualified accesses (DBx.VARx)
# If allowWholeArrayAccess is true, unsubscripted accesses
# to array variables are supported.
def __resolveNamedFullyQualified(self, block, insn, oper,
allowWholeArrayAccess=False):
if oper.type != AwlOperator.NAMED_DBVAR:
return oper
# Resolve the symbolic DB name, if needed
assert(oper.value.dbNumber is not None or\
oper.value.dbName is not None)
if oper.value.dbNumber is None:
oper.value.dbNumber = self.__resolveDBName(oper.value.dbName)
# Get the offset data and the width of the field.
offset, width, fieldDataType = self.__dbVarToOffset(oper.value.dbNumber,
oper.value.identChain,
allowWholeArrayAccess)
offset.dbNumber = oper.value.dbNumber
# Construct an absolute operator
......@@ -641,7 +664,54 @@ class AwlSymResolver(object):
insn = oper.insn)
# If this is a compound data type access, mark
# the operand as such.
oper.compound = field.dataType.compound
oper.compound = fieldDataType.compound
# Assign access data type.
oper.dataType = fieldDataType
return oper
# Resolve named fully qualified pointers (P#DBx.VARx)
# This is an awlsim extension.
def __resolveNamedFQPointer(self, block, insn, oper, param=None):
if oper.type != AwlOperator.IMM_PTR:
return oper
if oper.value.width > 0:
return oper
assert(isinstance(oper.value, SymbolicDBPointer))
# Resolve the symbolic DB name, if needed
assert(oper.value.dbNr or\
oper.value.dbSymbol is not None)
if oper.value.dbSymbol is not None:
oper.value.dbNr = self.__resolveDBName(oper.value.dbSymbol)
# Get the offset data and the width of the field.
offset, width, fieldDataType = self.__dbVarToOffset(oper.value.dbNr,
oper.value.identChain)
# Write the pointer value.
oper.value.setDWord(offset.toPointerValue() |\
(Pointer.AREA_DB << Pointer.AREA_SHIFT))
# Create a resolved Pointer.
if param and\
param.lValueDataType.type == AwlDataType.TYPE_POINTER:
# Create a resolved DB-Pointer (48 bit).
oper.value = oper.value.toDBPointer()
elif param and\
param.lValueDataType.type == AwlDataType.TYPE_ANY:
# Create a resolved ANY-Pointer (80 bit).
oper.value = ANYPointer.makeByAutoType(
dataType = fieldDataType,
ptrValue = oper.value.toPointerValue(),