# -*- coding: utf-8 -*-
#
# AWL simulator - operators
#
# Copyright 2012-2016 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.compat import *

#from awlsim.core.dynattrs cimport * #@cy
#from awlsim.core.statusword cimport * #@cy

from awlsim.core.dynattrs import * #@nocy
from awlsim.core.datatypes import *
from awlsim.core.statusword import * #@nocy
from awlsim.core.lstack import *
from awlsim.core.util import *

from awlsim.awlcompiler import AwlParser


class AwlOperator(DynAttrs):
	"""An AWL operator.
	An operator is an 'argument' to an instruction.
	For example MW 10 in:
	L  MW 10
	"""

	EnumGen.start	# Operator types

	__IMM_START	= EnumGen.item
	IMM		= EnumGen.item	# Immediate value (constant)
	IMM_REAL	= EnumGen.item	# Real
	IMM_S5T		= EnumGen.item	# S5T immediate
	IMM_TIME	= EnumGen.item	# T# immediate
	IMM_DATE	= EnumGen.item	# D# immediate
	IMM_TOD		= EnumGen.item	# TOD# immediate
	IMM_DT		= EnumGen.item	# DT# immediate
	IMM_PTR		= EnumGen.item	# Pointer immediate (P#x.y, P#area x.y, P#DBn.DBX x.y)
	IMM_STR		= EnumGen.item	# STRING immediate ('abc')
	__IMM_END	= EnumGen.item

	MEM_E		= EnumGen.item	# Input
	MEM_A		= EnumGen.item	# Output
	MEM_M		= EnumGen.item	# Flags
	MEM_L		= EnumGen.item	# Localstack
	MEM_VL		= EnumGen.item	# Parent localstack (indirect access)
	MEM_DB		= EnumGen.item	# Global datablock
	MEM_DI		= EnumGen.item	# Instance datablock
	MEM_T		= EnumGen.item	# Timer
	MEM_Z		= EnumGen.item	# Counter
	MEM_PA		= EnumGen.item	# Peripheral output
	MEM_PE		= EnumGen.item	# Peripheral input

	MEM_STW		= EnumGen.item	# Status word bit read
	MEM_STW_Z	= EnumGen.item	# Status word "==0" read
	MEM_STW_NZ	= EnumGen.item	# Status word "<>0" read
	MEM_STW_POS	= EnumGen.item	# Status word ">0" read
	MEM_STW_NEG	= EnumGen.item	# Status word "<0" read
	MEM_STW_POSZ	= EnumGen.item	# Status word ">=0" read
	MEM_STW_NEGZ	= EnumGen.item	# Status word "<=0" read
	MEM_STW_UO	= EnumGen.item	# Status word "UO" read

	MEM_DBLG	= EnumGen.item	# DB-register: DB length
	MEM_DBNO	= EnumGen.item	# DB-register: DB number
	MEM_DILG	= EnumGen.item	# DI-register: DB length
	MEM_DINO	= EnumGen.item	# DI-register: DB number

	MEM_AR2		= EnumGen.item	# AR2 register

	BLKREF_FC	= EnumGen.item	# FC reference
	BLKREF_SFC	= EnumGen.item	# SFC reference
	BLKREF_FB	= EnumGen.item	# FB reference
	BLKREF_SFB	= EnumGen.item	# SFB reference
	BLKREF_UDT	= EnumGen.item	# UDT reference
	BLKREF_DB	= EnumGen.item	# DB reference
	BLKREF_DI	= EnumGen.item	# DI reference
	BLKREF_OB	= EnumGen.item	# OB reference (only symbol table)
	BLKREF_VAT	= EnumGen.item	# VAT reference (only symbol table)
	MULTI_FB	= EnumGen.item	# FB multiinstance reference
	MULTI_SFB	= EnumGen.item	# SFB multiinstance reference

	LBL_REF		= EnumGen.item	# Label reference
	SYMBOLIC	= EnumGen.item	# Classic symbolic reference ("xyz")
	NAMED_LOCAL	= EnumGen.item	# Named local reference (#abc)
	NAMED_LOCAL_PTR	= EnumGen.item	# Pointer to named local (P##abc)
	NAMED_DBVAR	= EnumGen.item	# Named DB variable reference (DBx.VAR)

	INDIRECT	= EnumGen.item	# Indirect access
	UNSPEC		= EnumGen.item	# Not (yet) specified memory region

	# Virtual operators used internally in awlsim, only.
	# These operators do not have standard AWL mnemonics.
	VIRT_ACCU	= EnumGen.item	# Accu
	VIRT_AR		= EnumGen.item	# AR
	VIRT_DBR	= EnumGen.item	# DB and DI registers

	EnumGen.end	# Operator types

	# Type to string map
	type2str = {
		IMM		: "IMMEDIATE",
		IMM_REAL	: "REAL",
		IMM_S5T		: "S5T",
		IMM_TIME	: "TIME",
		IMM_DATE	: "DATE",
		IMM_TOD		: "TOD",
		IMM_DT		: "DT",
		IMM_PTR		: "P#",

		MEM_E		: "E",
		MEM_A		: "A",
		MEM_M		: "M",
		MEM_L		: "L",
		MEM_VL		: "VL",
		MEM_DB		: "DB",
		MEM_DI		: "DI",
		MEM_T		: "T",
		MEM_Z		: "Z",
		MEM_PA		: "PA",
		MEM_PE		: "PE",

		MEM_DBLG	: "DBLG",
		MEM_DBNO	: "DBNO",
		MEM_DILG	: "DILG",
		MEM_DINO	: "DINO",
		MEM_AR2		: "AR2",

		MEM_STW		: "STW",
		MEM_STW_Z	: "==0",
		MEM_STW_NZ	: "<>0",
		MEM_STW_POS	: ">0",
		MEM_STW_NEG	: "<0",
		MEM_STW_POSZ	: ">=0",
		MEM_STW_NEGZ	: "<=0",
		MEM_STW_UO	: "UO",

		LBL_REF		: "LABEL",

		BLKREF_FC	: "BLOCK_FC",
		BLKREF_SFC	: "BLOCK_SFC",
		BLKREF_FB	: "BLOCK_FB",
		BLKREF_SFB	: "BLOCK_SFB",
		BLKREF_UDT	: "BLOCK_UDT",
		BLKREF_DB	: "BLOCK_DB",
		BLKREF_DI	: "BLOCK_DI",
		BLKREF_OB	: "BLOCK_OB",
		BLKREF_VAT	: "BLOCK_VAT",

		INDIRECT	: "__INDIRECT",

		VIRT_ACCU	: "__ACCU",
		VIRT_AR		: "__AR",
		VIRT_DBR	: "__DBR",
	}

	# Dynamic attributes
	dynAttrs = {
		# Extended-operator flag.
		"isExtended"		: False,

		# Possible label index.
		"labelIndex"		: None,

		# Interface index number.
		# May be set by the symbol resolver.
		"interfaceIndex"	: None,

		# Compound data type flag.
		# Set to true for accesses > 32 bit or
		# arrays/structs or array/struct elements.
		"compound"		: False,

		# The access data type (AwlDataType), if known.
		# Only set for resolved symbolic accesses.
		"dataType"		: None,
	}

	def __init__(self, type, width, value, insn=None):
		# type -> The operator type ID number. See "Operator types" above.
		# width -> The bit width of the access.
		# value -> The value. May be an AwlOffset or a string (depends on type).
		# insn -> The instruction this operator is used in. May be None.
		self.type, self.width, self.value, self.insn =\
			type, width, value, insn

	def __eq__(self, other):
		return (self is other) or (\
			isinstance(other, AwlOperator) and\
			self.type == other.type and\
			self.width == other.width and\
			self.value == other.value and\
			super(AwlOperator, self).__eq__(other)\
		)

	def __ne__(self, other):
		return not self.__eq__(other)

	# Make a deep copy, except for "insn".
	def dup(self):
		if isInteger(self.value):
			dupValue = self.value
		else:
			dupValue = self.value.dup()
		oper = AwlOperator(type = self.type,
				   width = self.width,
				   value = dupValue,
				   insn = self.insn)
		oper.setExtended(self.isExtended)
		oper.setLabelIndex(self.labelIndex)
		oper.interfaceIndex = self.interfaceIndex
		return oper

	def setInsn(self, newInsn):
		self.insn = newInsn

	def setExtended(self, isExtended):
		self.isExtended = isExtended

	def setLabelIndex(self, newLabelIndex):
		self.labelIndex = newLabelIndex

	def isImmediate(self):
		return self.type > self.__IMM_START and\
		       self.type < self.__IMM_END

	def _raiseTypeError(self, actualType, expectedTypes):
		expectedTypes = [ self.type2str[t] for t in sorted(expectedTypes) ]
		raise AwlSimError("Invalid operator type. Got %s, but expected %s." %\
			(self.type2str[actualType],
			 listToHumanStr(expectedTypes)),
			insn=self.insn)

	def assertType(self, types, lowerLimit=None, upperLimit=None, widths=None):
		if self.type == AwlOperator.NAMED_LOCAL or\
		   self.type == AwlOperator.NAMED_LOCAL_PTR:
			return #FIXME we should check type for these, too.
		types = toSet(types)
		if not self.type in types:
			self._raiseTypeError(self.type, types)
		if lowerLimit is not None:
			if self.value < lowerLimit:
				raise AwlSimError("Operator value too small",
						  insn=self.insn)
		if upperLimit is not None:
			if self.value > upperLimit:
				raise AwlSimError("Operator value too big",
						  insn=self.insn)
		if widths is not None:
			widths = toSet(widths)
			if not self.width in widths:
				raise AwlSimError("Invalid operator width. "
					"Got %d, but expected %s." %\
					(self.width, listToHumanStr(widths)))

	def checkDataTypeCompat(self, cpu, dataType):
		assert(isinstance(dataType, AwlDataType))

		if self.type in (AwlOperator.NAMED_LOCAL,
				 AwlOperator.NAMED_LOCAL_PTR,
				 AwlOperator.NAMED_DBVAR,
				 AwlOperator.SYMBOLIC):
			# These are checked again after resolve.
			# So don't check them now.
			return

		def mismatch(dataType, oper, operWidth):
			raise AwlSimError("Data type '%s' of width %d bits "
				"is not compatible with operator '%s' "
				"of width %d bits." %\
				(str(dataType), dataType.width, str(oper), operWidth))

		if dataType.type == AwlDataType.TYPE_UDT_X:
			try:
				udt = cpu.udts[dataType.index]
				if udt.struct.getSize() * 8 != self.width:
					raise ValueError
			except (KeyError, ValueError) as e:
				mismatch(dataType, self, self.width)
		elif dataType.type in {AwlDataType.TYPE_POINTER,
				       AwlDataType.TYPE_ANY}:
			if self.type == AwlOperator.IMM_PTR:
				if dataType.type == AwlDataType.TYPE_POINTER and\
				   self.width > 48:
					raise AwlSimError("Invalid immediate pointer "
						"assignment to POINTER type.")
			else:
				if self.isImmediate():
					raise AwlSimError("Invalid immediate "
						"assignment to '%s' type." %\
						str(dataType))
				# Try to make pointer from operator.
				# This will raise AwlSimError on failure.
				self.makePointer()
		elif dataType.type == AwlDataType.TYPE_CHAR:
			if self.type == AwlOperator.IMM_STR:
				if self.width != (2 + 1) * 8:
					raise AwlSimError("String to CHAR parameter "
						"must be only one single character "
						"long.")
			else:
				if self.isImmediate():
					raise AwlSimError("Invalid immediate '%s'"
						"for CHAR data type." %\
						str(self))
				if self.width != dataType.width:
					mismatch(dataType, self, self.width)
		elif dataType.type == AwlDataType.TYPE_STRING:
			if self.type == AwlOperator.IMM_STR:
				if self.width > dataType.width:
					mismatch(dataType, self, self.width)
			else:
				if self.isImmediate():
					raise AwlSimError("Invalid immediate '%s'"
						"for STRING data type." %\
						str(self))
				assert(self.width <= (254 + 2) * 8)
				assert(dataType.width <= (254 + 2) * 8)
				if dataType.width != (254 + 2) * 8:
					if self.width != dataType.width:
						mismatch(dataType, self, self.width)
		else:
			if self.width != dataType.width:
				mismatch(dataType, self, self.width)

	# Resolve this indirect operator to a direct operator.
	def resolve(self, store=True):
		# This already is a direct operator.
		if self.type == self.NAMED_LOCAL:
			# This is a named-local access (#abc).
			# Resolve it to an interface-operator.
			return self.insn.cpu.callStackTop.getInterfIdxOper(self.interfaceIndex).resolve(store)
		return self

	# Make an area-spanning Pointer (32 bit) to this memory area.
	def makePointer(self):
		return Pointer(self.makePointerValue())

	# Make an area-spanning pointer value (32 bit) to this memory area.
	def makePointerValue(self):
		try:
			area = AwlIndirectOp.optype2area[self.type]
		except KeyError as e:
			raise AwlSimError("Could not transform operator '%s' "
				"into a pointer." % str(self))
		return area | self.value.toPointerValue()

	# Make a DBPointer (48 bit) to this memory area.
	def makeDBPointer(self):
		return DBPointer(self.makePointerValue(),
				 self.value.dbNumber)

	# Make an ANY-pointer to this memory area.
	# Returns an ANYPointer().
	def makeANYPointer(self, areaShifted=None):
		ptrValue = self.makePointerValue()
		if areaShifted:
			ptrValue &= ~Pointer.AREA_MASK_S
			ptrValue |= areaShifted
		if ANYPointer.dataTypeIsSupported(self.dataType):
			return ANYPointer.makeByAutoType(dataType = self.dataType,
							 ptrValue = ptrValue,
							 dbNr = self.value.dbNumber)
		return ANYPointer.makeByTypeWidth(bitWidth = self.width,
						  ptrValue = ptrValue,
						  dbNr = self.value.dbNumber)

	def __repr__(self):
		if self.type == self.IMM:
			if self.width == 1:
				return "TRUE" if (self.value & 1) else "FALSE"
			elif self.width == 8:
				return str(byteToSignedPyInt(self.value))
			elif self.width == 16:
				return str(wordToSignedPyInt(self.value))
			elif self.width == 32:
				return "L#" + str(dwordToSignedPyInt(self.value))
		if self.type == self.IMM_REAL:
			return str(dwordToPyFloat(self.value))
		elif self.type == self.IMM_S5T:
			seconds = Timer.s5t_to_seconds(self.value)
			return "S5T#" + AwlDataType.formatTime(seconds)
		elif self.type == self.IMM_TIME:
			return "T#" + AwlDataType.formatTime(self.value / 1000.0)
		elif self.type == self.IMM_DATE:
			return "D#" #TODO
		elif self.type == self.IMM_TOD:
			return "TOD#" #TODO
		elif self.type == self.IMM_PTR:
			return self.value.toPointerString()
		elif self.type == self.IMM_STR:
			strLen = self.value[1]
			return "'" + self.value[2:2+strLen].decode(AwlParser.TEXT_ENCODING) + "'"
		elif self.type in (self.MEM_A, self.MEM_E,
				   self.MEM_M, self.MEM_L, self.MEM_VL):
			pfx = self.type2str[self.type]
			if self.width == 1:
				return "%s %d.%d" %\
					(pfx, self.value.byteOffset, self.value.bitOffset)
			elif self.width == 8:
				return "%sB %d" % (pfx, self.value.byteOffset)
			elif self.width == 16:
				return "%sW %d" % (pfx, self.value.byteOffset)
			elif self.width == 32:
				return "%sD %d" % (pfx, self.value.byteOffset)
			return self.makeANYPointer().toPointerString()
		elif self.type == self.MEM_DB:
			if self.value.dbNumber is None:
				dbPrefix = ""
			else:
				dbPrefix = "DB%d." % self.value.dbNumber
			if self.width == 1:
				return "%sDBX %d.%d" % (dbPrefix,
							self.value.byteOffset,
							self.value.bitOffset)
			elif self.width == 8:
				return "%sDBB %d" % (dbPrefix, self.value.byteOffset)
			elif self.width == 16:
				return "%sDBW %d" % (dbPrefix, self.value.byteOffset)
			elif self.width == 32:
				return "%sDBD %d" % (dbPrefix, self.value.byteOffset)
			return self.makeANYPointer().toPointerString()
		elif self.type == self.MEM_DI:
			if self.width == 1:
				return "DIX %d.%d" % (self.value.byteOffset, self.value.bitOffset)
			elif self.width == 8:
				return "DIB %d" % self.value.byteOffset
			elif self.width == 16:
				return "DIW %d" % self.value.byteOffset
			elif self.width == 32:
				return "DID %d" % self.value.byteOffset
			return self.makeANYPointer().toPointerString()
		elif self.type == self.MEM_T:
			return "T %d" % self.value.byteOffset
		elif self.type == self.MEM_Z:
			return "Z %d" % self.value.byteOffset
		elif self.type == self.MEM_PA:
			if self.width == 8:
				return "PAB %d" % self.value.byteOffset
			elif self.width == 16:
				return "PAW %d" % self.value.byteOffset
			elif self.width == 32:
				return "PAD %d" % self.value.byteOffset
			return self.makeANYPointer().toPointerString()
		elif self.type == self.MEM_PE:
			if self.width == 8:
				return "PEB %d" % self.value.byteOffset
			elif self.width == 16:
				return "PEW %d" % self.value.byteOffset
			elif self.width == 32:
				return "PED %d" % self.value.byteOffset
			return self.makeANYPointer().toPointerString()
		elif self.type == self.MEM_STW:
			return "__STW " + S7StatusWord.nr2name_german[self.value.bitOffset]
		elif self.type == self.LBL_REF:
			return self.value
		elif self.type == self.BLKREF_FC:
			return "FC %d" % self.value.byteOffset
		elif self.type == self.BLKREF_SFC:
			return "SFC %d" % self.value.byteOffset
		elif self.type == self.BLKREF_FB:
			return "FB %d" % self.value.byteOffset
		elif self.type == self.BLKREF_SFB:
			return "SFB %d" % self.value.byteOffset
		elif self.type == self.BLKREF_UDT:
			return "UDT %d" % self.value.byteOffset
		elif self.type == self.BLKREF_DB:
			return "DB %d" % self.value.byteOffset
		elif self.type == self.BLKREF_DI:
			return "DI %d" % self.value.byteOffset
		elif self.type == self.BLKREF_OB:
			return "OB %d" % self.value.byteOffset
		elif self.type == self.BLKREF_VAT:
			return "VAT %d" % self.value.byteOffset
		elif self.type == self.MULTI_FB:
			return "#FB<" + self.makeANYPointer(AwlIndirectOp.AREA_DI).toPointerString() + ">"
		elif self.type == self.MULTI_SFB:
			return "#SFB<" + self.makeANYPointer(AwlIndirectOp.AREA_DI).toPointerString() + ">"
		elif self.type == self.SYMBOLIC:
			return '"%s"' % self.value.identChain.getString()
		elif self.type == self.NAMED_LOCAL:
			return "#" + self.value.identChain.getString()
		elif self.type == self.NAMED_LOCAL_PTR:
			return "P##" + self.value.identChain.getString()
		elif self.type == self.NAMED_DBVAR:
			return str(self.value) # value is AwlOffset
		elif self.type == self.INDIRECT:
			assert(0) # Overloaded in AwlIndirectOp
		elif self.type == self.VIRT_ACCU:
			return "__ACCU %d" % self.value.byteOffset
		elif self.type == self.VIRT_AR:
			return "__AR %d" % self.value.byteOffset
		elif self.type == self.VIRT_DBR:
			return "__DBR %d" % self.value.byteOffset
		elif self.type == self.UNSPEC:
			return "__UNSPEC"
		try:
			return self.type2str[self.type]
		except KeyError:
			assert(0)

class AwlIndirectOp(AwlOperator):
	"Indirect addressing operand"

	# Address register
	AR_NONE		= 0	# No address register
	AR_1		= 1	# Use AR1
	AR_2		= 2	# Use AR2

	# Pointer area constants
	AREA_MASK	= Pointer.AREA_MASK_S
	EXT_AREA_MASK	= Pointer.AREA_MASK_S | 0xFF00000000

	# Pointer area encodings
	AREA_NONE	= 0
	AREA_P		= Pointer.AREA_P << Pointer.AREA_SHIFT
	AREA_E		= Pointer.AREA_E << Pointer.AREA_SHIFT
	AREA_A		= Pointer.AREA_A << Pointer.AREA_SHIFT
	AREA_M		= Pointer.AREA_M << Pointer.AREA_SHIFT
	AREA_DB		= Pointer.AREA_DB << Pointer.AREA_SHIFT
	AREA_DI		= Pointer.AREA_DI << Pointer.AREA_SHIFT
	AREA_L		= Pointer.AREA_L << Pointer.AREA_SHIFT
	AREA_VL		= Pointer.AREA_VL << Pointer.AREA_SHIFT

	# Extended area encodings. Only used for internal purposes.
	# These are not used in the interpreted AWL code.
	EXT_AREA_T		= 0x01FF000000	# Timer
	EXT_AREA_Z		= 0x02FF000000	# Counter
	EXT_AREA_BLKREF_DB	= 0x03FF000000	# DB block reference
	EXT_AREA_BLKREF_DI	= 0x04FF000000	# DI block reference
	EXT_AREA_BLKREF_FB	= 0x05FF000000	# FB block reference
	EXT_AREA_BLKREF_FC	= 0x06FF000000	# FC block reference

	# Map for converting area code to operator type for fetch operations
	area2optype_fetch = {
		AREA_P			: AwlOperator.MEM_PE,
		AREA_E			: AwlOperator.MEM_E,
		AREA_A			: AwlOperator.MEM_A,
		AREA_M			: AwlOperator.MEM_M,
		AREA_DB			: AwlOperator.MEM_DB,
		AREA_DI			: AwlOperator.MEM_DI,
		AREA_L			: AwlOperator.MEM_L,
		AREA_VL			: AwlOperator.MEM_VL,
		EXT_AREA_T		: AwlOperator.MEM_T,
		EXT_AREA_Z		: AwlOperator.MEM_Z,
		EXT_AREA_BLKREF_DB	: AwlOperator.BLKREF_DB,
		EXT_AREA_BLKREF_DI	: AwlOperator.BLKREF_DI,
		EXT_AREA_BLKREF_FB	: AwlOperator.BLKREF_FB,
		EXT_AREA_BLKREF_FC	: AwlOperator.BLKREF_FC,
	}

	# Map for converting area code to operator type for store operations
	area2optype_store = area2optype_fetch.copy()
	area2optype_store[AREA_P] = AwlOperator.MEM_PA

	# Map for converting operator type to area code
	optype2area = pivotDict(area2optype_fetch)
	optype2area[AwlOperator.MEM_PA] = AREA_P
	optype2area[AwlOperator.MULTI_FB] = AREA_DI
	optype2area[AwlOperator.MULTI_SFB] = AREA_DI
	optype2area[AwlOperator.NAMED_DBVAR] = AREA_DB
	optype2area[AwlOperator.UNSPEC] = AREA_NONE

	def __init__(self, area, width, addressRegister, offsetOper, insn=None):
		# area -> The area code for this indirect operation.
		#         AREA_... or EXT_AREA_...
		#         This corresponds to the area code in AWL pointer format.
		# width -> The width (in bits) of the region that is being adressed.
		# addressRegister -> One of:
		#                    AR_NONE => This is a memory-indirect access.
		#                    AR_1 => This is a register-indirect access with AR1.
		#                    AR_2 => This is a register-indirect access with AR2.
		# offsetOper -> This is the AwlOperator for the offset.
		#               For memory-indirect access, this must be an AwlOperator
		#               with "type in __possibleOffsetOperTypes".
		#               For register-indirect access, this must be an AwlOperator
		#               with "type==IMM_PTR".
		# insn -> The instruction this operator is used in. May be None.
		AwlOperator.__init__(self,
				     type = AwlOperator.INDIRECT,
				     width = width,
				     value = None,
				     insn = insn)
		self.area, self.addressRegister, self.offsetOper =\
			area, addressRegister, offsetOper

	# Make a deep copy, except for "insn".
	def dup(self):
		return AwlIndirectOp(area = self.area,
				     width = self.width,
				     addressRegister = self.addressRegister,
				     offsetOper = self.offsetOper.dup(),
				     insn = self.insn)

	def setInsn(self, newInsn):
		AwlOperator.setInsn(self, newInsn)
		self.offsetOper.setInsn(newInsn)

	def assertType(self, types, lowerLimit=None, upperLimit=None):
		types = toSet(types)
		if not self.area2optype_fetch[self.area] in types and\
		   not self.area2optype_store[self.area] in types:
			self._raiseTypeError(self.area2optype_fetch[self.area], types)
		assert(lowerLimit is None)
		assert(upperLimit is None)

	# Possible offset oper types for indirect access
	__possibleOffsetOperTypes = (AwlOperator.MEM_M,
				     AwlOperator.MEM_L,
				     AwlOperator.MEM_DB,
				     AwlOperator.MEM_DI)

	# Resolve this indirect operator to a direct operator.
	def resolve(self, store=True):
		bitwiseDirectOffset = True
		offsetOper = self.offsetOper
		# Construct the pointer
		if self.addressRegister == AwlIndirectOp.AR_NONE:
			# Memory-indirect access
			if self.area == AwlIndirectOp.AREA_NONE:
				raise AwlSimError("Area-spanning access not "
					"possible in indirect access without "
					"address register.")
			if self.area > AwlIndirectOp.AREA_MASK:
				# Is extended area
				possibleWidths = {8, 16, 32}
				bitwiseDirectOffset = False
			else:
				# Is standard area
				possibleWidths = {32,}
			if offsetOper.type not in self.__possibleOffsetOperTypes:
				raise AwlSimError("Offset operator in indirect "
					"access is not a valid memory offset.")
			if offsetOper.width not in possibleWidths:
				raise AwlSimError("Offset operator in indirect "
					"access is not of %s bit width." %\
					listToHumanStr(possibleWidths))
			offsetValue = self.insn.cpu.fetch(offsetOper)
			pointer = (self.area | (offsetValue & 0x0007FFFF))
		else:
			# Register-indirect access
			if offsetOper.type != AwlOperator.IMM_PTR:
				raise AwlSimError("Offset operator in "
					"register-indirect access is not a "
					"pointer immediate.")
			offsetValue = self.insn.cpu.fetch(offsetOper) & 0x0007FFFF
			if self.area == AwlIndirectOp.AREA_NONE:
				# Area-spanning access
				pointer = (self.insn.cpu.getAR(self.addressRegister).get() +\
					   offsetValue) & 0xFFFFFFFF
			else:
				# Area-internal access
				pointer = ((self.insn.cpu.getAR(self.addressRegister).get() +
					    offsetValue) & 0x0007FFFF) |\
					  self.area
		# Create a direct operator
		try:
			if store:
				optype = AwlIndirectOp.area2optype_store[
						pointer & AwlIndirectOp.EXT_AREA_MASK]
			else:
				optype = AwlIndirectOp.area2optype_fetch[
						pointer & AwlIndirectOp.EXT_AREA_MASK]
		except KeyError:
			raise AwlSimError("Invalid area code (%X hex) in indirect addressing" %\
				((pointer & AwlIndirectOp.EXT_AREA_MASK) >>\
				 Pointer.AREA_SHIFT))
		if bitwiseDirectOffset:
			# 'pointer' has pointer format
			directOffset = AwlOffset.fromPointerValue(pointer)
		else:
			# 'pointer' is a byte offset
			directOffset = AwlOffset(pointer & 0x0000FFFF)
		if self.width != 1 and directOffset.bitOffset:
			raise AwlSimError("Bit offset (lowest three bits) in %d-bit "
				"indirect addressing is not zero. "
				"(Computed offset is: %s)" %\
				(self.width, str(directOffset)))
		return AwlOperator(optype, self.width, directOffset, self.insn)

	def __pointerError(self):
		# This is a programming error.
		# The caller should resolve() the operator first.
		raise AwlSimBug("Can not transform indirect operator "
			"into a pointer. Resolve it first.")

	def makePointer(self):
		self.__pointerError()

	def makePointerValue(self):
		self.__pointerError()

	def makeDBPointer(self):
		self.__pointerError()

	def makeANYPointer(self, areaShifted=None):
		self.__pointerError()

	def __repr__(self):
		return "__INDIRECT" #TODO
