/* code62015.c */
/*****************************************************************************/
/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only                     */
/*                                                                           */
/* AS-Portierung                                                             */
/*                                                                           */
/* Codegenerator Sharp SC62015                                               */
/*                                                                           */
/*****************************************************************************/

#include "stdinc.h"
#include <ctype.h>
#include <string.h>

#include "nls.h"
#include "strutil.h"
#include "chunks.h"
#include "asmdef.h"
#include "asmsub.h"
#include "asmpars.h"
#include "asmallg.h"
#include "onoff_common.h"
#include "asmitree.h"
#include "codevars.h"
#include "codepseudo.h"
#include "headids.h"
#include "errmsg.h"
#include "chartrans.h"
#include "codepseudo.h"
#include "intpseudo.h"

#include "code62015.h"

/*---------------------------------------------------------------------------*/

static CPUVar CPUSC62015;

/*---------------------------------------------------------------------------*/

#define RegA  0
#define RegIL 1
#define RegBA 2
#define RegI  3
#define RegX  4
#define RegY  5
#define RegU  6
#define RegS  7
#define RegB  8
#define RegIH 9
#define RegPS 10
#define RegPC 11
#define RegF  12
#define RegIMR 13

#define IsR1(reg) ((reg) == RegA || (reg) == RegIL)
#define IsR2(reg) ((reg) == RegBA || (reg) == RegI)
#define IsR3(reg) ((reg) >= RegX && (reg) <= RegS)
#define IsR4(reg) ((reg) >= RegX && (reg) <= RegU)

#define IsWP(index) ((index) == 0 || (index) == 1)
#define IsWPL(index) ((index) == 0 || (index) == 1 || (index) == 2)

typedef struct
{
	const char name[4];
	Byte addr;
} tIntReg;

static const tIntReg IntRegs[] =
{
	{ "BP" , 0xec },
	{ "PX" , 0xed },
	{ "PY" , 0xee },
	{ "AMC", 0xef },
	{ "KOL", 0xf0 },
	{ "KOH", 0xf1 },
	{ "KI" , 0xf2 },
	{ "EOL", 0xf3 },
	{ "EOH", 0xf4 },
	{ "EIL", 0xf5 },
	{ "EIH", 0xf6 },
	{ "UCR", 0xf7 },
	{ "USR", 0xf8 },
	{ "RXD", 0xf9 },
	{ "TXD", 0xfa },
	{ "IMR", 0xfb },
	{ "ISR", 0xfc },
	{ "SCR", 0xfd },
	{ "LCC", 0xfe },
	{ "SSR", 0xff },
};

static Boolean DecodeReg(const tStrComp *arg, Byte *pReg)
{
	const char rtable[][4] = { "A", "IL", "BA", "I", "X", "Y", "U", "S", "B", "IH", "PS", "PC", "F", "IMR" };
	const char *str = arg->str.p_str;
	size_t i;

	for (i = 0; i < as_array_size(rtable); i++)
	{
		if (as_strcasecmp(str, rtable[i]) == 0)
		{
			*pReg = i;
			return True;
		}
	}

	return False;
}

static int RegSize(Byte Reg)
{
	int size;

	switch (Reg)
	{
	case RegPS:
		size = 0;
		break;
	case 0:	/* A */
	case 1:	/* IL */
	case 8:	/* B */
	case 9:	/* IH */
		size = 1;
		break;
	case 2:	/* BA */
	case 3:	/* I */
	case 11:	/* PC */
		size = 2;
		break;
	default:
		size = 3;
	}

	return size;
}

static Boolean IsCoreReg(Byte reg)
{
	return (reg < 8) ? True : False;
}

static Boolean DecodeInternal(const tStrComp *pArg, Byte *pMode, Byte *pDisp, const char *pPReg)
{
	size_t i;
	String Temp;
	tStrComp temp;
	Byte mode = 0;
	Boolean OK;
	int val;
	int sign = 1;
	tSymbolFlags flags;

	for (i = 0; i < as_array_size(IntRegs); i++)
	{
		if (!as_strcasecmp(pArg->str.p_str, IntRegs[i].name))
		{
			*pMode = 0;
			*pDisp = IntRegs[i].addr;
			return True;
		}
	}

	if (!IsIndirect(pArg->str.p_str)) return False;

#if 0
	StrCompRefRight(&temp, pArg, 1);	/* strip off left '(' */
#endif
	StrCompMkTemp(&temp, Temp, sizeof(Temp));
	StrCompCopy(&temp, pArg);
	StrCompCutLeft(&temp, 1);
	StrCompShorten(&temp, 1);	/* strip off right ')' */
	KillPrefBlanksStrComp(&temp);
	KillPostBlanksStrComp(&temp);

	if (!as_strncasecmp(temp.str.p_str, "BP", 2))
	{
		mode = 1;
	}
	else if (!as_strncasecmp(temp.str.p_str, pPReg, 2))
	{
		mode = 2;
	}
	else
	{
		val = EvalStrIntExpression(&temp, UInt8, &OK);
		if (OK)
		{
			*pMode = 0;
			*pDisp = val;
			return OK;
		}

		return False;
	}

	StrCompCutLeft(&temp, 2);
	KillPrefBlanksStrComp(&temp);

	if (temp.Pos.Len == 0)
	{
		/* Displacement omitted */
		*pMode = mode;
		*pDisp = 0;
		return True;
	}

	if (temp.str.p_str[0] == '+')
	{
		sign = 1;
		StrCompCutLeft(&temp, 1);
	}
	else if (temp.str.p_str[0] == '-')
	{
		sign = -1;
		StrCompCutLeft(&temp, 1);
	}
	KillPrefBlanksStrComp(&temp);

	if (mode == 1 && !as_strcasecmp(temp.str.p_str, pPReg))
	{
		*pMode = 3;
		*pDisp = 0;
		return True;
	}

	val = EvalStrIntExpressionWithFlags(&temp, UInt8, &OK, &flags) * sign;
	if (OK)
	{
		if (!mFirstPassUnknownOrQuestionable(flags) && (val > 127 || val < -128))
		{
			WrStrErrorPos(ErrNum_DistTooBig, pArg);
			return False;
		}
		*pMode = mode;
		*pDisp = val;
		return True;
	}

	return False;
}

static void PreByte(Byte mode1, Byte mode2)
{
	static const Byte ptable[] = {
		0x32, 0x30, 0x33, 0x31,
		0x22, 0x00, 0x23, 0x21,
		0x36, 0x34, 0x37, 0x35,
		0x26, 0x24, 0x27, 0x25 };

	if (mode1 == 1 && mode2 == 1) return;

	if (mode1 > 3 || mode2 > 3)
	{
		WrError(ErrNum_InternalError);
		return;
	}

	BAsmCode[CodeLen++] = ptable[mode1 * 4 + mode2];
}

static Boolean DecodeDirect(const tStrComp *pArg, LongInt *pAddr)
{
	String Temp;
	tStrComp temp;
	LongInt val;
	Boolean OK;

	if (!IsIndirectGen(pArg->str.p_str, "[]")) return False;

	StrCompMkTemp(&temp, Temp, sizeof(Temp));
	StrCompCopy(&temp, pArg);
	StrCompCutLeft(&temp, 1);
	StrCompShorten(&temp, 1);
	KillPrefBlanksStrComp(&temp);
	KillPostBlanksStrComp(&temp);

	val = EvalStrIntExpression(&temp, UInt20, &OK);
	if (OK)
	{
		*pAddr = val;
		return OK;
	}

	return False;
}

static Boolean DecodeRegIndirect(const tStrComp *pArg, Byte *pReg, Byte *pMode)
{
	String Temp;
	tStrComp temp;
	Byte reg;
	int l;
	Byte mode = 0x00;

	if (!IsIndirectGen(pArg->str.p_str, "[]")) return False;
	StrCompMkTemp(&temp, Temp, sizeof(Temp));
	StrCompCopy(&temp, pArg);
	StrCompCutLeft(&temp, 1);
	StrCompShorten(&temp, 1);
	KillPrefBlanksStrComp(&temp);
	KillPostBlanksStrComp(&temp);

	l = strlen(temp.str.p_str);
	if ((l >= 2) && !strncmp(temp.str.p_str + l - 2, "++", 2))
	{
		mode = 0x20;
		StrCompShorten(&temp, 2);
	}
	else if (!strncmp(temp.str.p_str, "--", 2))
	{
		mode = 0x30;
		StrCompCutLeft(&temp, 2);
	}

	if (DecodeReg(&temp, &reg))
	{
		*pReg = reg;
		*pMode = mode;
		return True;
	}

	return False;
}

static Boolean DecodeRegBase(const tStrComp *pArg, Byte *pReg, Byte *pMode, Byte *pDisp)
{
	String Temp;
	tStrComp temp;
	String Temp2;
	tStrComp temp2;
	int i;
	char ch;
	Byte reg;
	Byte mode;
	Word val;
	Boolean OK;

	if (!IsIndirectGen(pArg->str.p_str, "[]")) return False;
	StrCompMkTemp(&temp, Temp, sizeof(Temp));
	StrCompCopy(&temp, pArg);
	StrCompCutLeft(&temp, 1);
	StrCompShorten(&temp, 1);
	KillPrefBlanksStrComp(&temp);
	KillPostBlanksStrComp(&temp);

	for (i = 0; temp.str.p_str[i]; i++)
	{
		ch = temp.str.p_str[i];
		if (isalpha(ch)) continue;
		if (isspace(ch) || ch == '+' || ch == '-') break;
		return False;
	}

	if (i <= 2)
	{
		StrCompMkTemp(&temp2, Temp2, sizeof(Temp2));
		StrCompCopy(&temp2, &temp);
		StrCompShorten(&temp, strlen(temp.str.p_str) - i);
		StrCompCutLeft(&temp2, i);
		KillPrefBlanksStrComp(&temp2);

		if (!DecodeReg(&temp, &reg) || !IsR3(reg)) return False;

		if (temp2.str.p_str[0] == '+')
		{
			mode = 0x80;
		}
		else if (temp2.str.p_str[0] == '-')
		{
			mode = 0xc0;
		}
		else return False;

		StrCompCutLeft(&temp2, i);
		KillPrefBlanksStrComp(&temp2);

		val = EvalStrIntExpression(&temp2, UInt8, &OK);
		if (!OK) return False;

		*pReg = reg;
		*pMode = mode;
		*pDisp = val;
		return True;
	}

	return False;
}

static Boolean DecodeImm(const tStrComp *pArg, LongInt *pVal, int size)
{
	LongInt val;
	Boolean OK;

	switch (size)
	{
	case 1:	/* 8 bit */
		val = EvalStrIntExpression(pArg, Int8, &OK);
		break;
	case 2:	/* 16 bit */
		val = EvalStrIntExpression(pArg, Int16, &OK);
		break;
	case 3:	/* 20 bit */
		val = EvalStrIntExpression(pArg, Int20, &OK);
		break;
	default:
		WrError(ErrNum_InternalError);
		return False;
	}

	if (OK)
	{
		*pVal = val;
		return True;
	}

	return False;
}

static Boolean DecodeIntIndirect(const tStrComp *pArg, Byte *pDir, Byte *pDisp2, Byte *pMode, Byte *pDisp, const char *pPReg)
{
	String Temp;
	String Temp2;
	tStrComp temp;
	tStrComp temp2;
	char *pEnd;
	int pos;
	Byte dir;
	Byte mode;
	Byte disp;
	Byte disp2;
	Boolean OK;

	if (!IsIndirectGen(pArg->str.p_str, "[]")) return False;
	StrCompMkTemp(&temp, Temp, sizeof(Temp));
	StrCompCopy(&temp, pArg);
	StrCompCutLeft(&temp, 1);
	StrCompShorten(&temp, 1);
	KillPrefBlanksStrComp(&temp);
	KillPostBlanksStrComp(&temp);

	if (temp.str.p_str[0] != '(') return False;
	pEnd = FindClosingParenthese(temp.str.p_str + 1);
	pos = pEnd - temp.str.p_str + 1;
	if (!pEnd) return False;

	StrCompMkTemp(&temp2, Temp2, sizeof(Temp2));
	StrCompCopySub(&temp2, &temp, pos, strlen(temp.str.p_str) - pos);
	StrCompShorten(&temp, strlen(temp.str.p_str) - pos);
	KillPrefBlanksStrComp(&temp2);

	if (!DecodeInternal(&temp, &mode, &disp, pPReg)) return False;

	dir = 0x00;
	if (strlen(temp2.str.p_str) > 0)
	{
		switch (temp2.str.p_str[0])
		{
		case '+':
			dir = 0x80;
			break;
		case '-':
			dir = 0xc0;
			break;
		default:
			return False;
		}

		StrCompCutLeft(&temp2, 1);
		disp2 = EvalStrIntExpression(&temp2, UInt8, &OK);
		if (!OK) return False;
	}
  else
    disp2 = 0;

	*pDir = dir;
	*pMode = mode;
	*pDisp2 = disp2;
	*pDisp = disp;
	return True;
}

static void AddrOut20(LongInt addr)
{
	BAsmCode[CodeLen++] = addr;
	BAsmCode[CodeLen++] = addr >> 8;
	BAsmCode[CodeLen++] = addr >> 16;
}

/*---------------------------------------------------------------------------*/

static void DecodeMV(Word Index)
{
	int size = Index;
	int i;
	Byte reg1;
	Byte reg2;
	Byte mode1;
	Byte mode2;
	Byte disp1;
	Byte disp2;
	Byte dir;
	Byte mdisp;
	LongInt addr;
	LongInt val;

	if (ChkArgCnt(2, 2))
	{
		if (DecodeReg(&ArgStr[1], &reg1))
		{
			if (!size) size = RegSize(reg1);

			if (DecodeReg(&ArgStr[2], &reg2))
			{
				if (size == 1)
				{
					if (reg1 == RegA && reg2 == RegB)
					{
						BAsmCode[0] = 0x74;
						CodeLen = 1;
						return;
					}
					if (reg1 == RegB && reg2 == RegA)
					{
						BAsmCode[0] = 0x75;
						CodeLen = 1;
						return;
					}
				}

				if (!Index && reg1 < 8 && reg2 < 8 && RegSize(reg1) > 1)
				{
					if (RegSize(reg1) != RegSize(reg2))
					{
						WrStrErrorPos(ErrNum_InvReg, &ArgStr[2]);	/* Register size conflict */
						return;
					}

					BAsmCode[0] = 0xFD;
					BAsmCode[1] = (reg1 << 4) | reg2;
					CodeLen = 2;
					return;
				}

				WrStrErrorPos(ErrNum_InvReg, &ArgStr[2]);
				return;
			}

			if (DecodeInternal(&ArgStr[2], &mode2, &disp2, "PX"))
			{
				PreByte(mode2, 1);
				BAsmCode[CodeLen++] = 0x80 | reg1;
				BAsmCode[CodeLen++] = disp2;
				return;
			}

			if (reg1 != RegS && DecodeRegIndirect(&ArgStr[2], &reg2, &mode2))
			{
				BAsmCode[0] = 0x90 | reg1;
				BAsmCode[1] = mode2 | reg2;
				CodeLen = 2;
				return;
			}

			if (DecodeRegBase(&ArgStr[2], &reg2, &mode2, &disp2))
			{
				BAsmCode[0] = 0x90 | reg1;
				BAsmCode[1] = mode2 | reg2;
				BAsmCode[2] = disp2;
				CodeLen = 3;
				return;
			}

			if (DecodeIntIndirect(&ArgStr[2], &dir, &mdisp, &mode2, &disp2, "PX"))
			{
				PreByte(mode2, 1);
				BAsmCode[CodeLen++] = 0x98 | reg1;
				BAsmCode[CodeLen++] = dir;
				BAsmCode[CodeLen++] = disp2;
				if (dir) BAsmCode[CodeLen++] = mdisp;
				return;
			}

			if (DecodeDirect(&ArgStr[2], &addr))
			{
				BAsmCode[CodeLen++] = 0x88 | reg1;
				AddrOut20(addr);
				return;
			}

			if (DecodeImm(&ArgStr[2], &val, size))
			{
				BAsmCode[CodeLen++] = 0x08 | reg1;
				for (i = 0; i < size; i++)
				{
					BAsmCode[CodeLen++] = val >> (i * 8);
				}
				return;
			}

			WrStrErrorPos(ErrNum_InvArg, &ArgStr[2]);
			return;
		}

		if (DecodeInternal(&ArgStr[1], &mode1, &disp1, "PX"))
		{
			if (DecodeReg(&ArgStr[2], &reg2) && IsCoreReg(reg2))
			{
				PreByte(mode1, 1);
				BAsmCode[CodeLen++] = 0xa0 | reg2;
				BAsmCode[CodeLen++] = disp1;
				return;
			}

			if (DecodeInternal(&ArgStr[2], &mode2, &disp2, "PY"))
			{
				PreByte(mode1, mode2);
				BAsmCode[CodeLen++] = 0xc8;
				BAsmCode[CodeLen++] = disp1;
				BAsmCode[CodeLen++] = disp2;
				return;
			}

			if (DecodeRegIndirect(&ArgStr[2], &reg2, &mode2))
			{
				PreByte(mode1, 1);
				BAsmCode[CodeLen++] = 0xe0;
				BAsmCode[CodeLen++] = mode2 | reg2;
				BAsmCode[CodeLen++] = disp1;
				return;
			}

			if (DecodeRegBase(&ArgStr[2], &reg2, &mode2, &disp2))
			{
				PreByte(mode1, 1);
				BAsmCode[CodeLen++] = 0xe0;
				BAsmCode[CodeLen++] = mode2 | reg2;
				BAsmCode[CodeLen++] = disp1;
				BAsmCode[CodeLen++] = disp2;
				return;
			}

			if (DecodeIntIndirect(&ArgStr[2], &dir, &mdisp, &mode2, &disp2, "PY"))
			{
				PreByte(mode1, mode2);
				BAsmCode[CodeLen++] = 0xf0;
				BAsmCode[CodeLen++] = dir;
				BAsmCode[CodeLen++] = disp1;
				BAsmCode[CodeLen++] = disp2;
				if (dir) BAsmCode[CodeLen++] = mdisp;
				return;
			}

			if (DecodeDirect(&ArgStr[2], &addr))
			{
				PreByte(mode1, 1);
				BAsmCode[CodeLen++] = 0xd0;
				BAsmCode[CodeLen++] = disp1;
				AddrOut20(addr);
				return;
			}

			if (DecodeImm(&ArgStr[2], &val, 1))
			{
				PreByte(mode1, 1);
				BAsmCode[CodeLen++] = 0xcc;
				BAsmCode[CodeLen++] = disp1;
				BAsmCode[CodeLen++] = val;
				return;
			}

			WrStrErrorPos(ErrNum_InvArg, &ArgStr[2]);
			return;
		}

		if (DecodeRegIndirect(&ArgStr[1], &reg1, &mode1))
		{
			if (DecodeReg(&ArgStr[2], &reg2) && reg2 != RegS)
			{
				BAsmCode[0] = 0xb0 | reg2;
				BAsmCode[1] = mode1 | reg1;
				CodeLen = 2;
				return;
			}

			if (DecodeInternal(&ArgStr[2], &mode2, &disp2, "PX"))
			{
				PreByte(mode2, 1);
				BAsmCode[CodeLen++] = 0xe8;
				BAsmCode[CodeLen++] = mode1 | reg1;
				BAsmCode[CodeLen++] = disp2;
				return;
			}

			WrStrErrorPos(ErrNum_InvArg, &ArgStr[2]);
			return;
		}

		if (DecodeRegBase(&ArgStr[1], &reg1, &mode1, &disp1))
		{
			if (DecodeReg(&ArgStr[2], &reg2))
			{
				BAsmCode[0] = 0xb0 | reg2;
				BAsmCode[1] = mode1 | reg1;
				BAsmCode[2] = disp1;
				CodeLen = 3;
				return;
			}

			if (DecodeInternal(&ArgStr[2], &mode2, &disp2, "PX"))
			{
				PreByte(mode2, 1);
				BAsmCode[CodeLen++] = 0xe8;
				BAsmCode[CodeLen++] = mode1 | reg1;
				BAsmCode[CodeLen++] = disp2;
				BAsmCode[CodeLen++] = disp1;
				return;				
			}

			WrStrErrorPos(ErrNum_InvArg, &ArgStr[2]);
			return;
		}

		if (DecodeIntIndirect(&ArgStr[1], &dir, &mdisp, &mode1, &disp1, "PX"))
		{
			if (DecodeReg(&ArgStr[2], &reg2))
			{
				PreByte(mode1, 1);
				BAsmCode[CodeLen++] = 0xb8 | reg2;
				BAsmCode[CodeLen++] = dir;
				BAsmCode[CodeLen++] = disp1;
				if (dir) BAsmCode[CodeLen++] = mdisp;
				return;
			}

			if (DecodeInternal(&ArgStr[2], &mode2, &disp2, "PY"))
			{
				PreByte(mode1, mode2);
				BAsmCode[CodeLen++] = 0xf8;
				BAsmCode[CodeLen++] = dir;
				BAsmCode[CodeLen++] = disp1;
				if (dir) BAsmCode[CodeLen++] = mdisp;
				BAsmCode[CodeLen++] = disp2;
				return;
			}

			WrStrErrorPos(ErrNum_InvArg, &ArgStr[2]);
			return;
		}

		if (DecodeDirect(&ArgStr[1], &addr))
		{
			if (DecodeReg(&ArgStr[2], &reg2) && IsCoreReg(reg2))
			{
				BAsmCode[CodeLen++] = 0xa8 | reg2;
				AddrOut20(addr);
				return;
			}

			if (DecodeInternal(&ArgStr[2], &mode2, &disp2, "PX"))
			{
				PreByte(mode2, 1);
				BAsmCode[CodeLen++] = 0xd8;
				AddrOut20(addr);
				BAsmCode[CodeLen++] = disp2;
				return;
			}

			WrStrErrorPos(ErrNum_InvArg, &ArgStr[2]);
			return;
		}

		WrStrErrorPos(ErrNum_InvArg, &ArgStr[1]);
		return;
	}
}

static void DecodeMVW(Word Index)
{
	Byte mode1;
	Byte mode2;
	Byte disp1;
	Byte disp2;
	Byte reg;
	Byte dir;
	Byte mdisp;
	LongInt val;
	LongInt addr;
	int i;

	if (ChkArgCnt(2, 2))
	{
		if (DecodeInternal(&ArgStr[1], &mode1, &disp1, "PX"))
		{
			if (DecodeInternal(&ArgStr[2], &mode2, &disp2, "PY"))
			{
				PreByte(mode1, mode2);
				BAsmCode[CodeLen++] = (Index == 3) ? 0xcf : 0xc9 + Index;
				BAsmCode[CodeLen++] = disp1;
				BAsmCode[CodeLen++] = disp2;
				return;
			}

			if (IsWPL(Index) &&	DecodeRegIndirect(&ArgStr[2], &reg, &mode2))
			{
				PreByte(mode1, 1);
				BAsmCode[CodeLen++] = 0xe1 + Index;
				BAsmCode[CodeLen++] = mode2 | reg;
				BAsmCode[CodeLen++] = disp1;
				return;
			}

			if (IsWPL(Index) &&	DecodeRegBase(&ArgStr[2], &reg, &mode2, &disp2))
			{
				const Byte optab[] = { 0xe1, 0xe2, 0x56, 0x00 };

				PreByte(mode1, 1);
				BAsmCode[CodeLen++] = optab[Index];
				BAsmCode[CodeLen++] = mode2 | reg;
				BAsmCode[CodeLen++] = disp1;
				BAsmCode[CodeLen++] = disp2;
				return;
			}

			if (IsWPL(Index) &&	DecodeIntIndirect(&ArgStr[2], &dir, &mdisp, &mode2, &disp2, "PY"))
			{
				PreByte(mode1, mode2);
				BAsmCode[CodeLen++] = 0xf1 + Index;
				BAsmCode[CodeLen++] = dir;
				BAsmCode[CodeLen++] = disp1;
				BAsmCode[CodeLen++] = disp2;
				if (dir) BAsmCode[CodeLen++] = mdisp;
				return;
			}

			if (IsWPL(Index) &&	DecodeDirect(&ArgStr[2], &addr))
			{
				PreByte(mode1, 1);
				BAsmCode[CodeLen++] = 0xd1 + Index;
				BAsmCode[CodeLen++] = disp1;
				AddrOut20(addr);
				return;
			}

			if (IsWP(Index) && DecodeImm(&ArgStr[2], &val, Index + 2))
			{
				PreByte(mode1, 1);
				BAsmCode[CodeLen++] = (Index == 0) ? 0xcd : 0xdc;
				BAsmCode[CodeLen++] = disp1;
				for (i = 0; i < Index + 2; i++)
				{
					BAsmCode[CodeLen++] = val >> (i * 8);
				}
				return;
			}

			WrStrErrorPos(ErrNum_InvArg, &ArgStr[2]);
			return;
		}

		if (DecodeRegIndirect(&ArgStr[1], &reg, &mode1))
		{
			if (IsWPL(Index) &&	DecodeInternal(&ArgStr[2], &mode2, &disp2, "PX"))
			{
				PreByte(mode2, 1);
				BAsmCode[CodeLen++] = 0xe9 + Index;
				BAsmCode[CodeLen++] = mode1 | reg;
				BAsmCode[CodeLen++] = disp2;
				return;
			}

			WrStrErrorPos(ErrNum_InvArg, &ArgStr[2]);
			return;
		}

		if (DecodeRegBase(&ArgStr[1], &reg, &mode1, &disp1))
		{
			if (IsWPL(Index) &&	DecodeInternal(&ArgStr[2], &mode2, &disp2, "PX"))
			{
				const Byte optab[] = { 0xe9, 0xea, 0x5e, 0x00 };

				PreByte(mode2, 1);
				BAsmCode[CodeLen++] = optab[Index];
				BAsmCode[CodeLen++] = mode1 | reg;
				BAsmCode[CodeLen++] = disp2;
				BAsmCode[CodeLen++] = disp1;
				return;
			}

			WrStrErrorPos(ErrNum_InvArg, &ArgStr[2]);
			return;
		}

		if (DecodeIntIndirect(&ArgStr[1], &dir, &mdisp, &mode1, &disp1, "PX"))
		{
			if (IsWPL(Index) &&	DecodeInternal(&ArgStr[2], &mode2, &disp2, "PY"))
			{
				PreByte(mode1, mode2);
				BAsmCode[CodeLen++] = 0xf9 + Index;
				BAsmCode[CodeLen++] = dir;
				BAsmCode[CodeLen++] = disp1;
				if (dir) BAsmCode[CodeLen++] = mdisp;
				BAsmCode[CodeLen++] = disp2;
				return;
			}

			WrStrErrorPos(ErrNum_InvArg, &ArgStr[2]);
			return;
		}

		if (DecodeDirect(&ArgStr[1], &addr))
		{
			if (IsWPL(Index) &&	DecodeInternal(&ArgStr[2], &mode2, &disp2, "PX"))
			{
				PreByte(mode2, 1);
				BAsmCode[CodeLen++] = 0xd9 + Index;
				AddrOut20(addr);
				BAsmCode[CodeLen++] = disp2;
				return;
			}

			WrStrErrorPos(ErrNum_InvArg, &ArgStr[2]);
			return;
		}

		WrStrErrorPos(ErrNum_InvArg, &ArgStr[1]);
		return;
	}
}

static void DecodeEX(Word Index)
{
	Byte reg1;
	Byte reg2;
	Byte mode1;
	Byte mode2;
	Byte disp1;
	Byte disp2;

  UNUSED(Index);

	if (ChkArgCnt(2, 2))
	{
		if (DecodeReg(&ArgStr[1], &reg1))
		{
			if (DecodeReg(&ArgStr[2], &reg2))
			{
				if (RegSize(reg1) != RegSize(reg2))
				{
					WrStrErrorPos(ErrNum_InvReg, &ArgStr[2]);	/* Register size conflict */
					return;
				}

				if (RegSize(reg1) == 1)
				{
					if (reg1 != RegA || reg2 != RegB)
					{
						WrStrErrorPos(ErrNum_InvReg, &ArgStr[2]);	/* Register size conflict */
						return;
					}

					BAsmCode[0] = 0xdd;
					CodeLen =1;
					return;
				}
				else
				{
					if ((IsR2(reg1) && IsR2(reg2)) || (IsR3(reg1) && IsR3(reg2)))
					{
						BAsmCode[0] = 0xed;
						BAsmCode[1] = (reg1 << 4) | reg2;
						CodeLen = 2;
						return;
					}
				}
				
				WrStrErrorPos(ErrNum_InvReg, &ArgStr[2]);
				return;
			}

			WrStrErrorPos(ErrNum_InvArg, &ArgStr[2]);
			return;
		}

		if (DecodeInternal(&ArgStr[1], &mode1, &disp1, "PX"))
		{
			if (DecodeInternal(&ArgStr[2], &mode2, &disp2, "PY"))
			{
				PreByte(mode1, mode2);
				BAsmCode[CodeLen++] = 0xc0;
				BAsmCode[CodeLen++] = disp1;
				BAsmCode[CodeLen++] = disp2;
				return;
			}

			WrStrErrorPos(ErrNum_InvArg, &ArgStr[2]);
			return;
		}

		WrStrErrorPos(ErrNum_InvArg, &ArgStr[2]);
		return;
	}
}

static void DecodeEXW(Word Index)
{
	Byte mode1;
	Byte mode2;
	Byte disp1;
	Byte disp2;

	if (ChkArgCnt(2, 2))
	{
		if (DecodeInternal(&ArgStr[1], &mode1, &disp1, "PX"))
		{
			if (DecodeInternal(&ArgStr[2], &mode2, &disp2, "PY"))
			{
				PreByte(mode1, mode2);
				BAsmCode[CodeLen++] = Index;
				BAsmCode[CodeLen++] = disp1;
				BAsmCode[CodeLen++] = disp2;
				return;
			}

			WrStrErrorPos(ErrNum_InvArg, &ArgStr[2]);
			return;
		}

		WrStrErrorPos(ErrNum_InvArg, &ArgStr[1]);
		return;
	}
}

static void DecodeSWAP(Word Index)
{
	Byte reg;

	if (ChkArgCnt(1, 1))
	{
		if (DecodeReg(&ArgStr[1], &reg))
		{
			if (reg == RegA)
			{
				BAsmCode[0] = Index;
				CodeLen = 1;
				return;
			}
		}

		WrStrErrorPos(ErrNum_InvArg, &ArgStr[1]);
		return;
	}
}

static void DecodeADD(Word Index)
{
	Byte reg1;
	Byte reg2;
	Byte mode;
	Byte disp;
	LongInt val;

	if (ChkArgCnt(2, 2))
	{
		if (DecodeReg(&ArgStr[1], &reg1))
		{
			if (DecodeReg(&ArgStr[2], &reg2))
			{
				if (IsR1(reg1) && IsR1(reg2))
				{
					BAsmCode[0] = Index | 0x06;
				}
				else if (IsR2(reg1) && (IsR1(reg2) || IsR2(reg2)))
				{
					BAsmCode[0] = Index | 0x04;
				}
				else if (IsR3(reg1) && (IsR1(reg2) || IsR2(reg2) || IsR3(reg2)))
				{
					BAsmCode[0] = Index | 0x05;
				}
				else
				{
					WrStrErrorPos(ErrNum_InvArg, &ArgStr[2]);
					return;
				}
				BAsmCode[1] = (reg1 << 4) | reg2;
				CodeLen = 2;
				return;
			}

			if (DecodeInternal(&ArgStr[2], &mode, &disp, "PX"))
			{
				if (reg1 == RegA)
				{
					PreByte(mode, 1);
					BAsmCode[CodeLen++] = Index | 0x02;
					BAsmCode[CodeLen++] = disp;
					return;
				}
			}

			if (DecodeImm(&ArgStr[2], &val, 1))
			{
				if (reg1 == RegA)
				{
					BAsmCode[0] = Index;
					BAsmCode[1] = val;
					CodeLen = 2;
					return;
				}
			}

			WrStrErrorPos(ErrNum_InvArg, &ArgStr[2]);
			return;
		}

		if (DecodeInternal(&ArgStr[1], &mode, &disp, "PX"))
		{
			if (DecodeReg(&ArgStr[2], &reg2))
			{
				if (reg2 == RegA)
				{
					PreByte(mode, 1);
					BAsmCode[CodeLen++] = Index | 0x03;
					BAsmCode[CodeLen++] = disp;
					return;
				}
			}

			if (DecodeImm(&ArgStr[2], &val, 1))
			{
				PreByte(mode, 1);
				BAsmCode[CodeLen++] = Index | 0x01;
				BAsmCode[CodeLen++] = disp;
				BAsmCode[CodeLen++] = val;
				return;
			}

			WrStrErrorPos(ErrNum_InvArg, &ArgStr[2]);
			return;
		}

		WrStrErrorPos(ErrNum_InvArg, &ArgStr[1]);
	}
}

static void DecodeADC(Word Index)
{
	Byte reg;
	LongInt val;
	Byte mode;
	Byte disp;

	if (ChkArgCnt(2, 2))
	{
		if (DecodeReg(&ArgStr[1], &reg))
		{
			if (DecodeInternal(&ArgStr[2], &mode, &disp, "PX"))
			{
				if (reg == RegA)
				{
					PreByte(mode, 1);
					BAsmCode[CodeLen++] = Index | 0x02;
					BAsmCode[CodeLen++] = disp;
					return;
				}
			}

			if (DecodeImm(&ArgStr[2], &val, 1))
			{
				if (reg == RegA)
				{
					BAsmCode[0] = Index;
					BAsmCode[1] = val;
					CodeLen = 2;
					return;
				}

				WrStrErrorPos(ErrNum_InvReg, &ArgStr[1]);
				return;
			}

			WrStrErrorPos(ErrNum_InvArg, &ArgStr[2]);
			return;
		}

		if (DecodeInternal(&ArgStr[1], &mode, &disp, "PX"))
		{
			if (DecodeReg(&ArgStr[2], &reg))
			{
				if (reg == RegA)
				{
					PreByte(mode, 1);
					BAsmCode[CodeLen++] = Index | 0x03;
					BAsmCode[CodeLen++] = disp;
					return;
				}
			}

			if (DecodeImm(&ArgStr[2], &val, 1))
			{
				PreByte(mode, 1);
				BAsmCode[CodeLen++] = Index | 0x01;
				BAsmCode[CodeLen++] = disp;
				BAsmCode[CodeLen++] = val;
				return;
			}

			WrStrErrorPos(ErrNum_InvArg, &ArgStr[2]);
			return;
		}

		WrStrErrorPos(ErrNum_InvArg, &ArgStr[1]);
	}
}

static void DecodeADCL(Word Index)
{
	Byte mode1;
	Byte mode2;
	Byte disp1;
	Byte disp2;
	Byte reg;

  UNUSED(Index);

	if (ChkArgCnt(2, 2))
	{
		if (DecodeInternal(&ArgStr[1], &mode1, &disp1, "PX"))
		{
			if (DecodeReg(&ArgStr[2], &reg))
			{
				if (reg == RegA)
				{
					PreByte(mode1, 1);
					BAsmCode[CodeLen++] = Index | 0x01;
					BAsmCode[CodeLen++] = disp1;
					return;
				}
			}

			if (DecodeInternal(&ArgStr[2], &mode2, &disp2, "PY"))
			{
				PreByte(mode1, mode2);
				BAsmCode[CodeLen++] = Index;
				BAsmCode[CodeLen++] = disp1;
				BAsmCode[CodeLen++] = disp2;
				return;
			}

			WrStrErrorPos(ErrNum_InvArg, &ArgStr[2]);
			return;
		}		
		
		WrStrErrorPos(ErrNum_InvArg, &ArgStr[1]);
	}
}

static void DecodePMDF(Word Index)
{
	Byte mode;
	Byte disp;
	Byte reg;
	LongInt val;

  UNUSED(Index);

	if (ChkArgCnt(2, 2))
	{
		if (DecodeInternal(&ArgStr[1], &mode, &disp, "PX"))
		{
			if (DecodeReg(&ArgStr[2], &reg))
			{
				if (reg == RegA)
				{
					PreByte(mode, 1);
					BAsmCode[CodeLen++] = 0x57;
					BAsmCode[CodeLen++] = disp;
					return;
				}
			}

			if (DecodeImm(&ArgStr[2], &val, 1))
			{
				PreByte(mode, 1);
				BAsmCode[CodeLen++] = 0x47;
				BAsmCode[CodeLen++] = disp;
				BAsmCode[CodeLen++] = val;
				return;
			}

			WrStrErrorPos(ErrNum_InvArg, &ArgStr[2]);
			return;
		}		
		
		WrStrErrorPos(ErrNum_InvArg, &ArgStr[1]);
	}
}

static void DecodeAND(Word Index)
{
	Byte reg;
	Byte mode1;
	Byte mode2;
	Byte disp1;
	Byte disp2;
	LongInt addr;
	LongInt val;

	if (ChkArgCnt(2, 2))
	{
		if (DecodeReg(&ArgStr[1], &reg) && reg == RegA)
		{
			if (DecodeInternal(&ArgStr[2], &mode2, &disp2, "PX"))
			{
				PreByte(mode2, 1);
				BAsmCode[CodeLen++] = Index | 0x07;
				BAsmCode[CodeLen++] = disp2;
				return;
			}

			if (DecodeImm(&ArgStr[2], &val, 1))
			{
				BAsmCode[0] = Index;
				BAsmCode[1] = val;
				CodeLen = 2;
				return;
			}

			WrStrErrorPos(ErrNum_InvArg, &ArgStr[2]);
			return;
		}

		if (DecodeInternal(&ArgStr[1], &mode1, &disp1, "PX"))
		{
			if (DecodeReg(&ArgStr[2], &reg) && reg == RegA)
			{
				PreByte(mode1, 1);
				BAsmCode[CodeLen++] = Index | 0x03;
				BAsmCode[CodeLen++] = disp1;
				return;
			}

			if (DecodeInternal(&ArgStr[2], &mode2, &disp2, "PY"))
			{
				PreByte(mode1, mode2);
				BAsmCode[CodeLen++] = Index | 0x06;
				BAsmCode[CodeLen++] = disp1;
				BAsmCode[CodeLen++] = disp2;
				return;
			}

			if (DecodeImm(&ArgStr[2], &val, 1))
			{
				PreByte(mode1, 1);
				BAsmCode[CodeLen++] = Index | 0x01;
				BAsmCode[CodeLen++] = disp1;
				BAsmCode[CodeLen++] = val;
				return;
			}

			WrStrErrorPos(ErrNum_InvArg, &ArgStr[2]);
			return;
		}

		if (DecodeDirect(&ArgStr[1], &addr))
		{
			if (DecodeImm(&ArgStr[2], &val, 1))
			{
				BAsmCode[CodeLen++] = Index | 0x02;
				AddrOut20(addr);
				BAsmCode[CodeLen++] = val;
				return;
			}

			WrStrErrorPos(ErrNum_InvArg, &ArgStr[2]);
			return;
		}

		WrStrErrorPos(ErrNum_InvArg, &ArgStr[1]);
	}
}

static void DecodeINC(Word Index)
{
	Byte reg;
	Byte mode;
	Byte disp;

	if (ChkArgCnt(1, 1))
	{
		if (DecodeReg(&ArgStr[1], &reg) && IsCoreReg(reg))
		{
			BAsmCode[0] = Index;
			BAsmCode[1] = reg;
			CodeLen = 2;
			return;
		}

		if (DecodeInternal(&ArgStr[1], &mode, &disp, "PX"))
		{
			PreByte(mode, 1);
			BAsmCode[CodeLen++] = Index | 0x01;
			BAsmCode[CodeLen++] = disp;
			return;
		}

		WrStrErrorPos(ErrNum_InvArg, &ArgStr[1]);
	}
}

static void DecodeROR(Word Index)
{
	Byte reg;
	Byte mode;
	Byte disp;

	if (ChkArgCnt(1, 1))
	{
		if (DecodeReg(&ArgStr[1], &reg) && reg == RegA)
		{
			BAsmCode[0] = Index;
			CodeLen = 1;
			return;
		}

		if (DecodeInternal(&ArgStr[1], &mode, &disp, "PX"))
		{
			PreByte(mode, 1);
			BAsmCode[CodeLen++] = Index | 0x01;
			BAsmCode[CodeLen++] = disp;
			return;
		}

		WrStrErrorPos(ErrNum_InvArg, &ArgStr[1]);
	}
}

static void DecodeDSRL(Word Index)
{
	Byte mode;
	Byte disp;

  UNUSED(Index);

	if (ChkArgCnt(1, 1))
	{
		if (DecodeInternal(&ArgStr[1], &mode, &disp, "PX"))
		{
			PreByte(mode, 1);
			BAsmCode[CodeLen++] = Index;
			BAsmCode[CodeLen++] = disp;
			return;
		}

		WrStrErrorPos(ErrNum_InvArg, &ArgStr[1]);
	}
}

static void DecodeCMP(Word Index)
{
	Byte reg;
	Byte mode1;
	Byte mode2;
	Byte disp1;
	Byte disp2;
	LongInt val;
	LongInt addr;

  UNUSED(Index);

	if (ChkArgCnt(2, 2))
	{
		if (DecodeReg(&ArgStr[1], &reg) && reg == RegA)
		{
			if (DecodeImm(&ArgStr[2], &val, 1))
			{
				BAsmCode[0] = 0x60;
				BAsmCode[1] = val;
				CodeLen = 2;
				return;
			}

			WrStrErrorPos(ErrNum_InvArg, &ArgStr[2]);
			return;
		}

		if (DecodeInternal(&ArgStr[1], &mode1, &disp1, "PX"))
		{
			if (DecodeReg(&ArgStr[2], &reg) && reg == RegA)
			{
				PreByte(mode1, 1);
				BAsmCode[CodeLen++] = 0x63;
				BAsmCode[CodeLen++] = disp1;
				return;
			}

			if (DecodeInternal(&ArgStr[2], &mode2, &disp2, "PY"))
			{
				PreByte(mode1, mode2);
				BAsmCode[CodeLen++] = 0xb7;
				BAsmCode[CodeLen++] = disp1;
				BAsmCode[CodeLen++] = disp2;
				return;
			}

			if (DecodeImm(&ArgStr[2], &val, 1))
			{
				PreByte(mode1, 1);
				BAsmCode[CodeLen++] = 0x61;
				BAsmCode[CodeLen++] = disp1;
				BAsmCode[CodeLen++] = val;
				return;
			}

			WrStrErrorPos(ErrNum_InvArg, &ArgStr[2]);
			return;
		}

		if (DecodeDirect(&ArgStr[1], &addr))
		{
			if (DecodeImm(&ArgStr[2], &val, 1))
			{
				BAsmCode[CodeLen++] = 0x62;
				AddrOut20(addr);
				BAsmCode[CodeLen++] = val;
				return;
			}

			WrStrErrorPos(ErrNum_InvArg, &ArgStr[2]);
			return;
		}

		WrStrErrorPos(ErrNum_InvArg, &ArgStr[1]);
	}
}

static void DecodeCMPW(Word Index)
{
	Byte reg;
	Byte mode1;
	Byte mode2;
	Byte disp1;
	Byte disp2;

  UNUSED(Index);

	if (ChkArgCnt(2, 2))
	{
		if (DecodeInternal(&ArgStr[1], &mode1, &disp1, "PX"))
		{
			if (DecodeReg(&ArgStr[2], &reg))
			{
				if (IsCoreReg(reg) && RegSize(reg) == Index)
				{
					PreByte(mode1, 1);
					BAsmCode[CodeLen++] = 0xd4 | Index;
					BAsmCode[CodeLen++] = reg;
					BAsmCode[CodeLen++] = disp1;
					return;
				}
			}

			if (DecodeInternal(&ArgStr[2], &mode2, &disp2, "PY"))
			{
				PreByte(mode1, mode2);
				BAsmCode[CodeLen++] = 0xc4 | Index;
				BAsmCode[CodeLen++] = disp1;
				BAsmCode[CodeLen++] = disp2;
				return;
			}

			WrStrErrorPos(ErrNum_InvArg, &ArgStr[2]);
			return;
		}

		WrStrErrorPos(ErrNum_InvArg, &ArgStr[1]);
	}
}

static void DecodeTEST(Word Index)
{
	Byte reg;
	Byte mode;
	Byte disp;
	LongInt val;
	LongInt addr;

  UNUSED(Index);

	if (ChkArgCnt(2, 2))
	{
		if (DecodeReg(&ArgStr[1], &reg) && reg == RegA)
		{
			if (DecodeImm(&ArgStr[2], &val, 1))
			{
				BAsmCode[0] = 0x64;
				BAsmCode[1] = val;
				CodeLen = 2;
				return;
			}

			WrStrErrorPos(ErrNum_InvArg, &ArgStr[2]);
			return;
		}

		if (DecodeDirect(&ArgStr[1], &addr))
		{
			if (DecodeImm(&ArgStr[2], &val, 1))
			{
				BAsmCode[CodeLen++] = 0x66;
				AddrOut20(addr);
				BAsmCode[CodeLen++] = val;
				return;
			}

			WrStrErrorPos(ErrNum_InvArg, &ArgStr[2]);
			return;
		}

		if (DecodeInternal(&ArgStr[1], &mode, &disp, "PX"))
		{
			if (DecodeReg(&ArgStr[2], &reg) && reg == RegA)
			{
				PreByte(mode, 1);
				BAsmCode[CodeLen++] = 0x67;
				BAsmCode[CodeLen++] = disp;
				return;
			}

			if (DecodeImm(&ArgStr[2], &val, 1))
			{
				PreByte(mode, 1);
				BAsmCode[CodeLen++] = 0x65;
				BAsmCode[CodeLen++] = disp;
				BAsmCode[CodeLen++] = val;
				return;
			}

			WrStrErrorPos(ErrNum_InvArg, &ArgStr[2]);
			return;
		}

		WrStrErrorPos(ErrNum_InvArg, &ArgStr[1]);
	}
}

static void DecodeJP(Word Index)
{
	const Byte OP = Index & 0xff;
	const int alen = (Index & 0x0100) ? 3 : 2;
	int i;
	Boolean ValOK;
	tSymbolFlags flags;
	LongInt addr;
	Byte mode;
	Byte disp;
	Byte reg;

	if (ChkArgCnt(1, 1))
	{
		if (Index & 0x0200)
		{
			if (DecodeReg(&ArgStr[1], &reg) && IsR3(reg))
			{
				BAsmCode[0] = 0x11;
				BAsmCode[1] = reg;
				CodeLen = 2;
				return;
			}

			if (DecodeInternal(&ArgStr[1], &mode, &disp, "PY"))
			{
				PreByte(mode, 1);
				BAsmCode[CodeLen++] = 0x10;
				BAsmCode[CodeLen++] = disp;
				return;
			}
		}

		addr = EvalStrIntExpressionWithFlags(&ArgStr[1], Int20, &ValOK, &flags);
		if (ValOK)
		{
			if (!(Index & 0x100) &&
				!mFirstPassUnknownOrQuestionable(flags) &&
				(addr & 0xf0000) != (EProgCounter() & 0xf0000))
			{
				WrStrErrorPos(ErrNum_TargOnDiffPage, &ArgStr[1]);
				return;
			}

			BAsmCode[CodeLen++] = OP;
			for (i = 0; i < alen; i++)
			{
				BAsmCode[CodeLen++] = addr >> (i * 8);
			}
			return;
		}

		WrStrErrorPos(ErrNum_InvArg, &ArgStr[1]);
	}
}

static void DecodeJR(Word Index)
{
	Byte OpCode = Index;
	LongInt addr;
	int disp;
	Boolean ValOK;
	tSymbolFlags flags;

	if (ChkArgCnt(1, 1))
	{
		addr = EvalStrIntExpressionWithFlags(&ArgStr[1], Int20, &ValOK, &flags);
		if (!mFirstPassUnknownOrQuestionable(flags) &&
			(addr & 0xf0000) != (EProgCounter() & 0xf0000))
		{
			WrStrErrorPos(ErrNum_TargOnDiffPage, &ArgStr[1]);
			return;
		}

		disp = addr - (EProgCounter() + 2);

		if (!mFirstPassUnknownOrQuestionable(flags))
		{
			if (disp < -255 || disp > 255)
			{
				WrStrErrorPos(ErrNum_JmpDistTooBig, &ArgStr[1]);
				return;
			}

			if (disp < 0)
			{
				OpCode |= 0x01;
				disp = -disp;
			}
		}

		BAsmCode[0] = OpCode;
		BAsmCode[1] = disp;
		CodeLen = 2;
	}
}

static void DecodeFixed(Word Index)
{
	if (ChkArgCnt(0, 0))
	{
		BAsmCode[0] = Index;
		CodeLen = 1;
	}
}

static void DecodePUSHS(Word Index)
{
	Byte reg;

	if (ChkArgCnt(1, 1))
	{
		if (DecodeReg(&ArgStr[1], &reg))
		{
			if (IsR1(reg) || IsR2(reg) || reg == RegX || reg == RegY)
			{
				BAsmCode[0] = (Index ? 0x90 : 0xb0) | reg;
				BAsmCode[1] = 0x37;
				CodeLen = 2;
				return;
			}

			if (reg == RegF)
			{
				BAsmCode[0] = Index ? 0x5f : 0x4f;
				CodeLen = 1;
				return;
			}

			if (reg == RegIMR)
			{
				BAsmCode[0] = 0x30;
				BAsmCode[1] = Index ? 0xe0 : 0xe8;
				BAsmCode[2] = Index ? 0x27 : 0x37;
				BAsmCode[3] = 0xfb;
				CodeLen = 4;
				return;
			}
		}

		WrStrErrorPos(ErrNum_InvArg, &ArgStr[1]);
	}
}

static void DecodePUSHU(Word Index)
{
	const Byte OpBase = Index ? 0x38 : 0x28;
	Byte reg;

	if (ChkArgCnt(1, 1))
	{
		if (DecodeReg(&ArgStr[1], &reg))
		{
			if (IsR1(reg) || IsR2(reg) || reg == RegX || reg == RegY)
			{
				BAsmCode[0] = OpBase | reg;
				CodeLen = 1;
				return;
			}

			if (reg == RegF)
			{
				BAsmCode[0] = OpBase | 0x06;
				CodeLen = 1;
				return;
			}

			if (reg == RegIMR)
			{
				BAsmCode[0] = OpBase | 0x07;
				CodeLen = 1;
				return;
			}
		}

		WrStrErrorPos(ErrNum_InvArg, &ArgStr[1]);
	}
}

/*---------------------------------------------------------------------------*/

static void AddMVW(const char *NName, Word NCode)
{
	AddInstTable(InstTable, NName, NCode, DecodeMVW);
}

static void AddEXW(const char *NName, Word NCode)
{
	AddInstTable(InstTable, NName, NCode, DecodeEXW);
}

static void AddADD(const char *NName, Word NCode)
{
	AddInstTable(InstTable, NName, NCode, DecodeADD);
}

static void AddADC(const char *NName, Word NCode)
{
	AddInstTable(InstTable, NName, NCode, DecodeADC);
}

static void AddADCL(const char *NName, Word NCode)
{
	AddInstTable(InstTable, NName, NCode, DecodeADCL);
}

static void AddAND(const char *NName, Word NCode)
{
	AddInstTable(InstTable, NName, NCode, DecodeAND);
}

static void AddINC(const char *NName, Word NCode)
{
	AddInstTable(InstTable, NName, NCode, DecodeINC);
}

static void AddROR(const char *NName, Word NCode)
{
	AddInstTable(InstTable, NName, NCode, DecodeROR);
}

static void AddDSRL(const char *NName, Word NCode)
{
	AddInstTable(InstTable, NName, NCode, DecodeDSRL);
}

static void AddCMPW(const char *NName, Word NCode)
{
	AddInstTable(InstTable, NName, NCode, DecodeCMPW);
}

static void AddJP(const char *NName, Word NCode)
{
	AddInstTable(InstTable, NName, NCode, DecodeJP);
}

static void AddJR(const char *NName, Word NCode)
{
	AddInstTable(InstTable, NName, NCode, DecodeJR);
}

static void AddFixed(const char *NName, Word NCode)
{
	AddInstTable(InstTable, NName, NCode, DecodeFixed);
}

static void AddPUSHS(const char *NName, Word NCode)
{
	AddInstTable(InstTable, NName, NCode, DecodePUSHS);
}

static void AddPUSHU(const char *NName, Word NCode)
{
	AddInstTable(InstTable, NName, NCode, DecodePUSHU);
}

static void InitFields(void)
{
	InstTable = CreateInstTable(128);

  add_null_pseudo(InstTable);

	/* a. MV */
	AddInstTable(InstTable, "MV", 0, DecodeMV);
	AddMVW("MVW", 0);
	AddMVW("MVP", 1);
	AddMVW("MVL", 2);
	AddMVW("MVLD", 3);

	/* b. EX/SWAP */
	AddInstTable(InstTable, "EX", 0, DecodeEX);
	AddEXW("EXW", 0xc1);
	AddEXW("EXP", 0xc2);
	AddEXW("EXL", 0xc3);
	AddInstTable(InstTable, "SWAP", 0xee, DecodeSWAP);

	/* c. ADD/ADC/SUB/SBC/DADL/DSBL/PMDF */
	AddADD("ADD", 0x40);
	AddADC("ADC", 0x50);
	AddADD("SUB", 0x48);
	AddADC("SBC", 0x58);
	AddADCL("ADCL", 0x54);
	AddADCL("SBCL", 0x5c);
	AddADCL("DADL", 0xc4);
	AddADCL("DSBL", 0xd4);
	AddInstTable(InstTable, "PMDF", 0, DecodePMDF);

	/* d. AND/OR/XOR */
	AddAND("AND", 0x70);
	AddAND("OR", 0x78);
	AddAND("XOR", 0x68);

	/* e. INC/DEC */
	AddINC("INC", 0x6c);
	AddINC("DEC", 0x7c);

	/* f. ROR */
	AddROR("ROR", 0xe4);
	AddROR("ROL", 0xe6);
	AddROR("SHR", 0xf4);
	AddROR("SHL", 0xf6);
	AddDSRL("DSRL", 0xfc);
	AddDSRL("DSLL", 0xec);

	/* g. CMP/TEST */
	AddInstTable(InstTable, "CMP", 0, DecodeCMP);
	AddCMPW("CMPW", 2);
	AddCMPW("CMPP", 3);
	AddInstTable(InstTable, "TEST", 0, DecodeTEST);

	/* h. JP/JPF/JR/CALL */
	AddJP("JP", 0x02 | 0x0200);
	AddJP("JPF", 0x03 | 0x0100);
	AddJR("JR", 0x12);
	AddJP("JPZ", 0x14);
	AddJP("JPNZ", 0x15);
	AddJP("JPC", 0x16);
	AddJP("JPNC", 0x17);
	AddJR("JRZ", 0x18);
	AddJR("JRNZ", 0x1a);
	AddJR("JRC", 0x1c);
	AddJR("JRNC", 0x1e);

	/* i. CALL/CALLF/RET */
	AddJP("CALL", 0x04);
	AddJP("CALLF", 0x05 | 0x0100);
	AddFixed("RET", 0x06);
	AddFixed("RETF", 0x07);

	/* j. PUSH/POP */
	AddPUSHS("PUSHS", 0);
	AddPUSHU("PUSHU", 0);
	AddPUSHS("POPS", 1);
	AddPUSHU("POPU", 1);

	/* k. Other */
	AddFixed("NOP", 0x00);
	AddFixed("WAIT", 0xef);
	AddFixed("SC", 0x97);
	AddFixed("RC", 0x9f);
	AddFixed("RETI", 0x01);
	AddFixed("HALT", 0xde);
	AddFixed("OFF", 0xdf);
	AddFixed("TCL", 0xce);
	AddFixed("IR", 0xfe);
	AddFixed("RESET", 0xff);

	AddIntelPseudo(InstTable, eIntPseudoFlag_LittleEndian);
}

static void DeinitFields(void)
{
	DestroyInstTable(InstTable);
}

/*---------------------------------------------------------------------------*/

static void MakeCode_SC62015(void)
{
	if (!LookupInstTable(InstTable, OpPart.str.p_str))
		WrStrErrorPos(ErrNum_UnknownInstruction, &OpPart);

	if ((EProgCounter() & 0xf0000) != ((EProgCounter() + CodeLen) & 0xf0000))
		WrError(ErrNum_PageCrossing);
}

static Boolean IsDef_SC62015(void)
{
	return False;
}

static void SwitchFrom_SC62015(void)
{
	DeinitFields();
}

static void SwitchTo_SC62015(void)
{
 	const TFamilyDescr *pDescr;
	TurnWords = False;
	SetIntConstMode(eIntConstModeIntel);

	pDescr = FindFamilyByName("SC62015");
	PCSymbol = "*";
	HeaderID = pDescr->Id;
	NOPCode = 0x00;	/* NOP */
	DivideChars = ",";
	HasAttrs = False;

	ValidSegs = (1 << SegCode) | (1 << SegReg);
	Grans[SegCode] = 1;
	ListGrans[SegCode] = 1;
	SegInits[SegCode] = 0x00000;
	SegLimits[SegCode] = 0xfffff;
	Grans[SegReg] = 1;
	ListGrans[SegReg] = 1;
	SegInits[SegReg] = 0x00;
	SegLimits[SegReg] = 0xff;

	MakeCode = MakeCode_SC62015;
	IsDef = IsDef_SC62015;
	SwitchFrom = SwitchFrom_SC62015;
	InitFields();
}

void code62015_init(void)
{
	CPUSC62015 = AddCPU("SC62015", SwitchTo_SC62015);
}
