//
// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System
// PARMODE.C - Addressing Modes Parser Include
// Copyright (C) 199x Landon Dyer, 2011 Reboot and Friends
// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986
// Source utilised with the kind permission of Landon Dyer
//

// This file is included (twice) to parse two addressing modes, into slightly
// different var names
{
	// Dn
	// An
	// # expression
	if ((*tok >= KW_D0) && (*tok <= KW_D7))
	{
		AMn = DREG;
		AnREG = *tok++ & 7;
	}
	else if ((*tok >= KW_A0) && (*tok <= KW_A7))
	{
		AMn = AREG;
		AnREG = *tok++ & 7;
	}
	else if (*tok == '#')
	{
		tok++;

		if (expr(AnEXPR, &AnEXVAL, &AnEXATTR, &AnESYM) != OK)
			return ERROR;

		AMn = IMMED;
	}

	// Small problem with this is that the opening parentheses might be an
	// expression that's part of a displacement; this code will falsely flag
	// that as an error.

	// (An)
	// (An)+
	// (An,Xn[.siz][*scale])
	// (PC,Xn[.siz][*scale])
	// (d16,An)
	// (d8,An,Xn[.siz][*scale])
	// (d16,PC)
	// (d8,PC,Xn[.siz][*scale])
	// ([bd,An],Xn,od)
	// ([bd,An,Xn],od)
	// ([bd,PC],Xn,od)
	// ([bd,PC,Xn],od)
	else if (*tok == '(')
	{
		tok++;

		if ((*tok >= KW_A0) && (*tok <= KW_A7))
		{
			AnREG = *tok++ & 7;

			if (*tok == ')')
			{
				tok++;

				if (*tok == '+')
				{
					tok++;
					AMn = APOSTINC;
				}
				else
					AMn = AIND;

				goto AnOK;
			}

			AMn = AINDEXED;
			goto AMn_IX0;                                      // Handle ",Xn[.siz][*scale])"
		}
		else if (*tok == KW_PC)
		{                            // (PC,Xn[.siz][*scale]) 
			tok++;
			AMn = PCINDEXED;

			// Common index handler; enter here with `tok' pointing at the comma.

			AMn_IX0:                                           // Handle indexed with missing expr

			AnEXVAL = 0;
			AnEXATTR = ABS | DEFINED;

			AMn_IXN:                                           // Handle any indexed (tok -> a comma)

			if (*tok++ != ',')
				goto badmode;

			if (*tok < KW_D0 || *tok > KW_A7)
				goto badmode;

			AnIXREG = *tok++ & 15;

			switch ((int)*tok)
			{                                // Index reg size: <empty> | .W | .L 
			case DOTW:
				tok++;
			default:
				AnIXSIZ = 0;
				break;
			case DOTL:
				AnIXSIZ = 0x0800;
				tok++;
				break;
			case DOTB:                                      // .B not allowed here...
				goto badmode;
			}

			if (*tok == '*')
			{                                  // scale: *1, *2, *4, *8 
				tok++;

				if (*tok++ != CONST || *tok > 8)
					goto badmode;

				switch ((int)*tok++)
				{
				case 1:
					break;
				case 2:
					AnIXSIZ |= TIMES2;
					break;
				case 4:
					AnIXSIZ |= TIMES4;
					break;
				case 8:
					AnIXSIZ |= TIMES8;
					break;
				default:
					goto badmode;
				}
			}

			if (*tok++ != ')')                                  // final ")" 
				goto badmode;

			goto AnOK;
		}
		else if (*tok == '[')
		{                              // ([... 
			goto unmode;
		}
		else
		{                                              // (expr... 
			if (expr(AnEXPR, &AnEXVAL, &AnEXATTR, &AnESYM) != OK)
				return ERROR;

			// It could be that this is really just an expression prefixing a
			// register as a displacement...
			if (*tok == ')')
			{
				tok++;
				goto CHK_FOR_DISPn;
			}

			// Otherwise, check for PC & etc displacements...
			if (*tok++ != ',')
				goto badmode;

			if ((*tok >= KW_A0) && (*tok <= KW_A7))
			{
				AnREG = *tok & 7;
				tok++;

				if (*tok == ',')
				{
					AMn = AINDEXED;
					goto AMn_IXN;
				}
				else if (*tok == ')')
				{
					AMn = ADISP;
					tok++;
					goto AnOK;
				}
				else
					goto badmode;
			}
			else if (*tok == KW_PC)
			{
				if (*++tok == ',')
				{                             // expr(PC,Xn...)
					AMn = PCINDEXED;
					goto AMn_IXN;
				}
				else if (*tok == ')')
				{
					AMn = PCDISP;                                // expr(PC) 
					tok++;
					goto AnOK;
				}
				else
					goto badmode;
			}
			else
				goto badmode;
		}
	}
	else if (*tok=='-' && tok[1]=='(' && ((tok[2]>=KW_A0) && (tok[2]<=KW_A7)) && tok[3]==')')
	{
		AMn = APREDEC;
		AnREG = tok[2] & 7;
		tok += 4;
	}
	else if (*tok == KW_CCR)
	{
		AMn = AM_CCR;
		tok++;
		goto AnOK;
	}
	else if (*tok == KW_SR)
	{
		AMn = AM_SR;
		tok++;
		goto AnOK;
	}
	else if (*tok == KW_USP)
	{
		AMn = AM_USP;
		tok++;
		goto AnOK;
	}
	// expr
	// expr.w
	// expr.l
	// d16(An)
	// d8(An,Xn[.siz])
	// d16(PC)
	// d8(PC,Xn[.siz])
	else
	{
		if (expr(AnEXPR, &AnEXVAL, &AnEXATTR, &AnESYM) != OK)
			return ERROR;

CHK_FOR_DISPn:
		if (*tok == DOTW)
		{
			// expr.W 
			tok++;
			AMn = ABSW;
			goto AnOK;
		}
		else if (*tok != '(')
		{
			// expr[.L]
			AMn = ABSL;

			// Defined, absolute values from $FFFF8000..$00007FFF get optimized
			// to absolute short
			if (optim_flag && (AnEXATTR & (TDB|DEFINED)) == DEFINED && (AnEXVAL + 0x8000) < 0x10000)
			{
				AMn = ABSW;
				if (sbra_flag)
					warn("absolute value from $FFFF8000..$00007FFF optimised to absolute short");
			}

			// Is .L forced here?
			if (*tok == DOTL)
			{
				tok++;
				AMn = ABSL;
			}

			goto AnOK;
		}

		tok++;

		if ((*tok >= KW_A0) && (*tok <= KW_A7))
		{
			AnREG = *tok++ & 7;

			if (*tok == ')')
			{
				AMn = ADISP;
				tok++;
				goto AnOK;
			}

			AMn = AINDEXED;
			goto AMn_IXN;
		}
		else if (*tok == KW_PC)
		{
			if (*++tok == ')')
			{
				AMn = PCDISP;
				tok++;
				goto AnOK;
			}

			AMn = PCINDEXED;
			goto AMn_IXN;
		}
		goto badmode;
	}

	// Addressing mode OK

	AnOK:
	;
}

// Clean up dirty little macros
#undef AnOK
#undef AMn
#undef AnREG
#undef AnIXREG
#undef AnIXSIZ
#undef AnEXPR
#undef AnEXVAL
#undef AnEXATTR
#undef AnOEXPR
#undef AnOEXVAL
#undef AnOEXATTR
#undef AnESYM
#undef AMn_IX0
#undef AMn_IXN
#undef CHK_FOR_DISPn
