gui: Add library selection widget
[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 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                                 raise AwlSimError("Multiple definitions of "\
317                                         "FB %d" % fbNumber)
318                         rawFB.index = fbNumber
319                         fb = translator.translateCodeBlock(rawFB, FB)
320                         self.fbs[fbNumber] = fb
321
322                 # Translate FCs
323                 for fcNumber, rawFC in parseTree.fcs.items():
324                         fcNumber, sym = resolver.resolveBlockName((AwlDataType.TYPE_FC_X,),
325                                                                   fcNumber)
326                         if fcNumber in self.fcs:
327                                 raise AwlSimError("Multiple definitions of "\
328                                         "FC %d" % fcNumber)
329                         rawFC.index = fcNumber
330                         fc = translator.translateCodeBlock(rawFC, FC)
331                         self.fcs[fcNumber] = fc
332
333                 if not self.sfbs:
334                         # Create the SFB tables
335                         for sfbNumber in SFB_table.keys():
336                                 if sfbNumber < 0 and not self.__extendedInsnsEnabled:
337                                         continue
338                                 sfb = SFB_table[sfbNumber](self)
339                                 self.sfbs[sfbNumber] = sfb
340
341                 if not self.sfcs:
342                         # Create the SFC tables
343                         for sfcNumber in SFC_table.keys():
344                                 if sfcNumber < 0 and not self.__extendedInsnsEnabled:
345                                         continue
346                                 sfc = SFC_table[sfcNumber](self)
347                                 self.sfcs[sfcNumber] = sfc
348
349                 # Build the data structures of code blocks.
350                 for block in self.__allCodeBlocks():
351                         block.interface.buildDataStructure(self)
352
353                 # Translate DBs
354                 for dbNumber, rawDB in parseTree.dbs.items():
355                         dbNumber, sym = resolver.resolveBlockName((AwlDataType.TYPE_DB_X,
356                                                                    AwlDataType.TYPE_FB_X,
357                                                                    AwlDataType.TYPE_SFB_X),
358                                                                   dbNumber)
359                         if dbNumber in self.dbs:
360                                 raise AwlSimError("Multiple definitions of "\
361                                         "DB %d" % dbNumber)
362                         rawDB.index = dbNumber
363                         db = translator.translateDB(rawDB)
364                         self.dbs[dbNumber] = db
365
366         def loadLibraryBlock(self, libSelection):
367                 pass#TODO
368
369         def loadSymbolTable(self, symbolTable):
370                 self.symbolTable.merge(symbolTable)
371
372         def reallocate(self, force=False):
373                 if force or (self.specs.nrAccus == 4) != self.is4accu:
374                         self.accu1, self.accu2, self.accu3, self.accu4 =\
375                                 Accu(), Accu(), Accu(), Accu()
376                         self.is4accu = (self.specs.nrAccus == 4)
377                 if force or self.specs.nrTimers != len(self.timers):
378                         self.timers = [ Timer(self, i)
379                                         for i in range(self.specs.nrTimers) ]
380                 if force or self.specs.nrCounters != len(self.counters):
381                         self.counters = [ Counter(self, i)
382                                           for i in range(self.specs.nrCounters) ]
383                 if force or self.specs.nrFlags != len(self.flags):
384                         self.flags = ByteArray(self.specs.nrFlags)
385                 if force or self.specs.nrInputs != len(self.inputs):
386                         self.inputs = ByteArray(self.specs.nrInputs)
387                 if force or self.specs.nrOutputs != len(self.outputs):
388                         self.outputs = ByteArray(self.specs.nrOutputs)
389                 CallStackElem.resetCache()
390
391         def reset(self):
392                 self.udts = {
393                         # UDTs
394                 }
395                 self.dbs = {
396                         # DBs
397                         0 : DB(0, permissions = 0), # read/write-protected system-DB
398                 }
399                 self.obs = {
400                         # OBs
401                         1 : OB([], 1), # Empty OB1
402                 }
403                 self.obTempPresetHandlers = {
404                         # OB TEMP-preset handlers
405                 }
406                 self.fcs = {
407                         # User FCs
408                 }
409                 self.fbs = {
410                         # User FBs
411                 }
412                 self.sfcs = {
413                         # System SFCs
414                 }
415                 self.sfbs = {
416                         # System SFBs
417                 }
418                 self.symbolTable = SymbolTable()
419                 self.is4accu = False
420                 self.reallocate(force=True)
421                 self.ar1 = Adressregister()
422                 self.ar2 = Adressregister()
423                 self.dbRegister = self.dbs[0]
424                 self.diRegister = self.dbs[0]
425                 self.callStack = [ ]
426                 self.callStackTop = None
427                 self.setMcrActive(False)
428                 self.mcrStack = [ ]
429                 self.statusWord = S7StatusWord()
430                 self.__clockMemByteOffset = None
431                 self.setRunTimeLimit()
432
433                 self.relativeJump = 1
434
435                 # Stats
436                 self.__insnCount = 0
437                 self.__cycleCount = 0
438                 self.insnPerSecond = 0.0
439                 self.avgInsnPerCycle = 0.0
440                 self.cycleStartTime = 0.0
441                 self.minCycleTime = 86400.0
442                 self.maxCycleTime = 0.0
443                 self.avgCycleTime = 0.0
444                 self.startupTime = self.__getTime()
445                 self.__speedMeasureStartTime = 0
446                 self.__speedMeasureStartInsnCount = 0
447                 self.__speedMeasureStartCycleCount = 0
448
449                 self.updateTimestamp()
450
451         def setCycleExitCallback(self, cb, data=None):
452                 self.cbCycleExit = cb
453                 self.cbCycleExitData = data
454
455         def setBlockExitCallback(self, cb, data=None):
456                 self.cbBlockExit = cb
457                 self.cbBlockExitData = data
458
459         def setPostInsnCallback(self, cb, data=None):
460                 self.cbPostInsn = cb
461                 self.cbPostInsnData = data
462
463         def setPeripheralReadCallback(self, cb, data=None):
464                 self.cbPeripheralRead = cb
465                 self.cbPeripheralReadData = data
466
467         def setPeripheralWriteCallback(self, cb, data=None):
468                 self.cbPeripheralWrite = cb
469                 self.cbPeripheralWriteData = data
470
471         def setScreenUpdateCallback(self, cb, data=None):
472                 self.cbScreenUpdate = cb
473                 self.cbScreenUpdateData = data
474
475         def requestScreenUpdate(self):
476                 if self.cbScreenUpdate:
477                         self.cbScreenUpdate(self.cbScreenUpdateData)
478
479         def __runOB(self, block):
480                 # Update timekeeping
481                 self.updateTimestamp()
482                 self.cycleStartTime = self.now
483
484                 # Initialize CPU state
485                 self.dbRegister = self.diRegister = self.dbs[0]
486                 self.accu1.reset()
487                 self.accu2.reset()
488                 self.accu3.reset()
489                 self.accu4.reset()
490                 self.ar1.reset()
491                 self.ar2.reset()
492                 self.statusWord.reset()
493                 self.callStack = [ CallStackElem(self, block, None, None, (), True) ]
494                 cse = self.callStackTop = self.callStack[-1]
495                 if self.__obTempPresetsEnabled:
496                         # Populate the TEMP region
497                         self.obTempPresetHandlers[block.index].generate(cse.localdata)
498
499                 # Run the user program cycle
500                 while self.callStack:
501                         while cse.ip < len(cse.insns):
502                                 insn, self.relativeJump = cse.insns[cse.ip], 1
503                                 insn.run()
504                                 if self.cbPostInsn:
505                                         self.cbPostInsn(cse, self.cbPostInsnData)
506                                 cse.ip += self.relativeJump
507                                 cse, self.__insnCount = self.callStackTop,\
508                                                         (self.__insnCount + 1) & 0x3FFFFFFF
509                                 if not self.__insnCount % 64:
510                                         self.updateTimestamp()
511                                         self.__runTimeCheck()
512                         if self.cbBlockExit:
513                                 self.cbBlockExit(self.cbBlockExitData)
514                         prevCse = self.callStack.pop()
515                         if self.callStack:
516                                 cse = self.callStackTop = self.callStack[-1]
517                         prevCse.handleBlockExit()
518                 if self.cbCycleExit:
519                         self.cbCycleExit(self.cbCycleExitData)
520
521         # Run startup code
522         def startup(self):
523                 # Resolve symbolic instructions and operators
524                 self.__resolveSymbols()
525                 # Do some finalizations
526                 self.__finalizeCodeBlocks()
527                 # Run some static sanity checks on the code
528                 self.__staticSanityChecks()
529
530                 self.updateTimestamp()
531                 self.__speedMeasureStartTime = self.now
532                 self.__speedMeasureStartInsnCount = 0
533                 self.__speedMeasureStartCycleCount = 0
534                 self.startupTime = self.now
535
536                 if self.specs.clockMemByte >= 0:
537                         self.__clockMemByteOffset = AwlOffset(self.specs.clockMemByte)
538                 else:
539                         self.__clockMemByteOffset = None
540                 self.__nextClockMemTime = self.now + 0.05
541                 self.__clockMemCount = 0
542                 self.__clockMemCountLCM = math_lcm(2, 4, 5, 8, 10, 16, 20)
543
544                 # Run startup OB
545                 if 102 in self.obs and self.is4accu:
546                         # Cold start.
547                         # This is only done on 4xx-series CPUs.
548                         self.__runOB(self.obs[102])
549                 elif 100 in self.obs:
550                         # Warm start.
551                         # This really is a cold start, because remanent
552                         # resources were reset. However we could not execute
553                         # OB 102, so this is a fallback.
554                         # This is not 100% compliant with real CPUs, but it probably
555                         # is sane behavior.
556                         self.__runOB(self.obs[100])
557
558         # Run one cycle of the user program
559         def runCycle(self):
560                 # Run the actual OB1 code
561                 self.__runOB(self.obs[1])
562
563                 # Update timekeeping and statistics
564                 self.updateTimestamp()
565                 self.__cycleCount = (self.__cycleCount + 1) & 0x3FFFFFFF
566
567                 # Evaluate speed measurement
568                 elapsedTime = self.now - self.__speedMeasureStartTime
569                 if elapsedTime >= 1.0:
570                         # Calculate instruction and cycle counts.
571                         cycleCount = (self.__cycleCount - self.__speedMeasureStartCycleCount) &\
572                                      0x3FFFFFFF
573                         insnCount = (self.__insnCount - self.__speedMeasureStartInsnCount) &\
574                                     0x3FFFFFFF
575
576                         # Calculate instruction statistics.
577                         self.insnPerSecond = insnCount / elapsedTime
578                         self.avgInsnPerCycle = insnCount / cycleCount
579
580                         # Get the average cycle time over the measurement period.
581                         cycleTime = elapsedTime / cycleCount
582
583                         # Store overall-average cycle time and maximum cycle time.
584                         self.maxCycleTime = max(self.maxCycleTime, cycleTime)
585                         self.minCycleTime = min(self.minCycleTime, cycleTime)
586                         self.avgCycleTime = (self.avgCycleTime + cycleTime) / 2.0
587
588                         # Reset the counters
589                         self.__speedMeasureStartTime = self.now
590                         self.__speedMeasureStartInsnCount = self.__insnCount
591                         self.__speedMeasureStartCycleCount = self.__cycleCount
592
593         __getTime = getattr(time, "perf_counter", time.time)
594
595         # updateTimestamp() updates self.now, which is a
596         # floating point count of seconds.
597         def updateTimestamp(self):
598                 # Update the system time
599                 self.now = self.__getTime()
600                 # Update the clock memory byte
601                 if self.__clockMemByteOffset:
602                         try:
603                                 if self.now >= self.__nextClockMemTime:
604                                         self.__nextClockMemTime += 0.05
605                                         value = self.flags.fetch(self.__clockMemByteOffset, 8)
606                                         value ^= 0x01 # 0.1s period
607                                         count = self.__clockMemCount + 1
608                                         self.__clockMemCount = count
609                                         if not count % 2:
610                                                 value ^= 0x02 # 0.2s period
611                                         if not count % 4:
612                                                 value ^= 0x04 # 0.4s period
613                                         if not count % 5:
614                                                 value ^= 0x08 # 0.5s period
615                                         if not count % 8:
616                                                 value ^= 0x10 # 0.8s period
617                                         if not count % 10:
618                                                 value ^= 0x20 # 1.0s period
619                                         if not count % 16:
620                                                 value ^= 0x40 # 1.6s period
621                                         if not count % 20:
622                                                 value ^= 0x80 # 2.0s period
623                                         if count >= self.__clockMemCountLCM:
624                                                 self.__clockMemCount = 0
625                                         self.flags.store(self.__clockMemByteOffset, 8, value)
626                         except AwlSimError as e:
627                                 raise AwlSimError("Failed to generate clock "
628                                         "memory signal:\n" + str(e) +\
629                                         "\n\nThe configured clock memory byte "
630                                         "address might be invalid." )
631                 # Check whether the runtime timeout exceeded
632                 if self.__runtimeLimit is not None:
633                         if self.now - self.startupTime >= self.__runtimeLimit:
634                                 raise MaintenanceRequest(MaintenanceRequest.TYPE_RTTIMEOUT,
635                                         "CPU runtime timeout")
636
637         __dateAndTimeWeekdayMap = {
638                 0       : 2,    # monday
639                 1       : 3,    # tuesday
640                 2       : 4,    # wednesday
641                 3       : 5,    # thursday
642                 4       : 6,    # friday
643                 5       : 7,    # saturday
644                 6       : 1,    # sunday
645         }
646
647         # Make a DATE_AND_TIME for the current wall-time and
648         # store it in byteArray, which is a list of GenericByte objects.
649         # If byteArray is smaller than 8 bytes, an IndexError is raised.
650         def makeCurrentDateAndTime(self, byteArray, offset):
651                 dt = datetime.datetime.now()
652                 year, month, day, hour, minute, second, msec =\
653                         dt.year, dt.month, dt.day, dt.hour, \
654                         dt.minute, dt.second, dt.microsecond // 1000
655                 byteArray[offset] = (year % 10) | (((year // 10) % 10) << 4)
656                 byteArray[offset + 1] = (month % 10) | (((month // 10) % 10) << 4)
657                 byteArray[offset + 2] = (day % 10) | (((day // 10) % 10) << 4)
658                 byteArray[offset + 3] = (hour % 10) | (((hour // 10) % 10) << 4)
659                 byteArray[offset + 4] = (minute % 10) | (((minute // 10) % 10) << 4)
660                 byteArray[offset + 5] = (second % 10) | (((second // 10) % 10) << 4)
661                 byteArray[offset + 6] = ((msec // 10) % 10) | (((msec // 100) % 10) << 4)
662                 byteArray[offset + 7] = ((msec % 10) << 4) |\
663                                         self.__dateAndTimeWeekdayMap[dt.weekday()]
664
665         def __runTimeCheck(self):
666                 if self.now - self.cycleStartTime > self.cycleTimeLimit:
667                         raise AwlSimError("Cycle time exceed %.3f seconds" %\
668                                           self.cycleTimeLimit)
669
670         def getCurrentIP(self):
671                 try:
672                         return self.callStackTop.ip
673                 except IndexError as e:
674                         return None
675
676         def getCurrentInsn(self):
677                 try:
678                         cse = self.callStackTop
679                         if not cse:
680                                 return None
681                         return cse.insns[cse.ip]
682                 except IndexError as e:
683                         return None
684
685         def labelIdxToRelJump(self, labelIndex):
686                 # Translate a label index into a relative IP offset.
687                 cse = self.callStackTop
688                 label = cse.block.labels[labelIndex]
689                 return label.getInsn().getIP() - cse.ip
690
691         def jumpToLabel(self, labelIndex):
692                 self.relativeJump = self.labelIdxToRelJump(labelIndex)
693
694         def jumpRelative(self, insnOffset):
695                 self.relativeJump = insnOffset
696
697         def __call_FC(self, blockOper, dbOper, parameters):
698                 fc = self.fcs[blockOper.value.byteOffset]
699                 return CallStackElem(self, fc, None, None, parameters)
700
701         def __call_RAW_FC(self, blockOper, dbOper, parameters):
702                 fc = self.fcs[blockOper.value.byteOffset]
703                 return CallStackElem(self, fc, None, None, (), True)
704
705         def __call_FB(self, blockOper, dbOper, parameters):
706                 fb = self.fbs[blockOper.value.byteOffset]
707                 db = self.dbs[dbOper.value.byteOffset]
708                 cse = CallStackElem(self, fb, db, AwlOffset(), parameters)
709                 self.dbRegister, self.diRegister = self.diRegister, db
710                 return cse
711
712         def __call_RAW_FB(self, blockOper, dbOper, parameters):
713                 fb = self.fbs[blockOper.value.byteOffset]
714                 return CallStackElem(self, fb, self.diRegister, None, (), True)
715
716         def __call_SFC(self, blockOper, dbOper, parameters):
717                 sfc = self.sfcs[blockOper.value.byteOffset]
718                 return CallStackElem(self, sfc, None, None, parameters)
719
720         def __call_RAW_SFC(self, blockOper, dbOper, parameters):
721                 sfc = self.sfcs[blockOper.value.byteOffset]
722                 return CallStackElem(self, sfc, None, None, (), True)
723
724         def __call_SFB(self, blockOper, dbOper, parameters):
725                 sfb = self.sfbs[blockOper.value.byteOffset]
726                 db = self.dbs[dbOper.value.byteOffset]
727                 cse = CallStackElem(self, sfb, db, AwlOffset(), parameters)
728                 self.dbRegister, self.diRegister = self.diRegister, db
729                 return cse
730
731         def __call_RAW_SFB(self, blockOper, dbOper, parameters):
732                 sfb = self.sfbs[blockOper.value.byteOffset]
733                 return CallStackElem(self, sfb, self.diRegister, None, (), True)
734
735         def __call_INDIRECT(self, blockOper, dbOper, parameters):
736                 blockOper = blockOper.resolve()
737                 callHelper = self.__rawCallHelpers[blockOper.type]
738                 try:
739                         return callHelper(self, blockOper, dbOper, parameters)
740                 except KeyError as e:
741                         raise AwlSimError("Code block %d not found in indirect call" %\
742                                           blockOper.value.byteOffset)
743
744         def __call_MULTI_FB(self, blockOper, dbOper, parameters):
745                 fb = self.fbs[blockOper.value.fbNumber]
746                 cse = CallStackElem(self, fb, self.diRegister, blockOper.value, parameters)
747                 self.dbRegister = self.diRegister
748                 return cse
749
750         def __call_MULTI_SFB(self, blockOper, dbOper, parameters):
751                 sfb = self.sfbs[blockOper.value.fbNumber]
752                 cse = CallStackElem(self, sfb, self.diRegister, blockOper.value, parameters)
753                 self.dbRegister = self.diRegister
754                 return cse
755
756         __callHelpers = {
757                 AwlOperator.BLKREF_FC   : __call_FC,
758                 AwlOperator.BLKREF_FB   : __call_FB,
759                 AwlOperator.BLKREF_SFC  : __call_SFC,
760                 AwlOperator.BLKREF_SFB  : __call_SFB,
761                 AwlOperator.MULTI_FB    : __call_MULTI_FB,
762                 AwlOperator.MULTI_SFB   : __call_MULTI_SFB,
763         }
764
765         __rawCallHelpers = {
766                 AwlOperator.BLKREF_FC   : __call_RAW_FC,
767                 AwlOperator.BLKREF_FB   : __call_RAW_FB,
768                 AwlOperator.BLKREF_SFC  : __call_RAW_SFC,
769                 AwlOperator.BLKREF_SFB  : __call_RAW_SFB,
770                 AwlOperator.INDIRECT    : __call_INDIRECT,
771         }
772
773         def run_CALL(self, blockOper, dbOper=None, parameters=(), raw=False):
774                 try:
775                         if raw:
776                                 callHelper = self.__rawCallHelpers[blockOper.type]
777                         else:
778                                 callHelper = self.__callHelpers[blockOper.type]
779                 except KeyError:
780                         raise AwlSimError("Invalid CALL operand")
781                 newCse = callHelper(self, blockOper, dbOper, parameters)
782                 self.callStack.append(newCse)
783                 self.callStackTop = newCse
784
785         def run_BE(self):
786                 s = self.statusWord
787                 s.OS, s.OR, s.STA, s.NER = 0, 0, 1, 0
788                 # Jump beyond end of block
789                 cse = self.callStackTop
790                 self.relativeJump = len(cse.insns) - cse.ip
791
792         def run_AUF(self, dbOper):
793                 dbOper = dbOper.resolve()
794                 try:
795                         db = self.dbs[dbOper.value.byteOffset]
796                 except KeyError:
797                         raise AwlSimError("Datablock %i does not exist" %\
798                                           dbOper.value.byteOffset)
799                 if dbOper.type == AwlOperator.BLKREF_DB:
800                         self.dbRegister = db
801                 elif dbOper.type == AwlOperator.BLKREF_DI:
802                         self.diRegister = db
803                 else:
804                         raise AwlSimError("Invalid DB reference in AUF")
805
806         def run_TDB(self):
807                 # Swap global and instance DB
808                 self.diRegister, self.dbRegister = self.dbRegister, self.diRegister
809
810         def getStatusWord(self):
811                 return self.statusWord
812
813         def getAccu(self, index):
814                 if index < 1 or index > self.specs.nrAccus:
815                         raise AwlSimError("Invalid ACCU offset")
816                 return (self.accu1, self.accu2,
817                         self.accu3, self.accu4)[index - 1]
818
819         def getAR(self, index):
820                 if index < 1 or index > 2:
821                         raise AwlSimError("Invalid AR offset")
822                 return (self.ar1, self.ar2)[index - 1]
823
824         def getTimer(self, index):
825                 try:
826                         return self.timers[index]
827                 except IndexError as e:
828                         raise AwlSimError("Fetched invalid timer %d" % index)
829
830         def getCounter(self, index):
831                 try:
832                         return self.counters[index]
833                 except IndexError as e:
834                         raise AwlSimError("Fetched invalid counter %d" % index)
835
836         def getSpecs(self):
837                 return self.specs
838
839         def setMcrActive(self, active):
840                 self.mcrActive = active
841
842         def mcrIsOn(self):
843                 return (not self.mcrActive or all(self.mcrStack))
844
845         def mcrStackAppend(self, statusWord):
846                 self.mcrStack.append(McrStackElem(statusWord))
847                 if len(self.mcrStack) > 8:
848                         raise AwlSimError("MCR stack overflow")
849
850         def mcrStackPop(self):
851                 try:
852                         return self.mcrStack.pop()
853                 except IndexError:
854                         raise AwlSimError("MCR stack underflow")
855
856         def parenStackAppend(self, insnType, statusWord):
857                 cse = self.callStackTop
858                 cse.parenStack.append(ParenStackElem(self, insnType, statusWord))
859                 if len(cse.parenStack) > 7:
860                         raise AwlSimError("Parenthesis stack overflow")
861
862         # Fetch a range in the 'output' memory area.
863         # 'byteOffset' is the byte offset into the output area.
864         # 'byteCount' is the number if bytes to fetch.
865         # Returns a bytearray.
866         def fetchOutputRange(self, byteOffset, byteCount):
867                 return self.outputs[byteOffset : byteOffset + byteCount]
868
869         # Store a range in the 'input' memory area.
870         # 'byteOffset' is the byte offset into the input area.
871         # 'data' is a bytearray.
872         def storeInputRange(self, byteOffset, data):
873                 self.inputs[byteOffset : byteOffset + len(data)] = data
874
875         def fetch(self, operator, enforceWidth=()): #@nocy
876 #@cy    cpdef object fetch(self, object operator, tuple enforceWidth=()):
877                 try:
878                         fetchMethod = self.fetchTypeMethods[operator.type]
879                 except KeyError:
880                         raise AwlSimError("Invalid fetch request: %s" %\
881                                 AwlOperator.type2str[operator.type])
882                 return fetchMethod(self, operator, enforceWidth)
883
884         def __fetchWidthError(self, operator, enforceWidth):
885                 raise AwlSimError("Data fetch of %d bits, "
886                         "but only %s bits are allowed." %\
887                         (operator.width,
888                          listToHumanStr(enforceWidth)))
889
890         def fetchIMM(self, operator, enforceWidth):
891                 if operator.width not in enforceWidth and enforceWidth:
892                         self.__fetchWidthError(operator, enforceWidth)
893
894                 return operator.value
895
896         def fetchDBLG(self, operator, enforceWidth):
897                 if operator.width not in enforceWidth and enforceWidth:
898                         self.__fetchWidthError(operator, enforceWidth)
899
900                 return self.dbRegister.struct.getSize()
901
902         def fetchDBNO(self, operator, enforceWidth):
903                 if operator.width not in enforceWidth and enforceWidth:
904                         self.__fetchWidthError(operator, enforceWidth)
905
906                 return self.dbRegister.index
907
908         def fetchDILG(self, operator, enforceWidth):
909                 if operator.width not in enforceWidth and enforceWidth:
910                         self.__fetchWidthError(operator, enforceWidth)
911
912                 return self.diRegister.struct.getSize()
913
914         def fetchDINO(self, operator, enforceWidth):
915                 if operator.width not in enforceWidth and enforceWidth:
916                         self.__fetchWidthError(operator, enforceWidth)
917
918                 return self.diRegister.index
919
920         def fetchAR2(self, operator, enforceWidth):
921                 if operator.width not in enforceWidth and enforceWidth:
922                         self.__fetchWidthError(operator, enforceWidth)
923
924                 return self.getAR(2).get()
925
926         def fetchSTW(self, operator, enforceWidth):
927                 if operator.width not in enforceWidth and enforceWidth:
928                         self.__fetchWidthError(operator, enforceWidth)
929
930                 if operator.width == 1:
931                         return self.statusWord.getByBitNumber(operator.value.bitOffset)
932                 elif operator.width == 16:
933                         return self.statusWord.getWord()
934                 else:
935                         assert(0)
936
937         def fetchSTW_Z(self, operator, enforceWidth):
938                 if operator.width not in enforceWidth and enforceWidth:
939                         self.__fetchWidthError(operator, enforceWidth)
940
941                 return (self.statusWord.A0 ^ 1) & (self.statusWord.A1 ^ 1)
942
943         def fetchSTW_NZ(self, operator, enforceWidth):
944                 if operator.width not in enforceWidth and enforceWidth:
945                         self.__fetchWidthError(operator, enforceWidth)
946
947                 return self.statusWord.A0 | self.statusWord.A1
948
949         def fetchSTW_POS(self, operator, enforceWidth):
950                 if operator.width not in enforceWidth and enforceWidth:
951                         self.__fetchWidthError(operator, enforceWidth)
952
953                 return (self.statusWord.A0 ^ 1) & self.statusWord.A1
954
955         def fetchSTW_NEG(self, operator, enforceWidth):
956                 if operator.width not in enforceWidth and enforceWidth:
957                         self.__fetchWidthError(operator, enforceWidth)
958
959                 return self.statusWord.A0 & (self.statusWord.A1 ^ 1)
960
961         def fetchSTW_POSZ(self, operator, enforceWidth):
962                 if operator.width not in enforceWidth and enforceWidth:
963                         self.__fetchWidthError(operator, enforceWidth)
964
965                 return self.statusWord.A0 ^ 1
966
967         def fetchSTW_NEGZ(self, operator, enforceWidth):
968                 if operator.width not in enforceWidth and enforceWidth:
969                         self.__fetchWidthError(operator, enforceWidth)
970
971                 return self.statusWord.A1 ^ 1
972
973         def fetchSTW_UO(self, operator, enforceWidth):
974                 if operator.width not in enforceWidth and enforceWidth:
975                         self.__fetchWidthError(operator, enforceWidth)
976
977                 return self.statusWord.A0 & self.statusWord.A1
978
979         def fetchE(self, operator, enforceWidth):
980                 if operator.width not in enforceWidth and enforceWidth:
981                         self.__fetchWidthError(operator, enforceWidth)
982
983                 return self.inputs.fetch(operator.value, operator.width)
984
985         def fetchA(self, operator, enforceWidth):
986                 if operator.width not in enforceWidth and enforceWidth:
987                         self.__fetchWidthError(operator, enforceWidth)
988
989                 return self.outputs.fetch(operator.value, operator.width)
990
991         def fetchM(self, operator, enforceWidth):
992                 if operator.width not in enforceWidth and enforceWidth:
993                         self.__fetchWidthError(operator, enforceWidth)
994
995                 return self.flags.fetch(operator.value, operator.width)
996
997         def fetchL(self, operator, enforceWidth):
998                 if operator.width not in enforceWidth and enforceWidth:
999                         self.__fetchWidthError(operator, enforceWidth)
1000
1001                 return self.callStackTop.localdata.fetch(operator.value, operator.width)
1002
1003         def fetchVL(self, operator, enforceWidth):
1004                 if operator.width not in enforceWidth and enforceWidth:
1005                         self.__fetchWidthError(operator, enforceWidth)
1006
1007                 try:
1008                         cse = self.callStack[-2]
1009                 except IndexError:
1010                         raise AwlSimError("Fetch of parent localstack, "
1011                                 "but no parent present.")
1012                 return cse.localdata.fetch(operator.value, operator.width)
1013
1014         def fetchDB(self, operator, enforceWidth):
1015                 if operator.width not in enforceWidth and enforceWidth:
1016                         self.__fetchWidthError(operator, enforceWidth)
1017
1018                 if operator.value.dbNumber is not None:
1019                         # This is a fully qualified access (DBx.DBx X)
1020                         # Open the data block first.
1021                         self.run_AUF(AwlOperator(AwlOperator.BLKREF_DB, 16,
1022                                                  AwlOffset(operator.value.dbNumber),
1023                                                  operator.insn))
1024                 return self.dbRegister.fetch(operator)
1025
1026         def fetchDI(self, operator, enforceWidth):
1027                 if operator.width not in enforceWidth and enforceWidth:
1028                         self.__fetchWidthError(operator, enforceWidth)
1029
1030                 if self.callStackTop.block.isFB:
1031                         # Fetch the data using the multi-instance base offset from AR2.
1032                         return self.diRegister.fetch(operator,
1033                                                      AwlOffset.fromPointerValue(self.ar2.get()))
1034                 # Fetch without base offset.
1035                 return self.diRegister.fetch(operator)
1036
1037         def fetchPE(self, operator, enforceWidth):
1038                 if operator.width not in enforceWidth and enforceWidth:
1039                         self.__fetchWidthError(operator, enforceWidth)
1040
1041                 value = None
1042                 if self.cbPeripheralRead:
1043                         value = self.cbPeripheralRead(self.cbPeripheralReadData,
1044                                                       operator.width,
1045                                                       operator.value.byteOffset)
1046                 if value is None:
1047                         raise AwlSimError("There is no hardware to handle "
1048                                 "the direct peripheral fetch. "
1049                                 "(width=%d, offset=%d)" %\
1050                                 (operator.width, operator.value.byteOffset))
1051                 self.inputs.store(operator.value, operator.width, value)
1052                 return self.inputs.fetch(operator.value, operator.width)
1053
1054         def fetchT(self, operator, enforceWidth):
1055                 insnType = operator.insn.insnType
1056                 if insnType == AwlInsn.TYPE_L or insnType == AwlInsn.TYPE_LC:
1057                         width = 32
1058                 else:
1059                         width = 1
1060                 if width not in enforceWidth and enforceWidth:
1061                         self.__fetchWidthError(operator, enforceWidth)
1062
1063                 timer = self.getTimer(operator.value.byteOffset)
1064                 if insnType == AwlInsn.TYPE_L:
1065                         return timer.getTimevalBin()
1066                 elif insnType == AwlInsn.TYPE_LC:
1067                         return timer.getTimevalS5T()
1068                 return timer.get()
1069
1070         def fetchZ(self, operator, enforceWidth):
1071                 insnType = operator.insn.insnType
1072                 if insnType == AwlInsn.TYPE_L or insnType == AwlInsn.TYPE_LC:
1073                         width = 32
1074                 else:
1075                         width = 1
1076                 if width not in enforceWidth and enforceWidth:
1077                         self.__fetchWidthError(operator, enforceWidth)
1078
1079                 counter = self.getCounter(operator.value.byteOffset)
1080                 if insnType == AwlInsn.TYPE_L:
1081                         return counter.getValueBin()
1082                 elif insnType == AwlInsn.TYPE_LC:
1083                         return counter.getValueBCD()
1084                 return counter.get()
1085
1086         def fetchNAMED_LOCAL(self, operator, enforceWidth):
1087                 # load from an FC interface field.
1088                 # First get the translated rvalue-operand that was used in the call.
1089                 translatedOp = self.callStackTop.interfRefs[operator.interfaceIndex].resolve(False)
1090                 # Add possible sub-offsets (array, struct) to the offset.
1091                 finalOp = translatedOp.dup()
1092                 finalOp.value += operator.value.subOffset
1093                 finalOp.width = operator.width
1094                 return self.fetch(finalOp, enforceWidth)
1095
1096         def fetchNAMED_LOCAL_PTR(self, operator, enforceWidth):
1097                 assert(operator.value.subOffset.byteOffset == 0)
1098                 return self.callStackTop.interfRefs[operator.interfaceIndex].resolve(False).makePointer()
1099
1100         def fetchNAMED_DBVAR(self, operator, enforceWidth):
1101                 # All legit accesses will have been translated to absolute addressing already
1102                 raise AwlSimError("Fully qualified load from DB variable "
1103                         "is not supported in this place.")
1104
1105         def fetchINDIRECT(self, operator, enforceWidth):
1106                 return self.fetch(operator.resolve(False), enforceWidth)
1107
1108         def fetchVirtACCU(self, operator, enforceWidth):
1109                 if operator.width not in enforceWidth and enforceWidth:
1110                         self.__fetchWidthError(operator, enforceWidth)
1111
1112                 return self.getAccu(operator.value.byteOffset).get()
1113
1114         def fetchVirtAR(self, operator, enforceWidth):
1115                 if operator.width not in enforceWidth and enforceWidth:
1116                         self.__fetchWidthError(operator, enforceWidth)
1117
1118                 return self.getAR(operator.value.byteOffset).get()
1119
1120         def fetchVirtDBR(self, operator, enforceWidth):
1121                 if operator.width not in enforceWidth and enforceWidth:
1122                         self.__fetchWidthError(operator, enforceWidth)
1123
1124                 if operator.value.byteOffset == 1:
1125                         if self.dbRegister:
1126                                 return self.dbRegister.index
1127                 elif operator.value.byteOffset == 2:
1128                         if self.diRegister:
1129                                 return self.diRegister.index
1130                 else:
1131                         raise AwlSimError("Invalid __DBR %d. "
1132                                 "Must be 1 for DB-register or "
1133                                 "2 for DI-register." %\
1134                                 operator.value.byteOffset)
1135                 return 0
1136
1137         fetchTypeMethods = {
1138                 AwlOperator.IMM                 : fetchIMM,
1139                 AwlOperator.IMM_REAL            : fetchIMM,
1140                 AwlOperator.IMM_S5T             : fetchIMM,
1141                 AwlOperator.IMM_TIME            : fetchIMM,
1142                 AwlOperator.IMM_DATE            : fetchIMM,
1143                 AwlOperator.IMM_TOD             : fetchIMM,
1144                 AwlOperator.IMM_PTR             : fetchIMM,
1145                 AwlOperator.MEM_E               : fetchE,
1146                 AwlOperator.MEM_A               : fetchA,
1147                 AwlOperator.MEM_M               : fetchM,
1148                 AwlOperator.MEM_L               : fetchL,
1149                 AwlOperator.MEM_VL              : fetchVL,
1150                 AwlOperator.MEM_DB              : fetchDB,
1151                 AwlOperator.MEM_DI              : fetchDI,
1152                 AwlOperator.MEM_T               : fetchT,
1153                 AwlOperator.MEM_Z               : fetchZ,
1154                 AwlOperator.MEM_PE              : fetchPE,
1155                 AwlOperator.MEM_DBLG            : fetchDBLG,
1156                 AwlOperator.MEM_DBNO            : fetchDBNO,
1157                 AwlOperator.MEM_DILG            : fetchDILG,
1158                 AwlOperator.MEM_DINO            : fetchDINO,
1159                 AwlOperator.MEM_AR2             : fetchAR2,
1160                 AwlOperator.MEM_STW             : fetchSTW,
1161                 AwlOperator.MEM_STW_Z           : fetchSTW_Z,
1162                 AwlOperator.MEM_STW_NZ          : fetchSTW_NZ,
1163                 AwlOperator.MEM_STW_POS         : fetchSTW_POS,
1164                 AwlOperator.MEM_STW_NEG         : fetchSTW_NEG,
1165                 AwlOperator.MEM_STW_POSZ        : fetchSTW_POSZ,
1166                 AwlOperator.MEM_STW_NEGZ        : fetchSTW_NEGZ,
1167                 AwlOperator.MEM_STW_UO          : fetchSTW_UO,
1168                 AwlOperator.NAMED_LOCAL         : fetchNAMED_LOCAL,
1169                 AwlOperator.NAMED_LOCAL_PTR     : fetchNAMED_LOCAL_PTR,
1170                 AwlOperator.NAMED_DBVAR         : fetchNAMED_DBVAR,
1171                 AwlOperator.INDIRECT            : fetchINDIRECT,
1172                 AwlOperator.VIRT_ACCU           : fetchVirtACCU,
1173                 AwlOperator.VIRT_AR             : fetchVirtAR,
1174                 AwlOperator.VIRT_DBR            : fetchVirtDBR,
1175         }
1176
1177         def store(self, operator, value, enforceWidth=()): #@nocy
1178 #@cy    cpdef store(self, object operator, object value, tuple enforceWidth=()):
1179                 try:
1180                         storeMethod = self.storeTypeMethods[operator.type]
1181                 except KeyError:
1182                         raise AwlSimError("Invalid store request")
1183                 storeMethod(self, operator, value, enforceWidth)
1184
1185         def __storeWidthError(self, operator, enforceWidth):
1186                 raise AwlSimError("Data store of %d bits, "
1187                         "but only %s bits are allowed." %\
1188                         (operator.width,
1189                          listToHumanStr(enforceWidth)))
1190
1191         def storeE(self, operator, value, enforceWidth):
1192                 if operator.width not in enforceWidth and enforceWidth:
1193                         self.__storeWidthError(operator, enforceWidth)
1194
1195                 self.inputs.store(operator.value, operator.width, value)
1196
1197         def storeA(self, operator, value, enforceWidth):
1198                 if operator.width not in enforceWidth and enforceWidth:
1199                         self.__storeWidthError(operator, enforceWidth)
1200
1201                 self.outputs.store(operator.value, operator.width, value)
1202
1203         def storeM(self, operator, value, enforceWidth):
1204                 if operator.width not in enforceWidth and enforceWidth:
1205                         self.__storeWidthError(operator, enforceWidth)
1206
1207                 self.flags.store(operator.value, operator.width, value)
1208
1209         def storeL(self, operator, value, enforceWidth):
1210                 if operator.width not in enforceWidth and enforceWidth:
1211                         self.__storeWidthError(operator, enforceWidth)
1212
1213                 self.callStackTop.localdata.store(operator.value, operator.width, value)
1214
1215         def storeVL(self, operator, value, enforceWidth):
1216                 if operator.width not in enforceWidth and enforceWidth:
1217                         self.__storeWidthError(operator, enforceWidth)
1218
1219                 try:
1220                         cse = self.callStack[-2]
1221                 except IndexError:
1222                         raise AwlSimError("Store to parent localstack, "
1223                                 "but no parent present.")
1224                 cse.localdata.store(operator.value, operator.width, value)
1225
1226         def storeDB(self, operator, value, enforceWidth):
1227                 if operator.width not in enforceWidth and enforceWidth:
1228                         self.__storeWidthError(operator, enforceWidth)
1229
1230                 if operator.value.dbNumber is None:
1231                         db = self.dbRegister
1232                 else:
1233                         try:
1234                                 db = self.dbs[operator.value.dbNumber]
1235                         except KeyError:
1236                                 raise AwlSimError("Store to DB %d, but DB "
1237                                         "does not exist" % operator.value.dbNumber)
1238                 db.store(operator, value)
1239
1240         def storeDI(self, operator, value, enforceWidth):
1241                 if operator.width not in enforceWidth and enforceWidth:
1242                         self.__storeWidthError(operator, enforceWidth)
1243
1244                 if self.callStackTop.block.isFB:
1245                         # Store the data using the multi-instance base offset from AR2.
1246                         self.diRegister.store(operator, value,
1247                                               AwlOffset.fromPointerValue(self.ar2.get()))
1248                 else:
1249                         # Store without base offset.
1250                         self.diRegister.store(operator, value)
1251
1252         def storePA(self, operator, value, enforceWidth):
1253                 if operator.width not in enforceWidth and enforceWidth:
1254                         self.__storeWidthError(operator, enforceWidth)
1255
1256                 self.outputs.store(operator.value, operator.width, value)
1257                 ok = False
1258                 if self.cbPeripheralWrite:
1259                         ok = self.cbPeripheralWrite(self.cbPeripheralWriteData,
1260                                                     operator.width,
1261                                                     operator.value.byteOffset,
1262                                                     value)
1263                 if not ok:
1264                         raise AwlSimError("There is no hardware to handle "
1265                                 "the direct peripheral store. "
1266                                 "(width=%d, offset=%d, value=0x%X)" %\
1267                                 (operator.width, operator.value.byteOffset,
1268                                  value))
1269
1270         def storeAR2(self, operator, value, enforceWidth):
1271                 if operator.width not in enforceWidth and enforceWidth:
1272                         self.__storeWidthError(operator, enforceWidth)
1273
1274                 self.getAR(2).set(value)
1275
1276         def storeSTW(self, operator, value, enforceWidth):
1277                 if operator.width not in enforceWidth and enforceWidth:
1278                         self.__storeWidthError(operator, enforceWidth)
1279
1280                 if operator.width == 1:
1281                         raise AwlSimError("Cannot store to individual STW bits")
1282                 elif operator.width == 16:
1283                         self.statusWord.setWord(value)
1284                 else:
1285                         assert(0)
1286
1287         def storeNAMED_LOCAL(self, operator, value, enforceWidth):
1288                 # store to an FC interface field.
1289                 # First get the translated rvalue-operand that was used in the call.
1290                 translatedOp = self.callStackTop.interfRefs[operator.interfaceIndex].resolve(True)
1291                 # Add possible sub-offsets (array, struct) to the offset.
1292                 finalOp = translatedOp.dup()
1293                 finalOp.value += operator.value.subOffset
1294                 finalOp.width = operator.width
1295                 self.store(finalOp, value, enforceWidth)
1296
1297         def storeNAMED_DBVAR(self, operator, value, enforceWidth):
1298                 # All legit accesses will have been translated to absolute addressing already
1299                 raise AwlSimError("Fully qualified store to DB variable "
1300                         "is not supported in this place.")
1301
1302         def storeINDIRECT(self, operator, value, enforceWidth):
1303                 self.store(operator.resolve(True), value, enforceWidth)
1304
1305         storeTypeMethods = {
1306                 AwlOperator.MEM_E               : storeE,
1307                 AwlOperator.MEM_A               : storeA,
1308                 AwlOperator.MEM_M               : storeM,
1309                 AwlOperator.MEM_L               : storeL,
1310                 AwlOperator.MEM_VL              : storeVL,
1311                 AwlOperator.MEM_DB              : storeDB,
1312                 AwlOperator.MEM_DI              : storeDI,
1313                 AwlOperator.MEM_PA              : storePA,
1314                 AwlOperator.MEM_AR2             : storeAR2,
1315                 AwlOperator.MEM_STW             : storeSTW,
1316                 AwlOperator.NAMED_LOCAL         : storeNAMED_LOCAL,
1317                 AwlOperator.NAMED_DBVAR         : storeNAMED_DBVAR,
1318                 AwlOperator.INDIRECT            : storeINDIRECT,
1319         }
1320
1321         def __dumpMem(self, prefix, memArray, maxLen):
1322                 if not memArray:
1323                         return prefix + "--"
1324                 ret, line, first, count, i = [], [], True, 0, 0
1325                 while i < maxLen:
1326                         line.append("%02X" % memArray[i])
1327                         count += 1
1328                         if count >= 16:
1329                                 if not first:
1330                                         prefix = ' ' * len(prefix)
1331                                 first = False
1332                                 ret.append(prefix + ' '.join(line))
1333                                 line, count = [], 0
1334                         i += 1
1335                 assert(count == 0)
1336                 return '\n'.join(ret)
1337
1338         def __repr__(self):
1339                 if not self.callStack:
1340                         return ""
1341                 mnemonics = self.specs.getMnemonics()
1342                 isEnglish = (mnemonics == S7CPUSpecs.MNEMONICS_EN)
1343                 self.updateTimestamp()
1344                 ret = []
1345                 ret.append("=== S7-CPU dump ===  (t: %.01fs  py: %d / %s / %s)" %\
1346                            (self.now - self.startupTime,
1347                             3 if isPy3Compat else 2,
1348                             pythonInterpreter,
1349                             "Win" if osIsWindows else ("Posix" if osIsPosix else "unknown")))
1350                 ret.append("    STW:  " + self.statusWord.getString(mnemonics))
1351                 if self.is4accu:
1352                         accus = [ accu.toHex()
1353                                   for accu in (self.accu1, self.accu2,
1354                                                self.accu3, self.accu4) ]
1355                 else:
1356                         accus = [ accu.toHex()
1357                                   for accu in (self.accu1, self.accu2) ]
1358                 ret.append("   Accu:  " + "  ".join(accus))
1359                 ars = [ "%s (%s)" % (ar.toHex(), ar.toPointerString())
1360                         for ar in (self.ar1, self.ar2) ]
1361                 ret.append("     AR:  " + "  ".join(ars))
1362                 ret.append(self.__dumpMem("      M:  ",
1363                                           self.flags,
1364                                           min(64, self.specs.nrFlags)))
1365                 prefix = "      I:  " if isEnglish else "      E:  "
1366                 ret.append(self.__dumpMem(prefix,
1367                                           self.inputs,
1368                                           min(64, self.specs.nrInputs)))
1369                 prefix = "      Q:  " if isEnglish else "      A:  "
1370                 ret.append(self.__dumpMem(prefix,
1371                                           self.outputs,
1372                                           min(64, self.specs.nrOutputs)))
1373                 pstack = str(self.callStackTop.parenStack) if self.callStackTop.parenStack else "Empty"
1374                 ret.append(" PStack:  " + pstack)
1375                 ret.append("     DB:  %s" % str(self.dbRegister))
1376                 ret.append("     DI:  %s" % str(self.diRegister))
1377                 if self.callStack:
1378                         elems = [ str(cse) for cse in self.callStack ]
1379                         elems = " => ".join(elems)
1380                         ret.append("  Calls:  depth:%d   %s" %\
1381                                    (len(self.callStack), elems))
1382                         localdata = self.callStack[-1].localdata
1383                         ret.append(self.__dumpMem("      L:  ",
1384                                                   localdata,
1385                                                   min(16, self.specs.nrLocalbytes)))
1386                         try:
1387                                 localdata = self.callStack[-2].localdata
1388                         except IndexError:
1389                                 localdata = None
1390                         ret.append(self.__dumpMem("     VL:  ",
1391                                                   localdata,
1392                                                   min(16, self.specs.nrLocalbytes)))
1393                 else:
1394                         ret.append("  Calls:  None")
1395                 curInsn = self.getCurrentInsn()
1396                 ret.append("   Stmt:  IP:%s   %s" %\
1397                            (str(self.getCurrentIP()),
1398                             str(curInsn) if curInsn else ""))
1399                 if self.insnPerSecond:
1400                         usPerInsn = "%.03f" % ((1.0 / self.insnPerSecond) * 1000000)
1401                 else:
1402                         usPerInsn = "-/-"
1403                 ret.append("  Speed:  %d stmt/s (= %s us/stmt)  %.01f stmt/cycle" %\
1404                            (int(round(self.insnPerSecond)),
1405                             usPerInsn,
1406                             self.avgInsnPerCycle))
1407                 ret.append(" CycleT:  avg:%.06fs  min:%.06fs  max:%.06fs" %\
1408                            (self.avgCycleTime, self.minCycleTime,
1409                             self.maxCycleTime))
1410                 return '\n'.join(ret)