Rewrite pyprofibus import
[awlsim.git] / awlsim / core / cpu.py
1 # -*- coding: utf-8 -*-
2 #
3 # AWL simulator - CPU
4 #
5 # Copyright 2012-2014 Michael Buesch <m@bues.ch>
6 #
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License along
18 # with this program; if not, write to the Free Software Foundation, Inc.,
19 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #
21
22 from __future__ import division, absolute_import, print_function, unicode_literals
23 from awlsim.core.compat import *
24
25 import time
26 import datetime
27 import random
28
29 from awlsim.core.cpuspecs import *
30 from awlsim.core.parser import *
31 from awlsim.core.symbolparser import *
32 from awlsim.core.datatypes import *
33 from awlsim.core.instructions.all_insns import *
34 from awlsim.core.systemblocks.system_sfb import *
35 from awlsim.core.systemblocks.system_sfc import *
36 from awlsim.core.operators import *
37 from awlsim.core.insntrans import *
38 from awlsim.core.optrans import *
39 from awlsim.core.blocks import *
40 from awlsim.core.datablocks import *
41 from awlsim.core.statusword import *
42 from awlsim.core.labels import *
43 from awlsim.core.timers import *
44 from awlsim.core.counters import *
45 from awlsim.core.callstack import *
46 from awlsim.core.obtemp import *
47 from awlsim.core.util import *
48
49
50 class ParenStackElem(object):
51         "Parenthesis stack element"
52
53         def __init__(self, cpu, insnType, statusWord):
54                 self.cpu = cpu
55                 self.insnType = insnType
56                 self.NER = statusWord.NER
57                 self.VKE = statusWord.VKE
58                 self.OR = statusWord.OR
59
60         def __repr__(self):
61                 mnemonics = self.cpu.specs.getMnemonics()
62                 type2name = {
63                         S7CPUSpecs.MNEMONICS_EN : AwlInsn.type2name_english,
64                         S7CPUSpecs.MNEMONICS_DE : AwlInsn.type2name_german,
65                 }[mnemonics]
66                 return '(insn="%s" VKE=%s OR=%d)' %\
67                         (type2name[self.insnType],
68                          self.VKE, self.OR)
69
70 class McrStackElem(object):
71         "MCR stack element"
72
73         def __init__(self, statusWord):
74                 self.VKE = statusWord.VKE
75
76         def __bool__(self):
77                 return bool(self.VKE)
78
79         __nonzero__ = __bool__
80
81 class S7CPU(object):
82         "STEP 7 CPU"
83
84         def __init__(self, sim):
85                 self.sim = sim
86                 self.specs = S7CPUSpecs(self)
87                 self.setCycleTimeLimit(5.0)
88                 self.setCycleExitCallback(None)
89                 self.setBlockExitCallback(None)
90                 self.setPostInsnCallback(None)
91                 self.setPeripheralReadCallback(None)
92                 self.setPeripheralWriteCallback(None)
93                 self.setScreenUpdateCallback(None)
94                 self.reset()
95                 self.enableExtendedInsns(False)
96                 self.enableObTempPresets(False)
97
98         def enableObTempPresets(self, en=True):
99                 self.__obTempPresetsEnabled = bool(en)
100
101         def obTempPresetsEnabled(self):
102                 return self.__obTempPresetsEnabled
103
104         def enableExtendedInsns(self, en=True):
105                 self.__extendedInsnsEnabled = bool(en)
106
107         def extendedInsnsEnabled(self):
108                 return self.__extendedInsnsEnabled
109
110         def setCycleTimeLimit(self, newLimit):
111                 self.cycleTimeLimit = float(newLimit)
112
113         def setRunTimeLimit(self, timeoutSeconds=0.0):
114                 self.__runtimeLimit = timeoutSeconds if timeoutSeconds > 0.0 else None
115
116         def __detectMnemonics(self, parseTree):
117                 specs = self.getSpecs()
118                 if specs.getConfiguredMnemonics() != S7CPUSpecs.MNEMONICS_AUTO:
119                         return
120                 codeBlocks = list(parseTree.obs.values())
121                 codeBlocks.extend(parseTree.fbs.values())
122                 codeBlocks.extend(parseTree.fcs.values())
123                 errorCounts = {
124                         S7CPUSpecs.MNEMONICS_EN         : 0,
125                         S7CPUSpecs.MNEMONICS_DE         : 0,
126                 }
127                 detected = None
128                 for mnemonics in (S7CPUSpecs.MNEMONICS_EN,
129                                   S7CPUSpecs.MNEMONICS_DE):
130                         for block in codeBlocks:
131                                 for rawInsn in block.insns:
132                                         ret = AwlInsnTranslator.name2type(rawInsn.getName(),
133                                                                           mnemonics)
134                                         if ret is None:
135                                                 errorCounts[mnemonics] += 1
136                                         try:
137                                                 optrans = AwlOpTranslator(None, mnemonics)
138                                                 optrans.translateFromRawInsn(rawInsn)
139                                         except AwlSimError:
140                                                 errorCounts[mnemonics] += 1
141                         if errorCounts[mnemonics] == 0:
142                                 # No error. Use these mnemonics.
143                                 detected = mnemonics
144                 if detected is None:
145                         # Select the mnemonics with the lower error count.
146                         if errorCounts[S7CPUSpecs.MNEMONICS_EN] <= errorCounts[S7CPUSpecs.MNEMONICS_DE]:
147                                 detected = S7CPUSpecs.MNEMONICS_EN
148                         else:
149                                 detected = S7CPUSpecs.MNEMONICS_DE
150                 if specs.getMnemonics() != S7CPUSpecs.MNEMONICS_AUTO:
151                         # Autodetected mnemonics were already set before
152                         if specs.getMnemonics() != detected:
153                                 raise AwlSimError("Cannot mix multiple AWL files with "\
154                                         "distinct mnemonics. This error may be caused by "\
155                                         "incorrect autodetection. "\
156                                         "Force mnemonics to EN or DE to avoid this error.")
157                 specs.setDetectedMnemonics(detected)
158
159         def __translateInsn(self, rawInsn, ip):
160                 ex = None
161                 try:
162                         insn = AwlInsnTranslator.fromRawInsn(self, rawInsn)
163                         insn.setIP(ip)
164                 except AwlSimError as e:
165                         if e.getRawInsn() is None:
166                                 e.setRawInsn(rawInsn)
167                         raise e
168                 return insn
169
170         def __translateInsns(self, rawInsns):
171                 insns = []
172                 # Translate raw instructions to simulator instructions
173                 for ip, rawInsn in enumerate(rawInsns):
174                         insns.append(self.__translateInsn(rawInsn, ip))
175                 # If the last instruction is not BE or BEA, add an implicit BE
176                 if not insns or insns[-1].type not in (AwlInsn.TYPE_BE,
177                                                        AwlInsn.TYPE_BEA):
178                         insns.append(AwlInsn_BE(cpu = self, rawInsn = None))
179                 return insns
180
181         def __translateInterfaceField(self, rawVar):
182                 dtype = AwlDataType.makeByName(rawVar.typeTokens, rawVar.dimensions)
183                 assert(len(rawVar.idents) == 1) #TODO no structs, yet
184                 field = BlockInterfaceField(name = rawVar.idents[0].name,
185                                             dataType = dtype)
186                 return field
187
188         def __translateCodeBlock(self, rawBlock, blockClass):
189                 insns = self.__translateInsns(rawBlock.insns)
190                 block = blockClass(insns, rawBlock.index)
191                 for rawVar in rawBlock.vars_in:
192                         block.interface.addField_IN(self.__translateInterfaceField(rawVar))
193                 for rawVar in rawBlock.vars_out:
194                         block.interface.addField_OUT(self.__translateInterfaceField(rawVar))
195                 if rawBlock.retTypeTokens:
196                         # ARRAY is not supported for RET_VAL. So make non-array dtype.
197                         dtype = AwlDataType.makeByName(rawBlock.retTypeTokens)
198                         if dtype.type != AwlDataType.TYPE_VOID:
199                                 # Ok, we have a RET_VAL.
200                                 field = BlockInterfaceField(name = "RET_VAL",
201                                                             dataType = dtype)
202                                 block.interface.addField_OUT(field)
203                 for rawVar in rawBlock.vars_inout:
204                         block.interface.addField_INOUT(self.__translateInterfaceField(rawVar))
205                 for rawVar in rawBlock.vars_static:
206                         block.interface.addField_STAT(self.__translateInterfaceField(rawVar))
207                 for rawVar in rawBlock.vars_temp:
208                         block.interface.addField_TEMP(self.__translateInterfaceField(rawVar))
209                 block.interface.buildDataStructure()
210                 return block
211
212         # Initialize a DB (global or instance) data field from a raw data-init.
213         def __initDBField(self, db, dataType, rawDataInit):
214                 if dataType.type == AwlDataType.TYPE_ARRAY:
215                         index = dataType.arrayIndicesCollapse(*rawDataInit.idents[-1].indices)
216                 else:
217                         index = None
218                 value = dataType.parseMatchingImmediate(rawDataInit.valueTokens)
219                 db.structInstance.setFieldDataByName(rawDataInit.idents[-1].name,
220                                                      index, value)
221
222         def __translateGlobalDB(self, rawDB):
223                 db = DB(rawDB.index, None)
224                 # Create the data structure fields
225                 for field in rawDB.fields:
226                         assert(len(field.idents) == 1) #TODO no structs, yet
227                         if not rawDB.getFieldInit(field):
228                                 raise AwlSimError(
229                                         "DB %d declares field '%s', "
230                                         "but does not initialize." %\
231                                         (rawDB.index, field.idents[0].name))
232                         dtype = AwlDataType.makeByName(field.typeTokens,
233                                                        field.dimensions)
234                         db.struct.addFieldNaturallyAligned(field.idents[0].name,
235                                                            dtype)
236                 # Allocate the data structure fields
237                 db.allocate()
238                 # Initialize the data structure fields
239                 for field, init in rawDB.allFieldInits():
240                         if not field:
241                                 raise AwlSimError(
242                                         "DB %d assigns field '%s', "
243                                         "but does not declare it." %\
244                                         (rawDB.index, init.getIdentString()))
245                         assert(len(field.idents) == 1 and len(init.idents) == 1) #TODO no structs, yet
246                         dtype = AwlDataType.makeByName(field.typeTokens,
247                                                        field.dimensions)
248                         self.__initDBField(db, dtype, init)
249                 return db
250
251         def __translateInstanceDB(self, rawDB):
252                 if rawDB.fb.fbSymbol is None:
253                         fbStr = "SFB" if rawDB.fb.isSFB else "FB"
254                         fbNumber = rawDB.fb.fbNumber
255                         isSFB = rawDB.fb.isSFB
256                 else:
257                         fbStr = '"%s"' % rawDB.fb.fbSymbol
258                         fbNumber, sym = self.__resolveBlockName((AwlDataType.TYPE_FB_X,
259                                                                  AwlDataType.TYPE_SFB_X),
260                                                                 rawDB.fb.fbSymbol)
261                         isSFB = sym.type.type == AwlDataType.TYPE_SFB_X
262
263                 try:
264                         if isSFB:
265                                 fb = self.sfbs[fbNumber]
266                         else:
267                                 fb = self.fbs[fbNumber]
268                 except KeyError:
269                         raise AwlSimError("Instance DB %d references %s %d, "
270                                 "but %s %d does not exist." %\
271                                 (rawDB.index,
272                                  fbStr, fbNumber,
273                                  fbStr, fbNumber))
274                 db = DB(rawDB.index, fb)
275                 interface = fb.interface
276                 # Sanity checks
277                 if rawDB.fields:
278                         raise AwlSimError("DB %d is an "
279                                 "instance DB, but it also "
280                                 "declares a data structure." %\
281                                 rawDB.index)
282                 # Allocate the data structure fields
283                 db.allocate()
284                 # Initialize the data structure fields
285                 for init in rawDB.fieldInits:
286                         assert(len(init.idents) == 1) #TODO no structs, yet
287                         dtype = interface.getFieldByName(init.idents[-1].name).dataType
288                         self.__initDBField(db, dtype, init)
289                 return db
290
291         def __translateDB(self, rawDB):
292                 if rawDB.index < 0:
293                         raise AwlSimError("DB number %d is invalid" % rawDB.index)
294                 if rawDB.isInstanceDB():
295                         return self.__translateInstanceDB(rawDB)
296                 return self.__translateGlobalDB(rawDB)
297
298         # Translate classic symbols ("abc")
299         def __resolveClassicSym(self, block, insn, oper):
300                 if oper.type == AwlOperator.SYMBOLIC:
301                         symbol = self.symbolTable.findOneByName(oper.value.varName)
302                         if not symbol:
303                                 raise AwlSimError("Symbol \"%s\" not found in "
304                                         "symbol table." % oper.value.varName,
305                                         insn = insn)
306                         newOper = symbol.operator.dup()
307                         newOper.setInsn(oper.insn)
308                         return newOper
309                 return oper
310
311         # Translate symbolic OB/FB/FC/DB block name
312         def __resolveBlockName(self, blockTypeIds, blockName):
313                 if isString(blockName):
314                         symbol = self.symbolTable.findOneByName(blockName)
315                         if not symbol:
316                                 raise AwlSimError("Symbolic block name \"%s\" "
317                                         "not found in symbol table." % blockName)
318                         if symbol.type.type not in blockTypeIds:
319                                 raise AwlSimError("Symbolic block name \"%s\" "
320                                         "has an invalid type." % blockName)
321                         return symbol.operator.value.byteOffset, symbol
322                 return blockName, None
323
324         # Translate local symbols (#abc or P##abc)
325         # If pointer is false, try to resolve #abc.
326         # If pointer is true, try to resolve P##abc.
327         def resolveNamedLocal(self, block, insn, oper, pointer=False):
328                 if pointer:
329                         if oper.type != AwlOperator.NAMED_LOCAL_PTR:
330                                 return oper
331                 else:
332                         if oper.type != AwlOperator.NAMED_LOCAL:
333                                 return oper
334
335                 # Get the interface field for this variable
336                 field = block.interface.getFieldByName(oper.value.varName)
337
338                 # Sanity checks
339                 if field.dataType.type == AwlDataType.TYPE_ARRAY:
340                         if not oper.value.indices and\
341                            oper.type != AwlOperator.NAMED_LOCAL_PTR:
342                                 raise AwlSimError("Cannot address array #%s "
343                                         "without subscript list." %\
344                                         oper.value.varName)
345                 else:
346                         if oper.value.indices:
347                                 raise AwlSimError("Trying to subscript array, "
348                                         "but #%s is not an array." %\
349                                         oper.value.varName)
350
351                 if block.interface.hasInstanceDB or\
352                    field.fieldType == BlockInterfaceField.FTYPE_TEMP:
353                         # This is an FB or a TEMP access. Translate the operator
354                         # to a DI/TEMP access.
355                         newOper = block.interface.getOperatorForField(oper.value.varName,
356                                                                       oper.value.indices,
357                                                                       pointer)
358                         newOper.setInsn(oper.insn)
359                         return newOper
360                 else:
361                         # This is an FC. Accesses to local symbols
362                         # are resolved at runtime.
363                         # Just set the value to the interface index.
364                         #TODO array
365                         index = block.interface.getFieldIndex(oper.value.varName)
366                         oper.interfaceIndex = index
367                 return oper
368
369         # Translate named fully qualified accesses (DBx.VARx)
370         def __resolveNamedFullyQualified(self, block, insn, oper):
371                 if oper.type != AwlOperator.NAMED_DBVAR:
372                         return oper
373
374                 # Resolve the symbolic DB name, if needed
375                 assert(oper.value.dbNumber is not None or\
376                        oper.value.dbName is not None)
377                 if oper.value.dbNumber is None:
378                         symbol = self.symbolTable.findOneByName(oper.value.dbName)
379                         if not symbol:
380                                 raise AwlSimError("Symbol \"%s\" specified as DB in "
381                                         "fully qualified operator not found." %\
382                                         oper.value.dbName)
383                         if symbol.type.type != AwlDataType.TYPE_DB_X:
384                                 raise AwlSimError("Symbol \"%s\" specified as DB in "
385                                         "fully qualified operator is not a DB-symbol." %\
386                                         oper.value.dbName)
387                         oper.value.dbNumber = symbol.operator.value.byteOffset
388
389                 # Get the DB
390                 try:
391                         db = self.dbs[oper.value.dbNumber]
392                 except KeyError as e:
393                         raise AwlSimError("DB %d specified in fully qualified "
394                                 "operator does not exist." % oper.value.dbNumber)
395
396                 # Get the data structure field descriptor
397                 # and construct the AwlOffset.
398                 field = db.struct.getField(oper.value.varName)
399                 if oper.value.indices is None:
400                         # Non-array access.
401                         if field.dataType.type == AwlDataType.TYPE_ARRAY:
402                                 #TODO this happens for parameter passing
403                                 raise AwlSimError("Variable '%s' in fully qualified "
404                                         "DB access is an ARRAY." %\
405                                         oper.value.varName)
406                 else:
407                         # This is an array access.
408                         if field.dataType.type != AwlDataType.TYPE_ARRAY:
409                                 raise AwlSimError("Indexed variable '%s' in fully qualified "
410                                         "DB access is not an ARRAY." %\
411                                         oper.value.varName)
412                         index = field.dataType.arrayIndicesCollapse(*oper.value.indices)
413                         # Get the actual data field
414                         field = db.struct.getField(oper.value.varName, index)
415                 # Extract the offset data
416                 offset = field.offset.dup()
417                 width = field.bitSize
418                 offset.dbNumber = oper.value.dbNumber
419
420                 # Construct an absolute operator
421                 return AwlOperator(type = AwlOperator.MEM_DB,
422                                    width = width,
423                                    value = offset,
424                                    insn = oper.insn)
425
426         def __resolveSymbols_block(self, block):
427                 for insn in block.insns:
428                         try:
429                                 for i, oper in enumerate(insn.ops):
430                                         oper = self.__resolveClassicSym(block, insn, oper)
431                                         oper = self.resolveNamedLocal(block, insn, oper, False)
432                                         oper = self.__resolveNamedFullyQualified(block, insn, oper)
433                                         if oper.type == AwlOperator.INDIRECT:
434                                                 oper.offsetOper = self.__resolveClassicSym(block,
435                                                                         insn, oper.offsetOper)
436                                                 oper.offsetOper = self.resolveNamedLocal(block,
437                                                                         insn, oper.offsetOper, False)
438                                         oper = self.resolveNamedLocal(block, insn, oper, True)
439                                         insn.ops[i] = oper
440                                 for param in insn.params:
441                                         oper = param.rvalueOp
442                                         oper = self.__resolveClassicSym(block, insn, oper)
443                                         oper = self.resolveNamedLocal(block, insn, oper, False)
444                                         oper = self.__resolveNamedFullyQualified(block, insn, oper)
445                                         if oper.type == AwlOperator.INDIRECT:
446                                                 oper.offsetOper = self.__resolveClassicSym(block,
447                                                                         insn, oper.offsetOper)
448                                                 oper.offsetOper = self.resolveNamedLocal(block,
449                                                                         insn, oper.offsetOper, False)
450                                         oper = self.resolveNamedLocal(block, insn, oper, False)
451                                         param.rvalueOp = oper
452                         except AwlSimError as e:
453                                 if not e.getInsn():
454                                         e.setInsn(insn)
455                                 raise e
456
457         # Resolve all symbols (global and local) on all blocks, as far as possible.
458         def __resolveSymbols(self):
459                 for ob in self.obs.values():
460                         self.__resolveSymbols_block(ob)
461                 for fb in self.fbs.values():
462                         self.__resolveSymbols_block(fb)
463                 for fc in self.fcs.values():
464                         self.__resolveSymbols_block(fc)
465                 for sfc in self.sfcs.values():
466                         self.__resolveSymbols_block(sfc)
467                         sfc.resolveHardwiredSymbols()
468                 for sfb in self.sfbs.values():
469                         self.__resolveSymbols_block(sfb)
470                         sfb.resolveHardwiredSymbols()
471
472         # Run static error checks for code block
473         def __staticSanityChecks_block(self, block):
474                 for insn in block.insns:
475                         insn.staticSanityChecks()
476
477         # Run static error checks
478         def __staticSanityChecks(self):
479                 try:
480                         self.obs[1]
481                 except KeyError:
482                         raise AwlSimError("No OB1 defined")
483
484                 for ob in self.obs.values():
485                         self.__staticSanityChecks_block(ob)
486                 for fb in self.fbs.values():
487                         self.__staticSanityChecks_block(fb)
488                 for fc in self.fcs.values():
489                         self.__staticSanityChecks_block(fc)
490
491         def load(self, parseTree):
492                 # Mnemonics autodetection
493                 self.__detectMnemonics(parseTree)
494                 # Translate OBs
495                 for obNumber, rawOB in parseTree.obs.items():
496                         obNumber, sym = self.__resolveBlockName((AwlDataType.TYPE_OB_X,),
497                                                                 obNumber)
498                         if obNumber in self.obs:
499                                 raise AwlSimError("Multiple definitions of "\
500                                         "OB %d" % obNumber)
501                         rawOB.index = obNumber
502                         ob = self.__translateCodeBlock(rawOB, OB)
503                         self.obs[obNumber] = ob
504                         # Create the TEMP-preset handler table
505                         try:
506                                 presetHandlerClass = OBTempPresets_table[obNumber]
507                         except KeyError:
508                                 presetHandlerClass = OBTempPresets_dummy
509                         self.obTempPresetHandlers[obNumber] = presetHandlerClass(self)
510                 # Translate FBs
511                 for fbNumber, rawFB in parseTree.fbs.items():
512                         fbNumber, sym = self.__resolveBlockName((AwlDataType.TYPE_FB_X,),
513                                                                 fbNumber)
514                         if fbNumber in self.fbs:
515                                 raise AwlSimError("Multiple definitions of "\
516                                         "FB %d" % fbNumber)
517                         rawFB.index = fbNumber
518                         fb = self.__translateCodeBlock(rawFB, FB)
519                         self.fbs[fbNumber] = fb
520                 # Translate FCs
521                 for fcNumber, rawFC in parseTree.fcs.items():
522                         fcNumber, sym = self.__resolveBlockName((AwlDataType.TYPE_FC_X,),
523                                                                 fcNumber)
524                         if fcNumber in self.fcs:
525                                 raise AwlSimError("Multiple definitions of "\
526                                         "FC %d" % fcNumber)
527                         rawFC.index = fcNumber
528                         fc = self.__translateCodeBlock(rawFC, FC)
529                         self.fcs[fcNumber] = fc
530
531                 if not self.sfbs:
532                         # Create the SFB tables
533                         for sfbNumber in SFB_table.keys():
534                                 if sfbNumber < 0 and not self.__extendedInsnsEnabled:
535                                         continue
536                                 sfb = SFB_table[sfbNumber](self)
537                                 sfb.interface.buildDataStructure()
538                                 self.sfbs[sfbNumber] = sfb
539                 if not self.sfcs:
540                         # Create the SFC tables
541                         for sfcNumber in SFC_table.keys():
542                                 if sfcNumber < 0 and not self.__extendedInsnsEnabled:
543                                         continue
544                                 sfc = SFC_table[sfcNumber](self)
545                                 sfc.interface.buildDataStructure()
546                                 self.sfcs[sfcNumber] = sfc
547
548                 # Translate DBs
549                 for dbNumber, rawDB in parseTree.dbs.items():
550                         dbNumber, sym = self.__resolveBlockName((AwlDataType.TYPE_DB_X,
551                                                                  AwlDataType.TYPE_FB_X,
552                                                                  AwlDataType.TYPE_SFB_X),
553                                                                 dbNumber)
554                         if dbNumber in self.dbs:
555                                 raise AwlSimError("Multiple definitions of "\
556                                         "DB %d" % dbNumber)
557                         rawDB.index = dbNumber
558                         db = self.__translateDB(rawDB)
559                         self.dbs[dbNumber] = db
560
561         def loadSymbolTable(self, symbolTable):
562                 self.symbolTable.merge(symbolTable)
563
564         def reallocate(self, force=False):
565                 if force or (self.specs.nrAccus == 4) != self.is4accu:
566                         self.accu1, self.accu2, self.accu3, self.accu4 =\
567                                 Accu(), Accu(), Accu(), Accu()
568                         self.is4accu = (self.specs.nrAccus == 4)
569                 if force or self.specs.nrTimers != len(self.timers):
570                         self.timers = [ Timer(self, i)
571                                         for i in range(self.specs.nrTimers) ]
572                 if force or self.specs.nrCounters != len(self.counters):
573                         self.counters = [ Counter(self, i)
574                                           for i in range(self.specs.nrCounters) ]
575                 if force or self.specs.nrFlags != len(self.flags):
576                         self.flags = ByteArray(self.specs.nrFlags)
577                 if force or self.specs.nrInputs != len(self.inputs):
578                         self.inputs = ByteArray(self.specs.nrInputs)
579                 if force or self.specs.nrOutputs != len(self.outputs):
580                         self.outputs = ByteArray(self.specs.nrOutputs)
581                 CallStackElem.resetCache()
582
583         def reset(self):
584                 self.dbs = {
585                         # DBs
586                         0 : DB(0, permissions = 0), # read/write-protected system-DB
587                 }
588                 self.obs = {
589                         # OBs
590                 }
591                 self.obTempPresetHandlers = {
592                         # OB TEMP-preset handlers
593                 }
594                 self.fcs = {
595                         # User FCs
596                 }
597                 self.fbs = {
598                         # User FBs
599                 }
600                 self.sfcs = {
601                         # System SFCs
602                 }
603                 self.sfbs = {
604                         # System SFBs
605                 }
606                 self.symbolTable = SymbolTable()
607                 self.is4accu = False
608                 self.reallocate(force=True)
609                 self.ar1 = Adressregister()
610                 self.ar2 = Adressregister()
611                 self.dbRegister = self.dbs[0]
612                 self.diRegister = self.dbs[0]
613                 self.callStack = [ ]
614                 self.callStackTop = None
615                 self.setMcrActive(False)
616                 self.mcrStack = [ ]
617                 self.statusWord = S7StatusWord()
618                 self.__clockMemByteOffset = None
619                 self.setRunTimeLimit()
620
621                 self.relativeJump = 1
622
623                 # Stats
624                 self.__insnCount = 0
625                 self.__cycleCount = 0
626                 self.insnPerSecond = 0.0
627                 self.avgInsnPerCycle = 0.0
628                 self.cycleStartTime = 0.0
629                 self.minCycleTime = 86400.0
630                 self.maxCycleTime = 0.0
631                 self.avgCycleTime = 0.0
632                 self.startupTime = self.__getTime()
633                 self.__speedMeasureStartTime = 0
634                 self.__speedMeasureStartInsnCount = 0
635                 self.__speedMeasureStartCycleCount = 0
636
637                 self.updateTimestamp()
638
639         def setCycleExitCallback(self, cb, data=None):
640                 self.cbCycleExit = cb
641                 self.cbCycleExitData = data
642
643         def setBlockExitCallback(self, cb, data=None):
644                 self.cbBlockExit = cb
645                 self.cbBlockExitData = data
646
647         def setPostInsnCallback(self, cb, data=None):
648                 self.cbPostInsn = cb
649                 self.cbPostInsnData = data
650
651         def setPeripheralReadCallback(self, cb, data=None):
652                 self.cbPeripheralRead = cb
653                 self.cbPeripheralReadData = data
654
655         def setPeripheralWriteCallback(self, cb, data=None):
656                 self.cbPeripheralWrite = cb
657                 self.cbPeripheralWriteData = data
658
659         def setScreenUpdateCallback(self, cb, data=None):
660                 self.cbScreenUpdate = cb
661                 self.cbScreenUpdateData = data
662
663         def requestScreenUpdate(self):
664                 if self.cbScreenUpdate:
665                         self.cbScreenUpdate(self.cbScreenUpdateData)
666
667         def __runOB(self, block):
668                 # Update timekeeping
669                 self.updateTimestamp()
670                 self.cycleStartTime = self.now
671
672                 # Initialize CPU state
673                 self.callStack = [ CallStackElem(self, block) ]
674                 self.dbRegister = self.diRegister = self.dbs[0]
675                 cse = self.callStackTop = self.callStack[-1]
676                 if self.__obTempPresetsEnabled:
677                         # Populate the TEMP region
678                         self.obTempPresetHandlers[block.index].generate(cse.localdata)
679
680                 # Run the user program cycle
681                 while self.callStack:
682                         while cse.ip < len(cse.insns):
683                                 insn, self.relativeJump = cse.insns[cse.ip], 1
684                                 insn.run()
685                                 if self.cbPostInsn:
686                                         self.cbPostInsn(self.cbPostInsnData)
687                                 cse.ip += self.relativeJump
688                                 cse, self.__insnCount = self.callStackTop,\
689                                                         (self.__insnCount + 1) & 0x3FFFFFFF
690                                 if not self.__insnCount % 64:
691                                         self.updateTimestamp()
692                                         self.__runTimeCheck()
693                         if self.cbBlockExit:
694                                 self.cbBlockExit(self.cbBlockExitData)
695                         prevCse = self.callStack.pop()
696                         if self.callStack:
697                                 cse = self.callStackTop = self.callStack[-1]
698                                 prevCse.handleBlockExit()
699                         prevCse.destroy()
700                 if self.cbCycleExit:
701                         self.cbCycleExit(self.cbCycleExitData)
702
703         # Run startup code
704         def startup(self):
705                 # Resolve symbolic instructions and operators
706                 self.__resolveSymbols()
707                 # Run some static sanity checks on the code
708                 self.__staticSanityChecks()
709
710                 self.updateTimestamp()
711                 self.__speedMeasureStartTime = self.now
712                 self.__speedMeasureStartInsnCount = 0
713                 self.__speedMeasureStartCycleCount = 0
714                 self.startupTime = self.now
715
716                 if self.specs.clockMemByte >= 0:
717                         self.__clockMemByteOffset = AwlOffset(self.specs.clockMemByte)
718                 else:
719                         self.__clockMemByteOffset = None
720                 self.__nextClockMemTime = self.now + 0.05
721                 self.__clockMemCount = 0
722                 self.__clockMemCountLCM = math_lcm(2, 4, 5, 8, 10, 16, 20)
723
724                 # Run startup OB
725                 if 102 in self.obs and self.is4accu:
726                         # Cold start.
727                         # This is only done on 4xx-series CPUs.
728                         self.__runOB(self.obs[102])
729                 elif 100 in self.obs:
730                         # Warm start.
731                         # This really is a cold start, because remanent
732                         # resources were reset. However we could not execute
733                         # OB 102, so this is a fallback.
734                         # This is not 100% compliant with real CPUs, but it probably
735                         # is sane behavior.
736                         self.__runOB(self.obs[100])
737
738         # Run one cycle of the user program
739         def runCycle(self):
740                 # Run the actual OB1 code
741                 self.__runOB(self.obs[1])
742
743                 # Update timekeeping and statistics
744                 self.updateTimestamp()
745                 self.__cycleCount = (self.__cycleCount + 1) & 0x3FFFFFFF
746
747                 # Evaluate speed measurement
748                 elapsedTime = self.now - self.__speedMeasureStartTime
749                 if elapsedTime >= 1.0:
750                         # Calculate instruction and cycle counts.
751                         cycleCount = (self.__cycleCount - self.__speedMeasureStartCycleCount) &\
752                                      0x3FFFFFFF
753                         insnCount = (self.__insnCount - self.__speedMeasureStartInsnCount) &\
754                                     0x3FFFFFFF
755
756                         # Calculate instruction statistics.
757                         self.insnPerSecond = insnCount / elapsedTime
758                         self.avgInsnPerCycle = insnCount / cycleCount
759
760                         # Get the average cycle time over the measurement period.
761                         cycleTime = elapsedTime / cycleCount
762
763                         # Store overall-average cycle time and maximum cycle time.
764                         self.maxCycleTime = max(self.maxCycleTime, cycleTime)
765                         self.minCycleTime = min(self.minCycleTime, cycleTime)
766                         self.avgCycleTime = (self.avgCycleTime + cycleTime) / 2.0
767
768                         # Reset the counters
769                         self.__speedMeasureStartTime = self.now
770                         self.__speedMeasureStartInsnCount = self.__insnCount
771                         self.__speedMeasureStartCycleCount = self.__cycleCount
772
773         __getTime = getattr(time, "perf_counter", time.time)
774
775         # updateTimestamp() updates self.now, which is a
776         # floating point count of seconds.
777         def updateTimestamp(self):
778                 # Update the system time
779                 self.now = self.__getTime()
780                 # Update the clock memory byte
781                 if self.__clockMemByteOffset:
782                         try:
783                                 if self.now >= self.__nextClockMemTime:
784                                         self.__nextClockMemTime += 0.05
785                                         value = self.flags.fetch(self.__clockMemByteOffset, 8)
786                                         value ^= 0x01 # 0.1s period
787                                         count = self.__clockMemCount + 1
788                                         self.__clockMemCount = count
789                                         if not count % 2:
790                                                 value ^= 0x02 # 0.2s period
791                                         if not count % 4:
792                                                 value ^= 0x04 # 0.4s period
793                                         if not count % 5:
794                                                 value ^= 0x08 # 0.5s period
795                                         if not count % 8:
796                                                 value ^= 0x10 # 0.8s period
797                                         if not count % 10:
798                                                 value ^= 0x20 # 1.0s period
799                                         if not count % 16:
800                                                 value ^= 0x40 # 1.6s period
801                                         if not count % 20:
802                                                 value ^= 0x80 # 2.0s period
803                                         if count >= self.__clockMemCountLCM:
804                                                 self.__clockMemCount = 0
805                                         self.flags.store(self.__clockMemByteOffset, 8, value)
806                         except AwlSimError as e:
807                                 raise AwlSimError("Failed to generate clock "
808                                         "memory signal:\n" + str(e) +\
809                                         "\n\nThe configured clock memory byte "
810                                         "address might be invalid." )
811                 # Check whether the runtime timeout exceeded
812                 if self.__runtimeLimit is not None:
813                         if self.now - self.startupTime >= self.__runtimeLimit:
814                                 raise MaintenanceRequest(MaintenanceRequest.TYPE_RTTIMEOUT,
815                                         "CPU runtime timeout")
816
817         __dateAndTimeWeekdayMap = {
818                 0       : 2,    # monday
819                 1       : 3,    # tuesday
820                 2       : 4,    # wednesday
821                 3       : 5,    # thursday
822                 4       : 6,    # friday
823                 5       : 7,    # saturday
824                 6       : 1,    # sunday
825         }
826
827         # Make a DATE_AND_TIME for the current wall-time and
828         # store it in byteArray, which is a list of GenericByte objects.
829         # If byteArray is smaller than 8 bytes, an IndexError is raised.
830         def makeCurrentDateAndTime(self, byteArray, offset):
831                 dt = datetime.datetime.now()
832                 year, month, day, hour, minute, second, msec =\
833                         dt.year, dt.month, dt.day, dt.hour, \
834                         dt.minute, dt.second, dt.microsecond // 1000
835                 byteArray[offset] = (year % 10) | (((year // 10) % 10) << 4)
836                 byteArray[offset + 1] = (month % 10) | (((month // 10) % 10) << 4)
837                 byteArray[offset + 2] = (day % 10) | (((day // 10) % 10) << 4)
838                 byteArray[offset + 3] = (hour % 10) | (((hour // 10) % 10) << 4)
839                 byteArray[offset + 4] = (minute % 10) | (((minute // 10) % 10) << 4)
840                 byteArray[offset + 5] = (second % 10) | (((second // 10) % 10) << 4)
841                 byteArray[offset + 6] = ((msec // 10) % 10) | (((msec // 100) % 10) << 4)
842                 byteArray[offset + 7] = ((msec % 10) << 4) |\
843                                         self.__dateAndTimeWeekdayMap[dt.weekday()]
844
845         def __runTimeCheck(self):
846                 if self.now - self.cycleStartTime > self.cycleTimeLimit:
847                         raise AwlSimError("Cycle time exceed %.3f seconds" %\
848                                           self.cycleTimeLimit)
849
850         def getCurrentIP(self):
851                 try:
852                         return self.callStackTop.ip
853                 except IndexError as e:
854                         return None
855
856         def getCurrentInsn(self):
857                 try:
858                         cse = self.callStackTop
859                         if not cse:
860                                 return None
861                         return cse.insns[cse.ip]
862                 except IndexError as e:
863                         return None
864
865         def labelIdxToRelJump(self, labelIndex):
866                 # Translate a label index into a relative IP offset.
867                 cse = self.callStackTop
868                 label = cse.block.labels[labelIndex]
869                 return label.getInsn().getIP() - cse.ip
870
871         def jumpToLabel(self, labelIndex):
872                 self.relativeJump = self.labelIdxToRelJump(labelIndex)
873
874         def jumpRelative(self, insnOffset):
875                 self.relativeJump = insnOffset
876
877         def __call_FC(self, blockOper, dbOper, parameters):
878                 fc = self.fcs[blockOper.value.byteOffset]
879                 return CallStackElem(self, fc, None, parameters)
880
881         def __call_RAW_FC(self, blockOper, dbOper, parameters):
882                 fc = self.fcs[blockOper.value.byteOffset]
883                 return CallStackElem(self, fc, None, (), True)
884
885         def __call_FB(self, blockOper, dbOper, parameters):
886                 fb = self.fbs[blockOper.value.byteOffset]
887                 db = self.dbs[dbOper.value.byteOffset]
888                 cse = CallStackElem(self, fb, db, parameters)
889                 self.dbRegister, self.diRegister = self.diRegister, db
890                 return cse
891
892         def __call_RAW_FB(self, blockOper, dbOper, parameters):
893                 fb = self.fbs[blockOper.value.byteOffset]
894                 return CallStackElem(self, fb, self.diRegister, (), True)
895
896         def __call_SFC(self, blockOper, dbOper, parameters):
897                 sfc = self.sfcs[blockOper.value.byteOffset]
898                 return CallStackElem(self, sfc, None, parameters)
899
900         def __call_RAW_SFC(self, blockOper, dbOper, parameters):
901                 sfc = self.sfcs[blockOper.value.byteOffset]
902                 return CallStackElem(self, sfc, None, (), True)
903
904         def __call_SFB(self, blockOper, dbOper, parameters):
905                 sfb = self.sfbs[blockOper.value.byteOffset]
906                 db = self.dbs[dbOper.value.byteOffset]
907                 cse = CallStackElem(self, sfb, db, parameters)
908                 self.dbRegister, self.diRegister = self.diRegister, db
909                 return cse
910
911         def __call_RAW_SFB(self, blockOper, dbOper, parameters):
912                 sfb = self.sfbs[blockOper.value.byteOffset]
913                 return CallStackElem(self, sfb, self.diRegister, (), True)
914
915         def __call_INDIRECT(self, blockOper, dbOper, parameters):
916                 blockOper = blockOper.resolve()
917                 callHelper = self.__rawCallHelpers[blockOper.type]
918                 try:
919                         return callHelper(self, blockOper, dbOper, parameters)
920                 except KeyError as e:
921                         raise AwlSimError("Code block %d not found in indirect call" %\
922                                           blockOper.value.byteOffset)
923
924         __callHelpers = {
925                 AwlOperator.BLKREF_FC   : __call_FC,
926                 AwlOperator.BLKREF_FB   : __call_FB,
927                 AwlOperator.BLKREF_SFC  : __call_SFC,
928                 AwlOperator.BLKREF_SFB  : __call_SFB,
929         }
930
931         __rawCallHelpers = {
932                 AwlOperator.BLKREF_FC   : __call_RAW_FC,
933                 AwlOperator.BLKREF_FB   : __call_RAW_FB,
934                 AwlOperator.BLKREF_SFC  : __call_RAW_SFC,
935                 AwlOperator.BLKREF_SFB  : __call_RAW_SFB,
936                 AwlOperator.INDIRECT    : __call_INDIRECT,
937         }
938
939         def run_CALL(self, blockOper, dbOper=None, parameters=(), raw=False):
940                 try:
941                         if raw:
942                                 callHelper = self.__rawCallHelpers[blockOper.type]
943                         else:
944                                 callHelper = self.__callHelpers[blockOper.type]
945                 except KeyError:
946                         raise AwlSimError("Invalid CALL operand")
947                 newCse = callHelper(self, blockOper, dbOper, parameters)
948                 if newCse:
949                         self.callStack.append(newCse)
950                         self.callStackTop = newCse
951
952         def run_BE(self):
953                 s = self.statusWord
954                 s.OS, s.OR, s.STA, s.NER = 0, 0, 1, 0
955                 # Jump beyond end of block
956                 cse = self.callStackTop
957                 self.relativeJump = len(cse.insns) - cse.ip
958
959         def run_AUF(self, dbOper):
960                 dbOper = dbOper.resolve()
961                 try:
962                         db = self.dbs[dbOper.value.byteOffset]
963                 except KeyError:
964                         raise AwlSimError("Datablock %i does not exist" %\
965                                           dbOper.value.byteOffset)
966                 if dbOper.type == AwlOperator.BLKREF_DB:
967                         self.dbRegister = db
968                 elif dbOper.type == AwlOperator.BLKREF_DI:
969                         self.diRegister = db
970                 else:
971                         raise AwlSimError("Invalid DB reference in AUF")
972
973         def run_TDB(self):
974                 # Swap global and instance DB
975                 self.diRegister, self.dbRegister = self.dbRegister, self.diRegister
976
977         def getStatusWord(self):
978                 return self.statusWord
979
980         def getAccu(self, index):
981                 if index < 1 or index > self.specs.nrAccus:
982                         raise AwlSimError("Invalid ACCU offset")
983                 return (self.accu1, self.accu2,
984                         self.accu3, self.accu4)[index - 1]
985
986         def getAR(self, index):
987                 if index < 1 or index > 2:
988                         raise AwlSimError("Invalid AR offset")
989                 return (self.ar1, self.ar2)[index - 1]
990
991         def getTimer(self, index):
992                 try:
993                         return self.timers[index]
994                 except IndexError as e:
995                         raise AwlSimError("Fetched invalid timer %d" % index)
996
997         def getCounter(self, index):
998                 try:
999                         return self.counters[index]
1000                 except IndexError as e:
1001                         raise AwlSimError("Fetched invalid counter %d" % index)
1002
1003         def getSpecs(self):
1004                 return self.specs
1005
1006         def setMcrActive(self, active):
1007                 self.mcrActive = active
1008
1009         def mcrIsOn(self):
1010                 return (not self.mcrActive or all(self.mcrStack))
1011
1012         def mcrStackAppend(self, statusWord):
1013                 self.mcrStack.append(McrStackElem(statusWord))
1014                 if len(self.mcrStack) > 8:
1015                         raise AwlSimError("MCR stack overflow")
1016
1017         def mcrStackPop(self):
1018                 try:
1019                         return self.mcrStack.pop()
1020                 except IndexError:
1021                         raise AwlSimError("MCR stack underflow")
1022
1023         def parenStackAppend(self, insnType, statusWord):
1024                 cse = self.callStackTop
1025                 cse.parenStack.append(ParenStackElem(self, insnType, statusWord))
1026                 if len(cse.parenStack) > 7:
1027                         raise AwlSimError("Parenthesis stack overflow")
1028
1029         # Fetch a range in the 'output' memory area.
1030         # 'byteOffset' is the byte offset into the output area.
1031         # 'byteCount' is the number if bytes to fetch.
1032         # Returns a bytearray.
1033         def fetchOutputRange(self, byteOffset, byteCount):
1034                 return self.outputs[byteOffset : byteOffset + byteCount]
1035
1036         # Store a range in the 'input' memory area.
1037         # 'byteOffset' is the byte offset into the input area.
1038         # 'data' is a bytearray.
1039         def storeInputRange(self, byteOffset, data):
1040                 self.inputs[byteOffset : byteOffset + len(data)] = data
1041
1042         def fetch(self, operator, enforceWidth=()):
1043                 try:
1044                         fetchMethod = self.fetchTypeMethods[operator.type]
1045                 except KeyError:
1046                         raise AwlSimError("Invalid fetch request: %s" %\
1047                                 AwlOperator.type2str[operator.type])
1048                 return fetchMethod(self, operator, enforceWidth)
1049
1050         def __fetchWidthError(self, operator, enforceWidth):
1051                 raise AwlSimError("Data fetch of %d bits, "
1052                         "but only %s bits are allowed." %\
1053                         (operator.width,
1054                          listToHumanStr(enforceWidth)))
1055
1056         def fetchIMM(self, operator, enforceWidth):
1057                 if operator.width not in enforceWidth and enforceWidth:
1058                         self.__fetchWidthError(operator, enforceWidth)
1059
1060                 return operator.value
1061
1062         def fetchDBLG(self, operator, enforceWidth):
1063                 if operator.width not in enforceWidth and enforceWidth:
1064                         self.__fetchWidthError(operator, enforceWidth)
1065
1066                 return self.dbRegister.struct.getSize()
1067
1068         def fetchDBNO(self, operator, enforceWidth):
1069                 if operator.width not in enforceWidth and enforceWidth:
1070                         self.__fetchWidthError(operator, enforceWidth)
1071
1072                 return self.dbRegister.index
1073
1074         def fetchDILG(self, operator, enforceWidth):
1075                 if operator.width not in enforceWidth and enforceWidth:
1076                         self.__fetchWidthError(operator, enforceWidth)
1077
1078                 return self.diRegister.struct.getSize()
1079
1080         def fetchDINO(self, operator, enforceWidth):
1081                 if operator.width not in enforceWidth and enforceWidth:
1082                         self.__fetchWidthError(operator, enforceWidth)
1083
1084                 return self.diRegister.index
1085
1086         def fetchAR2(self, operator, enforceWidth):
1087                 if operator.width not in enforceWidth and enforceWidth:
1088                         self.__fetchWidthError(operator, enforceWidth)
1089
1090                 return self.getAR(2).get()
1091
1092         def fetchSTW(self, operator, enforceWidth):
1093                 if operator.width not in enforceWidth and enforceWidth:
1094                         self.__fetchWidthError(operator, enforceWidth)
1095
1096                 if operator.width == 1:
1097                         return self.statusWord.getByBitNumber(operator.value.bitOffset)
1098                 elif operator.width == 16:
1099                         return self.statusWord.getWord()
1100                 else:
1101                         assert(0)
1102
1103         def fetchSTW_Z(self, operator, enforceWidth):
1104                 if operator.width not in enforceWidth and enforceWidth:
1105                         self.__fetchWidthError(operator, enforceWidth)
1106
1107                 return (self.statusWord.A0 ^ 1) & (self.statusWord.A1 ^ 1)
1108
1109         def fetchSTW_NZ(self, operator, enforceWidth):
1110                 if operator.width not in enforceWidth and enforceWidth:
1111                         self.__fetchWidthError(operator, enforceWidth)
1112
1113                 return self.statusWord.A0 | self.statusWord.A1
1114
1115         def fetchSTW_POS(self, operator, enforceWidth):
1116                 if operator.width not in enforceWidth and enforceWidth:
1117                         self.__fetchWidthError(operator, enforceWidth)
1118
1119                 return (self.statusWord.A0 ^ 1) & self.statusWord.A1
1120
1121         def fetchSTW_NEG(self, operator, enforceWidth):
1122                 if operator.width not in enforceWidth and enforceWidth:
1123                         self.__fetchWidthError(operator, enforceWidth)
1124
1125                 return self.statusWord.A0 & (self.statusWord.A1 ^ 1)
1126
1127         def fetchSTW_POSZ(self, operator, enforceWidth):
1128                 if operator.width not in enforceWidth and enforceWidth:
1129                         self.__fetchWidthError(operator, enforceWidth)
1130
1131                 return self.statusWord.A0 ^ 1
1132
1133         def fetchSTW_NEGZ(self, operator, enforceWidth):
1134                 if operator.width not in enforceWidth and enforceWidth:
1135                         self.__fetchWidthError(operator, enforceWidth)
1136
1137                 return self.statusWord.A1 ^ 1
1138
1139         def fetchSTW_UO(self, operator, enforceWidth):
1140                 if operator.width not in enforceWidth and enforceWidth:
1141                         self.__fetchWidthError(operator, enforceWidth)
1142
1143                 return self.statusWord.A0 & self.statusWord.A1
1144
1145         def fetchE(self, operator, enforceWidth):
1146                 if operator.width not in enforceWidth and enforceWidth:
1147                         self.__fetchWidthError(operator, enforceWidth)
1148
1149                 return self.inputs.fetch(operator.value, operator.width)
1150
1151         def fetchA(self, operator, enforceWidth):
1152                 if operator.width not in enforceWidth and enforceWidth:
1153                         self.__fetchWidthError(operator, enforceWidth)
1154
1155                 return self.outputs.fetch(operator.value, operator.width)
1156
1157         def fetchM(self, operator, enforceWidth):
1158                 if operator.width not in enforceWidth and enforceWidth:
1159                         self.__fetchWidthError(operator, enforceWidth)
1160
1161                 return self.flags.fetch(operator.value, operator.width)
1162
1163         def fetchL(self, operator, enforceWidth):
1164                 if operator.width not in enforceWidth and enforceWidth:
1165                         self.__fetchWidthError(operator, enforceWidth)
1166
1167                 return self.callStackTop.localdata.fetch(operator.value, operator.width)
1168
1169         def fetchVL(self, operator, enforceWidth):
1170                 if operator.width not in enforceWidth and enforceWidth:
1171                         self.__fetchWidthError(operator, enforceWidth)
1172
1173                 try:
1174                         cse = self.callStack[-2]
1175                 except IndexError:
1176                         raise AwlSimError("Fetch of parent localstack, "
1177                                 "but no parent present.")
1178                 return cse.localdata.fetch(operator.value, operator.width)
1179
1180         def fetchDB(self, operator, enforceWidth):
1181                 if operator.width not in enforceWidth and enforceWidth:
1182                         self.__fetchWidthError(operator, enforceWidth)
1183
1184                 if operator.value.dbNumber is not None:
1185                         # This is a fully qualified access (DBx.DBx X)
1186                         # Open the data block first.
1187                         self.run_AUF(AwlOperator(AwlOperator.BLKREF_DB, 16,
1188                                                  AwlOffset(operator.value.dbNumber),
1189                                                  operator.insn))
1190                 if not self.dbRegister:
1191                         raise AwlSimError("Fetch from global DB, "
1192                                 "but no DB is opened")
1193                 return self.dbRegister.fetch(operator)
1194
1195         def fetchDI(self, operator, enforceWidth):
1196                 if operator.width not in enforceWidth and enforceWidth:
1197                         self.__fetchWidthError(operator, enforceWidth)
1198
1199                 if not self.diRegister:
1200                         raise AwlSimError("Fetch from instance DI, "
1201                                 "but no DI is opened")
1202                 return self.diRegister.fetch(operator)
1203
1204         def fetchPE(self, operator, enforceWidth):
1205                 if operator.width not in enforceWidth and enforceWidth:
1206                         self.__fetchWidthError(operator, enforceWidth)
1207
1208                 value = None
1209                 if self.cbPeripheralRead:
1210                         value = self.cbPeripheralRead(self.cbPeripheralReadData,
1211                                                       operator.width,
1212                                                       operator.value.byteOffset)
1213                 if value is None:
1214                         raise AwlSimError("There is no hardware to handle "
1215                                 "the direct peripheral fetch. "
1216                                 "(width=%d, offset=%d)" %\
1217                                 (operator.width, operator.value.byteOffset))
1218                 self.inputs.store(operator.value, operator.width, value)
1219                 return self.inputs.fetch(operator.value, operator.width)
1220
1221         def fetchT(self, operator, enforceWidth):
1222                 insnType = operator.insn.type
1223                 if insnType == AwlInsn.TYPE_L or insnType == AwlInsn.TYPE_LC:
1224                         width = 32
1225                 else:
1226                         width = 1
1227                 if width not in enforceWidth and enforceWidth:
1228                         self.__fetchWidthError(operator, enforceWidth)
1229
1230                 timer = self.getTimer(operator.value.byteOffset)
1231                 if insnType == AwlInsn.TYPE_L:
1232                         return timer.getTimevalBin()
1233                 elif insnType == AwlInsn.TYPE_LC:
1234                         return timer.getTimevalS5T()
1235                 return timer.get()
1236
1237         def fetchZ(self, operator, enforceWidth):
1238                 insnType = operator.insn.type
1239                 if insnType == AwlInsn.TYPE_L or insnType == AwlInsn.TYPE_LC:
1240                         width = 32
1241                 else:
1242                         width = 1
1243                 if width not in enforceWidth and enforceWidth:
1244                         self.__fetchWidthError(operator, enforceWidth)
1245
1246                 counter = self.getCounter(operator.value.byteOffset)
1247                 if insnType == AwlInsn.TYPE_L:
1248                         return counter.getValueBin()
1249                 elif insnType == AwlInsn.TYPE_LC:
1250                         return counter.getValueBCD()
1251                 return counter.get()
1252
1253         def fetchNAMED_LOCAL(self, operator, enforceWidth):
1254                 # load from an FC interface field.
1255                 return self.fetch(self.callStackTop.interfRefs[operator.interfaceIndex].resolve(False),
1256                                   enforceWidth)
1257
1258         def fetchNAMED_LOCAL_PTR(self, operator, enforceWidth):
1259                 return self.callStackTop.interfRefs[operator.interfaceIndex].resolve(False).makePointer()
1260
1261         def fetchNAMED_DBVAR(self, operator, enforceWidth):
1262                 # All legit accesses will have been translated to absolute addressing already
1263                 raise AwlSimError("Fully qualified load from DB variable "
1264                         "is not supported in this place.")
1265
1266         def fetchINDIRECT(self, operator, enforceWidth):
1267                 return self.fetch(operator.resolve(False), enforceWidth)
1268
1269         def fetchVirtACCU(self, operator, enforceWidth):
1270                 if operator.width not in enforceWidth and enforceWidth:
1271                         self.__fetchWidthError(operator, enforceWidth)
1272
1273                 return self.getAccu(operator.value.byteOffset).get()
1274
1275         def fetchVirtAR(self, operator, enforceWidth):
1276                 if operator.width not in enforceWidth and enforceWidth:
1277                         self.__fetchWidthError(operator, enforceWidth)
1278
1279                 return self.getAR(operator.value.byteOffset).get()
1280
1281         def fetchVirtDBR(self, operator, enforceWidth):
1282                 if operator.width not in enforceWidth and enforceWidth:
1283                         self.__fetchWidthError(operator, enforceWidth)
1284
1285                 if operator.value.byteOffset == 1:
1286                         if self.dbRegister:
1287                                 return self.dbRegister.index
1288                 elif operator.value.byteOffset == 2:
1289                         if self.diRegister:
1290                                 return self.diRegister.index
1291                 else:
1292                         raise AwlSimError("Invalid __DBR %d. "
1293                                 "Must be 1 for DB-register or "
1294                                 "2 for DI-register." %\
1295                                 operator.value.byteOffset)
1296                 return 0
1297
1298         fetchTypeMethods = {
1299                 AwlOperator.IMM                 : fetchIMM,
1300                 AwlOperator.IMM_REAL            : fetchIMM,
1301                 AwlOperator.IMM_S5T             : fetchIMM,
1302                 AwlOperator.IMM_TIME            : fetchIMM,
1303                 AwlOperator.IMM_DATE            : fetchIMM,
1304                 AwlOperator.IMM_TOD             : fetchIMM,
1305                 AwlOperator.IMM_PTR             : fetchIMM,
1306                 AwlOperator.MEM_E               : fetchE,
1307                 AwlOperator.MEM_A               : fetchA,
1308                 AwlOperator.MEM_M               : fetchM,
1309                 AwlOperator.MEM_L               : fetchL,
1310                 AwlOperator.MEM_VL              : fetchVL,
1311                 AwlOperator.MEM_DB              : fetchDB,
1312                 AwlOperator.MEM_DI              : fetchDI,
1313                 AwlOperator.MEM_T               : fetchT,
1314                 AwlOperator.MEM_Z               : fetchZ,
1315                 AwlOperator.MEM_PE              : fetchPE,
1316                 AwlOperator.MEM_DBLG            : fetchDBLG,
1317                 AwlOperator.MEM_DBNO            : fetchDBNO,
1318                 AwlOperator.MEM_DILG            : fetchDILG,
1319                 AwlOperator.MEM_DINO            : fetchDINO,
1320                 AwlOperator.MEM_AR2             : fetchAR2,
1321                 AwlOperator.MEM_STW             : fetchSTW,
1322                 AwlOperator.MEM_STW_Z           : fetchSTW_Z,
1323                 AwlOperator.MEM_STW_NZ          : fetchSTW_NZ,
1324                 AwlOperator.MEM_STW_POS         : fetchSTW_POS,
1325                 AwlOperator.MEM_STW_NEG         : fetchSTW_NEG,
1326                 AwlOperator.MEM_STW_POSZ        : fetchSTW_POSZ,
1327                 AwlOperator.MEM_STW_NEGZ        : fetchSTW_NEGZ,
1328                 AwlOperator.MEM_STW_UO          : fetchSTW_UO,
1329                 AwlOperator.NAMED_LOCAL         : fetchNAMED_LOCAL,
1330                 AwlOperator.NAMED_LOCAL_PTR     : fetchNAMED_LOCAL_PTR,
1331                 AwlOperator.NAMED_DBVAR         : fetchNAMED_DBVAR,
1332                 AwlOperator.INDIRECT            : fetchINDIRECT,
1333                 AwlOperator.VIRT_ACCU           : fetchVirtACCU,
1334                 AwlOperator.VIRT_AR             : fetchVirtAR,
1335                 AwlOperator.VIRT_DBR            : fetchVirtDBR,
1336         }
1337
1338         def store(self, operator, value, enforceWidth=()):
1339                 try:
1340                         storeMethod = self.storeTypeMethods[operator.type]
1341                 except KeyError:
1342                         raise AwlSimError("Invalid store request")
1343                 storeMethod(self, operator, value, enforceWidth)
1344
1345         def __storeWidthError(self, operator, enforceWidth):
1346                 raise AwlSimError("Data store of %d bits, "
1347                         "but only %s bits are allowed." %\
1348                         (operator.width,
1349                          listToHumanStr(enforceWidth)))
1350
1351         def storeE(self, operator, value, enforceWidth):
1352                 if operator.width not in enforceWidth and enforceWidth:
1353                         self.__storeWidthError(operator, enforceWidth)
1354
1355                 self.inputs.store(operator.value, operator.width, value)
1356
1357         def storeA(self, operator, value, enforceWidth):
1358                 if operator.width not in enforceWidth and enforceWidth:
1359                         self.__storeWidthError(operator, enforceWidth)
1360
1361                 self.outputs.store(operator.value, operator.width, value)
1362
1363         def storeM(self, operator, value, enforceWidth):
1364                 if operator.width not in enforceWidth and enforceWidth:
1365                         self.__storeWidthError(operator, enforceWidth)
1366
1367                 self.flags.store(operator.value, operator.width, value)
1368
1369         def storeL(self, operator, value, enforceWidth):
1370                 if operator.width not in enforceWidth and enforceWidth:
1371                         self.__storeWidthError(operator, enforceWidth)
1372
1373                 self.callStackTop.localdata.store(operator.value, operator.width, value)
1374
1375         def storeVL(self, operator, value, enforceWidth):
1376                 if operator.width not in enforceWidth and enforceWidth:
1377                         self.__storeWidthError(operator, enforceWidth)
1378
1379                 try:
1380                         cse = self.callStack[-2]
1381                 except IndexError:
1382                         raise AwlSimError("Store to parent localstack, "
1383                                 "but no parent present.")
1384                 cse.localdata.store(operator.value, operator.width, value)
1385
1386         def storeDB(self, operator, value, enforceWidth):
1387                 if operator.width not in enforceWidth and enforceWidth:
1388                         self.__storeWidthError(operator, enforceWidth)
1389
1390                 if operator.value.dbNumber is None:
1391                         db = self.dbRegister
1392                         if not db:
1393                                 raise AwlSimError("Store to global DB, "
1394                                         "but no DB is opened")
1395                 else:
1396                         try:
1397                                 db = self.dbs[operator.value.dbNumber]
1398                         except KeyError:
1399                                 raise AwlSimError("Store to DB %d, but DB "
1400                                         "does not exist" % operator.value.dbNumber)
1401                 db.store(operator, value)
1402
1403         def storeDI(self, operator, value, enforceWidth):
1404                 if operator.width not in enforceWidth and enforceWidth:
1405                         self.__storeWidthError(operator, enforceWidth)
1406
1407                 if not self.diRegister:
1408                         raise AwlSimError("Store to instance DI, "
1409                                 "but no DI is opened")
1410                 self.diRegister.store(operator, value)
1411
1412         def storePA(self, operator, value, enforceWidth):
1413                 if operator.width not in enforceWidth and enforceWidth:
1414                         self.__storeWidthError(operator, enforceWidth)
1415
1416                 self.outputs.store(operator.value, operator.width, value)
1417                 ok = False
1418                 if self.cbPeripheralWrite:
1419                         ok = self.cbPeripheralWrite(self.cbPeripheralWriteData,
1420                                                     operator.width,
1421                                                     operator.value.byteOffset,
1422                                                     value)
1423                 if not ok:
1424                         raise AwlSimError("There is no hardware to handle "
1425                                 "the direct peripheral store. "
1426                                 "(width=%d, offset=%d, value=0x%X)" %\
1427                                 (operator.width, operator.value.byteOffset,
1428                                  value))
1429
1430         def storeAR2(self, operator, value, enforceWidth):
1431                 if operator.width not in enforceWidth and enforceWidth:
1432                         self.__storeWidthError(operator, enforceWidth)
1433
1434                 self.getAR(2).set(value)
1435
1436         def storeSTW(self, operator, value, enforceWidth):
1437                 if operator.width not in enforceWidth and enforceWidth:
1438                         self.__storeWidthError(operator, enforceWidth)
1439
1440                 if operator.width == 1:
1441                         raise AwlSimError("Cannot store to individual STW bits")
1442                 elif operator.width == 16:
1443                         self.statusWord.setWord(value)
1444                 else:
1445                         assert(0)
1446
1447         def storeNAMED_LOCAL(self, operator, value, enforceWidth):
1448                 # store to an FC interface field.
1449                 self.store(self.callStackTop.interfRefs[operator.interfaceIndex].resolve(True),
1450                            value, enforceWidth)
1451
1452         def storeNAMED_DBVAR(self, operator, value, enforceWidth):
1453                 # All legit accesses will have been translated to absolute addressing already
1454                 raise AwlSimError("Fully qualified store to DB variable "
1455                         "is not supported in this place.")
1456
1457         def storeINDIRECT(self, operator, value, enforceWidth):
1458                 self.store(operator.resolve(True), value, enforceWidth)
1459
1460         storeTypeMethods = {
1461                 AwlOperator.MEM_E               : storeE,
1462                 AwlOperator.MEM_A               : storeA,
1463                 AwlOperator.MEM_M               : storeM,
1464                 AwlOperator.MEM_L               : storeL,
1465                 AwlOperator.MEM_VL              : storeVL,
1466                 AwlOperator.MEM_DB              : storeDB,
1467                 AwlOperator.MEM_DI              : storeDI,
1468                 AwlOperator.MEM_PA              : storePA,
1469                 AwlOperator.MEM_AR2             : storeAR2,
1470                 AwlOperator.MEM_STW             : storeSTW,
1471                 AwlOperator.NAMED_LOCAL         : storeNAMED_LOCAL,
1472                 AwlOperator.NAMED_DBVAR         : storeNAMED_DBVAR,
1473                 AwlOperator.INDIRECT            : storeINDIRECT,
1474         }
1475
1476         def __dumpMem(self, prefix, memArray, maxLen):
1477                 if not memArray:
1478                         return prefix + "--"
1479                 ret, line, first, count, i = [], [], True, 0, 0
1480                 while i < maxLen:
1481                         line.append("%02X" % memArray[i])
1482                         count += 1
1483                         if count >= 16:
1484                                 if not first:
1485                                         prefix = ' ' * len(prefix)
1486                                 first = False
1487                                 ret.append(prefix + ' '.join(line))
1488                                 line, count = [], 0
1489                         i += 1
1490                 assert(count == 0)
1491                 return '\n'.join(ret)
1492
1493         def __repr__(self):
1494                 if not self.callStack:
1495                         return ""
1496                 mnemonics = self.specs.getMnemonics()
1497                 isEnglish = (mnemonics == S7CPUSpecs.MNEMONICS_EN)
1498                 self.updateTimestamp()
1499                 ret = []
1500                 ret.append("=== S7-CPU dump ===  (t: %.01fs)" %\
1501                            (self.now - self.startupTime))
1502                 ret.append("    STW:  " + self.statusWord.getString(mnemonics))
1503                 if self.is4accu:
1504                         accus = [ accu.toHex()
1505                                   for accu in (self.accu1, self.accu2,
1506                                                self.accu3, self.accu4) ]
1507                 else:
1508                         accus = [ accu.toHex()
1509                                   for accu in (self.accu1, self.accu2) ]
1510                 ret.append("   Accu:  " + "  ".join(accus))
1511                 ars = [ "%s (%s)" % (ar.toHex(), ar.toPointerString())
1512                         for ar in (self.ar1, self.ar2) ]
1513                 ret.append("     AR:  " + "  ".join(ars))
1514                 ret.append(self.__dumpMem("      M:  ",
1515                                           self.flags,
1516                                           min(64, self.specs.nrFlags)))
1517                 prefix = "      I:  " if isEnglish else "      E:  "
1518                 ret.append(self.__dumpMem(prefix,
1519                                           self.inputs,
1520                                           min(64, self.specs.nrInputs)))
1521                 prefix = "      Q:  " if isEnglish else "      A:  "
1522                 ret.append(self.__dumpMem(prefix,
1523                                           self.outputs,
1524                                           min(64, self.specs.nrOutputs)))
1525                 pstack = str(self.callStackTop.parenStack) if self.callStackTop.parenStack else "Empty"
1526                 ret.append(" PStack:  " + pstack)
1527                 ret.append("     DB:  %s" % str(self.dbRegister))
1528                 ret.append("     DI:  %s" % str(self.diRegister))
1529                 if self.callStack:
1530                         elems = [ str(cse) for cse in self.callStack ]
1531                         elems = " => ".join(elems)
1532                         ret.append("  Calls:  depth:%d   %s" %\
1533                                    (len(self.callStack), elems))
1534                         localdata = self.callStack[-1].localdata
1535                         ret.append(self.__dumpMem("      L:  ",
1536                                                   localdata,
1537                                                   min(16, self.specs.nrLocalbytes)))
1538                         try:
1539                                 localdata = self.callStack[-2].localdata
1540                         except IndexError:
1541                                 localdata = None
1542                         ret.append(self.__dumpMem("     VL:  ",
1543                                                   localdata,
1544                                                   min(16, self.specs.nrLocalbytes)))
1545                 else:
1546                         ret.append("  Calls:  None")
1547                 curInsn = self.getCurrentInsn()
1548                 ret.append("   Stmt:  IP:%s   %s" %\
1549                            (str(self.getCurrentIP()),
1550                             str(curInsn) if curInsn else ""))
1551                 ret.append("  Speed:  %d stmt/s  %.01f stmt/cycle" %\
1552                            (int(round(self.insnPerSecond)),
1553                             self.avgInsnPerCycle))
1554                 ret.append(" CycleT:  avg:%.06fs  min:%.06fs  max:%.06fs" %\
1555                            (self.avgCycleTime, self.minCycleTime,
1556                             self.maxCycleTime))
1557                 return '\n'.join(ret)