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