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