aboutsummaryrefslogtreecommitdiffstats
path: root/awlsim/core/callstack.py
blob: 6bfadd5ed5dc52c4af1009979f5be9f89ba4ac76 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
# -*- coding: utf-8 -*-
#
# AWL simulator - CPU call stack
#
# Copyright 2012-2018 Michael Buesch <m@bues.ch>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#

from __future__ import division, absolute_import, print_function, unicode_literals
#from awlsim.common.cython_support cimport * #@cy
from awlsim.common.compat import *

from awlsim.common.exceptions import *

from awlsim.core.datatypes import *
from awlsim.core.memory import * #+cimport
from awlsim.core.offset import * #+cimport
from awlsim.core.operatortypes import * #+cimport
from awlsim.core.operators import * #+cimport
from awlsim.core.blocks import * #+cimport
from awlsim.core.blockinterface import *
from awlsim.core.datablocks import * #+cimport
from awlsim.core.parameters import * #+cimport
from awlsim.core.parenstack import * #+cimport
from awlsim.core.util import *


__all__ = [
	"CallStackElem",
	"make_CallStackElem",
]


class CallStackElem(object): #+cdef
	"""Call stack element.
	"""

	__slots__ = (
		"prevCse",		# Previous call stack element (None if at OB level)
		"cpu",			# S7CPU that belongs to this CSE
		"parenStack",		# Active parenthesis stack
		"insns",		# Instruction list that is being executed
		"nrInsns",		# Length of the instruction list
		"ip",			# Current instruction pointer
		"block",		# CodeBlock that is being executed
		"isRawCall",		# True, if this call is raw (UC, CC)
		"instanceDB",		# Instance data block, if any
		"prevDbRegister",	# DB register from before this call
		"prevDiRegister",	# DI register from before this call
		"prevAR2value",		# AR2 register from before this call
		"_outboundParams",	# List of outbound AwlParamAssign. (Internal)
		"_interfRefs",		# List of translated interface references (Internal; FC only)
	)

	# Get an FC interface operand by interface field index.
	def getInterfIdxOper(self, interfaceFieldIndex): #@nocy
#@cy	cpdef AwlOperator getInterfIdxOper(self, uint32_t interfaceFieldIndex):
		if self._interfRefs is None: #+unlikely
			# Huh, no interface ref? We might have been called via raw call.
			raise AwlSimError("The block interface field could not "
				"be found. This probably means that this function block "
				"has been called with a raw call instruction like UC or CC, "
				"but the function block has an interface. This is not "
				"allowed in Awlsim.")
		return self._interfRefs[interfaceFieldIndex]

	# FB parameter translation:
	# Translate FB DB-pointer variable.
	# This is used for FB IN_OUT compound data type parameters.
	# Returns the actual DB-pointer data. (Not an operator!)
	def _FB_trans_dbpointer(self, param, rvalueOp): #@nocy
#@cy	cdef uint64_t _FB_trans_dbpointer(self, AwlParamAssign param, AwlOperator rvalueOp):
#@cy		cdef uint64_t dbPtr
#@cy		cdef int32_t dbNumber

		dbPtr = 0
		dbNumber = rvalueOp.offset.dbNumber
		if dbNumber >= 0:
			dbPtr = dbNumber
			dbPtr <<= 32
		dbPtr |= rvalueOp.makePointerValue()
		return dbPtr

	# FC parameter translation:
	# Don't perform translation.
	# For various MEM and BLKREF accesses.
	# Returns the translated rvalueOp.
	def _FC_trans_direct(self, param, rvalueOp): #@nocy
#@cy	cdef AwlOperator _FC_trans_direct(self, AwlParamAssign param, AwlOperator rvalueOp):
		return rvalueOp

	# FC parameter translation:
	# Copy parameter r-value to the caller-L-stack, if inbound
	# and register a copy-back request, if outbound.
	# Returns the translated rvalueOp.
	def _FC_trans_copyToVL(self, param, rvalueOp): #@nocy
#@cy	cdef AwlOperator _FC_trans_copyToVL(self, AwlParamAssign param, AwlOperator rvalueOp):
#@cy		cdef S7CPU cpu
#@cy		cdef AwlOffset loffset
#@cy		cdef AwlOperator oper
#@cy		cdef uint32_t widthMaskAll

		widthMaskAll = AwlOperatorWidths.WIDTH_MASK_ALL
		cpu = self.cpu

		# Allocate space in the caller-L-stack.
		loffset = cpu.activeLStack.alloc(rvalueOp.width)
		# Make an operator for the allocated space.
		oper = make_AwlOperator(AwlOperatorTypes.MEM_L,
				   rvalueOp.width,
				   loffset,
				   rvalueOp.insn)
		# Write the value to the allocated space.
		# This would only be necessary for inbound parameters,
		# but S7 supports read access to certain outbound
		# FC parameters as well. So copy the value unconditionally.
		cpu.store(oper,
			  cpu.fetch(rvalueOp, widthMaskAll),
			  widthMaskAll)
		# Change the operator to VL
		oper.operType = AwlOperatorTypes.MEM_VL
		# If outbound, save param and operator for return from CALL.
		# Do not do this for immediates (which would be pointer
		# immediates, for example), because there is nothing to copy
		# back in that case.
		if param.isOutbound and not rvalueOp.isImmediate():
			param.scratchSpaceOp = oper
			self._outboundParams.append(param)
		return oper

	# FC parameter translation:
	# Create a DB-pointer to the r-value in the caller's L-stack (VL).
	# Returns the translated rvalueOp.
	def _FC_trans_dbpointerInVL(self, param, rvalueOp): #@nocy
#@cy	cdef AwlOperator _FC_trans_dbpointerInVL(self, AwlParamAssign param, AwlOperator rvalueOp):
#@cy		cdef S7CPU cpu
#@cy		cdef AwlOffset loffset
#@cy		cdef int32_t dbNumber
#@cy		cdef int64_t area
#@cy		cdef uint64_t area_u64
#@cy		cdef AwlOperator storeOper
#@cy		cdef uint32_t widthMaskAll

		widthMaskAll = AwlOperatorWidths.WIDTH_MASK_ALL
		cpu = self.cpu

		# Allocate space for the DB-ptr in the caller-L-stack
		loffset = cpu.activeLStack.alloc(48) # 48 bits
		# Create and store the the DB-ptr to the allocated space.
		storeOper = make_AwlOperator(AwlOperatorTypes.MEM_L,
					16,
					loffset,
					rvalueOp.insn)
		if rvalueOp.operType == AwlOperatorTypes.MEM_DI:
			dbNumber = cpu.diRegister.index
		else:
			dbNumber = rvalueOp.offset.dbNumber
		cpu.store(storeOper,
			  make_AwlMemoryObject_fromScalar16(max(0, dbNumber)),
			  widthMaskAll)
		storeOper.offset = loffset.addInt(2, 0)
		storeOper.width = 32
		area = AwlIndirectOpConst.optype2area(rvalueOp.operType)
		if area < 0: #+unlikely
			raise AwlSimBug("FC_trans_dbpointerInVL: Invalid rValueOp area. "
				"(area=%d, operType=%d)" % (area, rvalueOp.operType))
		area_u64 = area
		if area_u64 == PointerConst.AREA_L_S:
			area_u64 = PointerConst.AREA_VL_S
		elif area_u64 == PointerConst.AREA_VL_S: #+unlikely
			raise AwlSimError("Cannot forward VL-parameter "
					  "to called FC")
		elif area_u64 == PointerConst.AREA_DI_S:
			area_u64 = PointerConst.AREA_DB_S
		cpu.store(storeOper,
			  make_AwlMemoryObject_fromScalar32(area_u64 | rvalueOp.offset.toPointerValue()),
			  widthMaskAll)
		# Return the operator for the DB pointer.
		return make_AwlOperator(AwlOperatorTypes.MEM_VL,
				   48,
				   loffset,
				   rvalueOp.insn)

	# FC parameter translation:
	# Copy the r-value to the caller's L-stack (VL) and also create
	# a DB-pointer to the copied value in VL.
	# Returns the translated rvalueOp.
	def _FC_trans_copyToVLWithDBPtr(self, param, rvalueOp): #@nocy
#@cy	cdef AwlOperator _FC_trans_copyToVLWithDBPtr(self, AwlParamAssign param, AwlOperator rvalueOp):
#@cy		cdef AwlOperator oper

		oper = self._FC_trans_copyToVL(param, rvalueOp)
		oper.operType = AwlOperatorTypes.MEM_L
		return self._FC_trans_dbpointerInVL(param, oper)

	# FC parameter translation:
	# Translate L-stack access r-value.
	# Returns the translated rvalueOp.
	def _FC_trans_MEM_L(self, param, rvalueOp): #@nocy
#@cy	cdef AwlOperator _FC_trans_MEM_L(self, AwlParamAssign param, AwlOperator rvalueOp):
		# r-value is an L-stack memory access.
		if rvalueOp.compound:
			# rvalue is a compound data type.
			# Create a DB-pointer to it in VL.
			return self._FC_trans_dbpointerInVL(param, rvalueOp)
		# Translate it to a VL-stack memory access.
		return make_AwlOperator(AwlOperatorTypes.MEM_VL,
				   rvalueOp.width,
				   rvalueOp.offset,
				   rvalueOp.insn)

	# FC parameter translation:
	# Translate DB access r-value.
	# Returns the translated rvalueOp.
	def _FC_trans_MEM_DB(self, param, rvalueOp, copyToVL=False): #@nocy
#@cy	cdef AwlOperator _FC_trans_MEM_DB(self, AwlParamAssign param, AwlOperator rvalueOp, _Bool copyToVL=False):
#@cy		cdef AwlOffset offset
#@cy		cdef int32_t dbNumber

		# A (fully qualified) DB variable is passed to an FC.
		dbNumber = rvalueOp.offset.dbNumber
		if dbNumber >= 0:
			# This is a fully qualified DB access.
			if rvalueOp.compound:
				# rvalue is a compound data type.
				# Create a DB-pointer to it in VL.
				return self._FC_trans_dbpointerInVL(param, rvalueOp)
			# Basic data type.
			self.cpu.openDB(dbNumber, False)
			copyToVL = True
		if copyToVL:
			# Copy to caller-L-stack.
			return self._FC_trans_copyToVL(param, rvalueOp)
		# Do not copy to caller-L-stack. Just make a DB-reference.
		offset = rvalueOp.offset.dup()
		return make_AwlOperator(AwlOperatorTypes.MEM_DB,
				   rvalueOp.width,
				   offset,
				   rvalueOp.insn)

	# FC parameter translation:
	# Translate DI access r-value.
	# Returns the translated rvalueOp.
	def _FC_trans_MEM_DI(self, param, rvalueOp): #@nocy
#@cy	cdef AwlOperator _FC_trans_MEM_DI(self, AwlParamAssign param, AwlOperator rvalueOp):
		# A parameter is forwarded from an FB to an FC
		if rvalueOp.compound:
			# rvalue is a compound data type.
			# Create a DB-pointer to it in VL.
			return self._FC_trans_dbpointerInVL(param, rvalueOp)
		# Basic data type.
		# Copy the value to VL.
		return self._FC_trans_copyToVL(param, rvalueOp)

	# FC parameter translation:
	# Translate named local variable r-value.
	# Returns the translated rvalueOp.
	def _FC_trans_NAMED_LOCAL(self, param, rvalueOp): #@nocy
#@cy	cdef AwlOperator _FC_trans_NAMED_LOCAL(self, AwlParamAssign param, AwlOperator rvalueOp):
#@cy		cdef AwlOperator oper

		# r-value is a named-local (#abc)
		oper = self.cpu.callStackTop.getInterfIdxOper(rvalueOp.interfaceIndex)
		if oper.operType == AwlOperatorTypes.MEM_DB:
			return self._FC_trans_MEM_DB(param, oper, True)

		# Call the operator translation handler (Python)
		try:							#@nocy
			trans = self._FC_paramTrans[oper.operType]	#@nocy
		except KeyError as e:					#@nocy #@nocov
			self._FCTransBug(param, oper)			#@nocy
		return trans(self, param, oper)				#@nocy

		# Call the operator translation handler (Cython)
#@cy		oper = self._translateFCParam(param, oper)
#@cy		if oper is None:
#@cy			self._FCTransBug(param, oper)
#@cy		return oper

	# FC call parameter translators
	_FC_paramTrans = {							#@nocy
		AwlOperatorTypes.IMM		: _FC_trans_copyToVL,		#@nocy
		AwlOperatorTypes.IMM_REAL	: _FC_trans_copyToVL,		#@nocy
		AwlOperatorTypes.IMM_S5T	: _FC_trans_copyToVL,		#@nocy
		AwlOperatorTypes.IMM_TIME	: _FC_trans_copyToVL,		#@nocy
		AwlOperatorTypes.IMM_DATE	: _FC_trans_copyToVL,		#@nocy
		AwlOperatorTypes.IMM_TOD	: _FC_trans_copyToVL,		#@nocy
		AwlOperatorTypes.IMM_DT		: _FC_trans_copyToVLWithDBPtr,	#@nocy
		AwlOperatorTypes.IMM_PTR	: _FC_trans_copyToVL,		#@nocy
		AwlOperatorTypes.IMM_STR	: _FC_trans_copyToVLWithDBPtr,	#@nocy
		AwlOperatorTypes.MEM_E		: _FC_trans_direct,		#@nocy
		AwlOperatorTypes.MEM_A		: _FC_trans_direct,		#@nocy
		AwlOperatorTypes.MEM_M		: _FC_trans_direct,		#@nocy
		AwlOperatorTypes.MEM_L		: _FC_trans_MEM_L,		#@nocy
		AwlOperatorTypes.MEM_VL		: _FC_trans_copyToVL,		#@nocy
		AwlOperatorTypes.MEM_DB		: _FC_trans_MEM_DB,		#@nocy
		AwlOperatorTypes.MEM_DI		: _FC_trans_MEM_DI,		#@nocy
		AwlOperatorTypes.MEM_T		: _FC_trans_direct,		#@nocy
		AwlOperatorTypes.MEM_Z		: _FC_trans_direct,		#@nocy
		AwlOperatorTypes.MEM_PA		: _FC_trans_direct,		#@nocy
		AwlOperatorTypes.MEM_PE		: _FC_trans_direct,		#@nocy
		AwlOperatorTypes.BLKREF_FC	: _FC_trans_direct,		#@nocy
		AwlOperatorTypes.BLKREF_FB	: _FC_trans_direct,		#@nocy
		AwlOperatorTypes.BLKREF_DB	: _FC_trans_direct,		#@nocy
		AwlOperatorTypes.NAMED_LOCAL	: _FC_trans_NAMED_LOCAL,	#@nocy
	}									#@nocy

#@cy	cdef AwlOperator _translateFCParam(self, AwlParamAssign param, AwlOperator rvalueOp):
#@cy		cdef uint32_t operType
#@cy		cdef AwlOperator oper
#@cy
#@cy		operType = rvalueOp.operType
#@cy		if operType == AwlOperatorTypes.IMM:
#@cy			oper = self._FC_trans_copyToVL(param, rvalueOp)
#@cy		elif operType == AwlOperatorTypes.IMM_REAL:
#@cy			oper = self._FC_trans_copyToVL(param, rvalueOp)
#@cy		elif operType == AwlOperatorTypes.IMM_S5T:
#@cy			oper = self._FC_trans_copyToVL(param, rvalueOp)
#@cy		elif operType == AwlOperatorTypes.IMM_TIME:
#@cy			oper = self._FC_trans_copyToVL(param, rvalueOp)
#@cy		elif operType == AwlOperatorTypes.IMM_DATE:
#@cy			oper = self._FC_trans_copyToVL(param, rvalueOp)
#@cy		elif operType == AwlOperatorTypes.IMM_TOD:
#@cy			oper = self._FC_trans_copyToVL(param, rvalueOp)
#@cy		elif operType == AwlOperatorTypes.IMM_DT:
#@cy			oper = self._FC_trans_copyToVLWithDBPtr(param, rvalueOp)
#@cy		elif operType == AwlOperatorTypes.IMM_PTR:
#@cy			oper = self._FC_trans_copyToVL(param, rvalueOp)
#@cy		elif operType == AwlOperatorTypes.IMM_STR:
#@cy			oper = self._FC_trans_copyToVLWithDBPtr(param, rvalueOp)
#@cy		elif operType == AwlOperatorTypes.MEM_E:
#@cy			oper = self._FC_trans_direct(param, rvalueOp)
#@cy		elif operType == AwlOperatorTypes.MEM_A:
#@cy			oper = self._FC_trans_direct(param, rvalueOp)
#@cy		elif operType == AwlOperatorTypes.MEM_M:
#@cy			oper = self._FC_trans_direct(param, rvalueOp)
#@cy		elif operType == AwlOperatorTypes.MEM_L:
#@cy			oper = self._FC_trans_MEM_L(param, rvalueOp)
#@cy		elif operType == AwlOperatorTypes.MEM_VL:
#@cy			oper = self._FC_trans_copyToVL(param, rvalueOp)
#@cy		elif operType == AwlOperatorTypes.MEM_DB:
#@cy			oper = self._FC_trans_MEM_DB(param, rvalueOp)
#@cy		elif operType == AwlOperatorTypes.MEM_DI:
#@cy			oper = self._FC_trans_MEM_DI(param, rvalueOp)
#@cy		elif operType == AwlOperatorTypes.MEM_T:
#@cy			oper = self._FC_trans_direct(param, rvalueOp)
#@cy		elif operType == AwlOperatorTypes.MEM_Z:
#@cy			oper = self._FC_trans_direct(param, rvalueOp)
#@cy		elif operType == AwlOperatorTypes.MEM_PA:
#@cy			oper = self._FC_trans_direct(param, rvalueOp)
#@cy		elif operType == AwlOperatorTypes.MEM_PE:
#@cy			oper = self._FC_trans_direct(param, rvalueOp)
#@cy		elif operType == AwlOperatorTypes.BLKREF_FC:
#@cy			oper = self._FC_trans_direct(param, rvalueOp)
#@cy		elif operType == AwlOperatorTypes.BLKREF_FB:
#@cy			oper = self._FC_trans_direct(param, rvalueOp)
#@cy		elif operType == AwlOperatorTypes.BLKREF_DB:
#@cy			oper = self._FC_trans_direct(param, rvalueOp)
#@cy		elif operType == AwlOperatorTypes.NAMED_LOCAL:
#@cy			oper = self._FC_trans_NAMED_LOCAL(param, rvalueOp)
#@cy		else:
#@cy			oper = None
#@cy		return oper

	def _FCTransError(self, param, oper): #@nocov
		raise AwlSimError("Do not know how to translate "
			"FC parameter '%s' for call. The specified "
			"actual-parameter is not allowed in this call." % (
			str(param)))

	def _FCTransBug(self, param, oper):
		raise AwlSimBug("Unhandled call translation of "
			"named local parameter assignment:\n"
			"'%s' => r-value operator '%s'" % (
			(str(param), str(oper))))

	# Handle the exit from this code block.
	# This stack element (self) will already have been
	# removed from the CPU's call stack.
	def handleBlockExit(self): #+cdef
#@cy		cdef S7CPU cpu
#@cy		cdef AwlOffset instanceBaseOffset
#@cy		cdef AwlParamAssign param
#@cy		cdef uint32_t widthMaskAll

		cpu = self.cpu

		# Destroy this call stack element.
		cpu.activeLStack.exitStackFrame()

		if not self.isRawCall:
			widthMaskAll = AwlOperatorWidths.WIDTH_MASK_ALL

			# Handle outbound parameters.
			if self.block.isFB:
				# We are returning from an FB.

				# Get the multi-instance base offset.
				instanceBaseOffset = make_AwlOffset_fromPointerValue(cpu.ar2.get())
				# Restore the AR2 register.
				cpu.ar2.set(self.prevAR2value)

				# Transfer data out of DBI.
				structInstance = cpu.diRegister.structInstance
				for param in self._outboundParams:
					cpu.store(
						param.rvalueOp,
						structInstance.getFieldData(param.lValueStructField,
									    instanceBaseOffset),
						widthMaskAll
					)
				# Assign the DB/DI registers.
				cpu.dbRegister, cpu.diRegister = self.instanceDB, self.prevDiRegister
			else:
				# We are returning from an FC.

				# Restore the AR2 register.
				cpu.ar2.set(self.prevAR2value)

				# Transfer data out of temporary sections.
				for param in self._outboundParams:
					cpu.store(
						param.rvalueOp,
						cpu.fetch(make_AwlOperator(AwlOperatorTypes.MEM_L,
									   param.scratchSpaceOp.width,
									   param.scratchSpaceOp.offset,
									   None),
							  widthMaskAll),
						widthMaskAll
					)
				# Assign the DB/DI registers.
				cpu.dbRegister, cpu.diRegister = self.prevDbRegister, self.prevDiRegister

		# Unlink this call stack element from the previous one.
		self.prevCse = None

	def __repr__(self): #@nocov
		return "CallStackElem of %s" % str(self.block)

#
# make_CallStackElem() - Create a CallStackElem instance.
#
# Init the call stack element.
# cpu -> The CPU this runs on.
# block -> The code block that is being called.
# instanceDB -> The instance-DB, if FB-call. Otherwise None.
# instanceBaseOffset -> AwlOffset for use as AR2 instance base (multi-instance).
#                       If None, AR2 is not modified.
# parameters -> A tuple of AwlParamAssign instances
#               representing the parameter assignments in CALL insn.
# isRawCall -> True, if the calling instruction was UC or CC.
#
def make_CallStackElem(cpu,						#@nocy
		       block,						#@nocy
		       instanceDB,					#@nocy
		       instanceBaseOffset,				#@nocy
		       parameters,					#@nocy
		       isRawCall,					#@nocy
		       CallStackElem=CallStackElem):			#@nocy
#cdef CallStackElem make_CallStackElem(S7CPU cpu,			#@cy
#				       CodeBlock block,			#@cy
#				       DB instanceDB,			#@cy
#				       AwlOffset instanceBaseOffset,	#@cy
#				       tuple parameters,		#@cy
#				       _Bool isRawCall):		#@cy
#@cy	cdef CallStackElem cse
#@cy	cdef AwlOperator oper
#@cy	cdef AwlParamAssign param
#@cy	cdef AwlStructField structField
#@cy	cdef AwlStructInstance structInstance
#@cy	cdef uint32_t widthMaskAll
#@cy	cdef AwlMemoryObject memObj

	cse = CallStackElem()

	cse.cpu = cpu
	cse.parenStack = make_ParenStack(cpu)
	cse.ip = 0
	cse.block = block
	cse.insns = block.insns
	cse.nrInsns = block.nrInsns
	cse.isRawCall = isRawCall
	cse.instanceDB = instanceDB
	cse.prevDbRegister = cpu.dbRegister
	cse.prevDiRegister = cpu.diRegister
	cse.prevCse = cpu.callStackTop

	# Handle parameters
	cse._outboundParams = []
	if parameters and not isRawCall: #@nocy
#@cy	if not isRawCall:
		if block.isFB:
			structInstance = instanceDB.structInstance
			widthMaskAll = AwlOperatorWidths.WIDTH_MASK_ALL
			# This is a call to an FB.
			# Copy the inbound data into the instance DB
			# and add the outbound parameters to the list.
			for param in parameters:
				structField = param.lValueStructField
				if param.isOutbound:
					# This is an outbound parameter.
					# If it is not IN_OUT compound data type,
					# add it to the outbound parameter list
					# for use at BE time.
					if not param.isInbound or\
					   not structField.compound:
						cse._outboundParams.append(param)
				if param.isInbound:
					# This is an inbound parameter.
					# Check if this is an IN_OUT compound data
					# type variable. These are passed via DB-ptr.
					if param.isOutbound and\
					   structField.compound:
						# Compound data type with IN_OUT decl.
						# Make a DB-ptr to the actual data.
						memObj = make_AwlMemoryObject_fromScalar48(
							cse._FB_trans_dbpointer(param, param.rvalueOp))
						# Get the DB-ptr struct field.
						structField = structField.finalOverride
					else:
						# Non-compound (basic) data type or
						# not IN_OUT declaration.
						# Get the actual data.
						if structField.callByRef:
							# Do not fetch. Type is passed 'by reference'.
							# This is for TIMER, COUNTER, etc...
							memObj = make_AwlMemoryObject_fromScalar16(
								param.rvalueOp.resolve(True).offset.byteOffset)
						else:
							memObj = cpu.fetch(param.rvalueOp, widthMaskAll)
					# Transfer data into DBI.
					structInstance.setFieldData(structField,
								    memObj,
								    instanceBaseOffset)
			cse._interfRefs = None
		else:
			# This is a call to an FC.
			# Prepare the interface (IN/OUT/INOUT) references.
			# cse._interfRefs is a dict of AwlOperators for the FC interface.
			#                   The key of cse._interfRefs is the interface field index.
			#                   This dict is used by the CPU for lookup and resolve of
			#                   the FC interface r-value.
			cse._interfRefs = {}
			for param in parameters:
				oper = param.rvalueOp

				# Call the operator translation handler (Python)
				try:							#@nocy
					trans = cse._FC_paramTrans[oper.operType]	#@nocy
				except KeyError as e:					#@nocy #@nocov
					cse._FCTransError(param, oper)			#@nocy
				cse._interfRefs[param.interfaceFieldIndex] = trans(	#@nocy
						cse, param, oper)			#@nocy

				# Call the operator translation handler (Cython)
#@cy				oper = cse._translateFCParam(param, oper)
#@cy				if oper is None:
#@cy					cse._FCTransError(param, oper)
#@cy				cse._interfRefs[param.interfaceFieldIndex] = oper
	else:
		cse._interfRefs = None

	# Prepare the localdata stack.
	cpu.activeLStack.enterStackFrame()
	if block.tempAllocation:
		cpu.activeLStack.alloc(block.tempAllocation * 8)

	# Set AR2 to the specified multi-instance base
	# and save the old AR2 value.
	cse.prevAR2value = cpu.ar2.get()
	if instanceBaseOffset is not None:
		cpu.ar2.set(PointerConst.AREA_DB_S |\
			    instanceBaseOffset.toPointerValue())

	return cse
bues.ch cgit interface