// DSP instruction decoder
#include "pch.h"

namespace DSP
{
	void Decoder::ResetInfo(DecoderInfo& info)
	{
		info.sizeInBytes = 0;

		info.parallel = false;
		info.parallelMemInstr = DspParallelMemInstruction::Unknown;
		info.instr = DspRegularInstruction::Unknown;

		info.numParameters = 0;
		info.numParametersEx = 0;

		info.flowControl = false;
	}

	void Decoder::AddParam(DecoderInfo& info, DspParameter param)
	{
		assert(info.numParameters < DspDecoderNumParam);

		info.params[info.numParameters] = param;
		info.numParameters++;
	}

	void Decoder::AddParamEx(DecoderInfo& info, DspParameter param)
	{
		assert(info.numParametersEx < DspDecoderNumParam);

		info.paramsEx[info.numParametersEx] = param;
		info.numParametersEx++;
	}

	void Decoder::AddImmOperand(DecoderInfo& info, DspParameter param, uint8_t imm)
	{
		AddParam(info, param);
		if (param == DspParameter::Byte)
			info.ImmOperand.Byte = imm;
		else if (param == DspParameter::Byte2)
			info.ImmOperand2.Byte = imm;
	}

	void Decoder::AddImmOperand(DecoderInfo& info, DspParameter param, int8_t imm)
	{
		AddParam(info, param);
		if (param == DspParameter::SignedByte)
			info.ImmOperand.SignedByte = imm;
		else if (param == DspParameter::SignedByte2)
			info.ImmOperand2.SignedByte = imm;
	}

	void Decoder::AddImmOperand(DecoderInfo& info, DspParameter param, uint16_t imm)
	{
		AddParam(info, param);
		if (param == DspParameter::UnsignedShort)
			info.ImmOperand.UnsignedShort = imm;
		else if (param == DspParameter::UnsignedShort2)
			info.ImmOperand2.UnsignedShort = imm;
	}

	void Decoder::AddImmOperand(DecoderInfo& info, DspParameter param, int16_t imm)
	{
		AddParam(info, param);
		if (param == DspParameter::SignedShort)
			info.ImmOperand.SignedShort = imm;
		else if (param == DspParameter::SignedShort2)
			info.ImmOperand2.SignedShort = imm;
	}

	void Decoder::AddImmOperand(DecoderInfo& info, DspParameter param, DspAddress imm)
	{
		AddParam(info, param);
		if (param == DspParameter::Address)
			info.ImmOperand.Address = imm;
		else if (param == DspParameter::Address2)
			info.ImmOperand2.Address = imm;
	}

	void Decoder::AddBytes(uint8_t* instrPtr, size_t bytes, DecoderInfo& info)
	{
		assert(info.sizeInBytes < sizeof(info.bytes));

		memcpy(&info.bytes[info.sizeInBytes], instrPtr, bytes);
		info.sizeInBytes += bytes;
	}

	void Decoder::Group0(uint8_t* instrPtr, size_t instrMaxSize, DecoderInfo& info, uint16_t instrBits)
	{
		static DspParameter addressreg[] = {
			DspParameter::r0,
			DspParameter::r1,
			DspParameter::r2,
			DspParameter::r3,
		};

		static DspParameter topreg[] = {
			DspParameter::x0,
			DspParameter::y0,
			DspParameter::x1,
			DspParameter::y1,
			DspParameter::a0,
			DspParameter::b0,
			DspParameter::a1,
			DspParameter::b1,
		};

		static DspParameter modifier[] = {	// pld
			DspParameter::mod_none,
			DspParameter::mod_dec,
			DspParameter::mod_inc,
			DspParameter::mod_plus_m,
		};

		static DspParameter modifier_n[] = {
			DspParameter::mod_plus_m0,
			DspParameter::mod_plus_m1,
			DspParameter::mod_plus_m2,
			DspParameter::mod_plus_m3,
		};

		switch ((instrBits >> 8) & 0xf)
		{
			//|nop         |0000 0000 0[00]0 0000|
			//|mr rn,mn    |0000 0000 0[00]m mmrr|
			//|trap        |0000 0000 0[01]0 0000|
			//|wait        |0000 0000 0[01]0 0001|
			//|repr reg    |0000 0000 0[10]r rrrr|
			//|loop reg,ea |0000 0000 0[11]r rrrr aaaa aaaa aaaa aaaa|
			//|mvli d,li   |0000 0000 1[00]d dddd iiii iiii iiii iiii|
			//|ldla d,la   |0000 0000 1[10]d dddd aaaa aaaa aaaa aaaa|
			//|stla la,s   |0000 0000 1[11]s ssss aaaa aaaa aaaa aaaa|

		case 0:
			if ((instrBits & 0x80) == 0)
			{
				switch ((instrBits >> 5) & 3)
				{
				case 0:		// nop, mr
				{
					int r = instrBits & 3;
					int m = (instrBits >> 2) & 7;
					if ((r == 0) && (m == 0))
					{
						info.instr = DspRegularInstruction::nop;
					}
					else
					{
						info.instr = DspRegularInstruction::mr;
						AddParam(info, addressreg[r]);
						AddParam(info, (DspParameter)((int)DspParameter::mod_base + m));
					}
					break;
				}
				case 1:		// trap, wait
				{
					info.instr = (instrBits & 1) == 0 ? DspRegularInstruction::trap : DspRegularInstruction::wait;
					info.flowControl = true;
					break;
				}
				case 2:		// rep r
				{
					info.instr = DspRegularInstruction::rep;
					info.flowControl = true;
					int r = instrBits & 0x1f;
					AddParam(info, (DspParameter)((int)DspParameter::regs + r));
					break;
				}
				case 3:		// loop r
				{
					info.instr = DspRegularInstruction::loop;
					info.flowControl = true;
					int r = instrBits & 0x1f;
					AddParam(info, (DspParameter)((int)DspParameter::regs + r));
					DspAddress addr = _BYTESWAP_UINT16(*(uint16_t*)instrPtr);
					AddBytes(instrPtr, sizeof(uint16_t), info);
					AddImmOperand(info, DspParameter::Address, addr);
					break;
				}
				}
			}
			else
			{
				switch ((instrBits >> 5) & 3)
				{
				case 0:		// mvli
				{
					info.instr = DspRegularInstruction::mvli;
					int r = instrBits & 0x1f;
					uint16_t imm = _BYTESWAP_UINT16(*(uint16_t*)instrPtr);
					AddBytes(instrPtr, sizeof(uint16_t), info);
					AddParam(info, (DspParameter)((int)DspParameter::regs + r));
					AddImmOperand(info, DspParameter::UnsignedShort, imm);
					break;
				}
				case 1:
					// Reserved
					break;
				case 2:		// ldla
				{
					info.instr = DspRegularInstruction::ldla;
					int r = instrBits & 0x1f;
					DspAddress addr = _BYTESWAP_UINT16(*(uint16_t*)instrPtr);
					AddBytes(instrPtr, sizeof(uint16_t), info);
					AddParam(info, (DspParameter)((int)DspParameter::regs + r));
					AddImmOperand(info, DspParameter::Address, addr);
					break;
				}
				case 3:		// stla
				{
					info.instr = DspRegularInstruction::stla;
					int r = instrBits & 0x1f;
					DspAddress addr = _BYTESWAP_UINT16(*(uint16_t*)instrPtr);
					AddBytes(instrPtr, sizeof(uint16_t), info);
					AddImmOperand(info, DspParameter::Address, addr);
					AddParam(info, (DspParameter)((int)DspParameter::regs + r));
					break;
				}
				}
			}
			break;

		case 1:
			// Reserved
			break;

			//|adli d,li   |0000 001d 0000 0000 iiii iiii iiii iiii|
			//|norm d,rn   |0000 001d 0000 01rr|
			//|negc d      |0000 001d 0000 1101|
			//|pld d,rn,mn |0000 001d 0001 mmrr|
			//|xorli d,li  |0000 001d 0010 0000 iiii iiii iiii iiii|

			//|anli d,li   |0000 001d 0100 0000 iiii iiii iiii iiii|
			//|orli d,li   |0000 001d 0110 0000 iiii iiii iiii iiii|
			//|div d,s     |0000 001d 0ss0 1000|
			//|max d,s     |0000 001d 0ss0 1001|
			//|lsf d,s     |0000 001d 01s0 1010|
			//|asf d,s     |0000 001d 01s0 1011|

			//|exec(cc)    |0000 0010 0111 cccc|
			//|cmpli s,li  |0000 001s 1000 0000 iiii iiii iiii iiii|
			//|addc d,s    |0000 001d 10s0 1100|
			//|subc d,s    |0000 001d 10s0 1101|
			//|jmp(cc) ta  |0000 0010 1001 cccc aaaa aaaa aaaa aaaa|
			//|btstl d,bs  |0000 001d 1010 0000 bbbb bbbb bbbb bbbb|
			//|call(cc) ta |0000 0010 1011 cccc aaaa aaaa aaaa aaaa|
			//|btsth d,bs  |0000 001d 1100 0000 bbbb bbbb bbbb bbbb|
			//|lsf d,s     |0000 001d 1100 1010|
			//|asf d,s     |0000 001d 1100 1011|
			//|rets(cc)    |0000 0010 1101 cccc|
			//|reti(cc)    |0000 0010 1111 cccc|

		case 2:
		case 3:
			switch ((instrBits >> 4) & 0xf)
			{
			case 0:		// adli, norm, negc + div, max
				switch (instrBits & 0xf)
				{
				case 0:		// adli
				{
					info.instr = DspRegularInstruction::adli;
					int d = (instrBits & 0x100) ? 1 : 0;
					AddParam(info, d == 0 ? DspParameter::a : DspParameter::b);
					int16_t imm = _BYTESWAP_UINT16(*(uint16_t*)instrPtr);
					AddBytes(instrPtr, sizeof(uint16_t), info);
					AddImmOperand(info, DspParameter::SignedShort, imm);
					break;
				}
				case 4:		// norm
				case 5:
				case 6:
				case 7:
				{
					info.instr = DspRegularInstruction::norm;
					int d = (instrBits & 0x100) ? 1 : 0;
					AddParam(info, d == 0 ? DspParameter::a : DspParameter::b);
					int r = instrBits & 3;
					AddParam(info, addressreg[r]);
					break;
				}
				case 8:		// div
				{
					info.instr = DspRegularInstruction::div;
					int d = (instrBits & 0x100) ? 1 : 0;
					AddParam(info, d == 0 ? DspParameter::a : DspParameter::b);
					int s = (instrBits >> 5) & 3;
					AddParam(info, topreg[s]);
					break;
				}
				case 9:		// max
				{
					info.instr = DspRegularInstruction::max;
					int d = (instrBits & 0x100) ? 1 : 0;
					AddParam(info, d == 0 ? DspParameter::a : DspParameter::b);
					int s = (instrBits >> 5) & 3;
					AddParam(info, topreg[s]);
					break;
				}
				case 0xd:	// negc
				{
					info.instr = DspRegularInstruction::negc;
					int d = (instrBits & 0x100) ? 1 : 0;
					AddParam(info, d == 0 ? DspParameter::a : DspParameter::b);
					break;
				}
				}
				break;
			case 1:		// pld
			{
				info.instr = DspRegularInstruction::pld;
				int d = (instrBits & 0x100) ? 1 : 0;
				AddParam(info, d == 0 ? DspParameter::a1 : DspParameter::b1);
				int r = instrBits & 3;
				AddParam(info, addressreg[r]);
				int m = (instrBits >> 2) & 3;
				AddParam(info, modifier[m] == DspParameter::mod_plus_m ? modifier_n[r] : modifier[m]);
				break;
			}
			case 2:		// xorli + div, max
				switch (instrBits & 0xf)
				{
				case 0:		// xorli
				{
					info.instr = DspRegularInstruction::xorli;
					int d = (instrBits & 0x100) ? 1 : 0;
					AddParam(info, d == 0 ? DspParameter::a1 : DspParameter::b1);
					uint16_t imm = _BYTESWAP_UINT16(*(uint16_t*)instrPtr);
					AddBytes(instrPtr, sizeof(uint16_t), info);
					AddImmOperand(info, DspParameter::UnsignedShort, imm);
					break;
				}
				case 8:		// div
				{
					info.instr = DspRegularInstruction::div;
					int d = (instrBits & 0x100) ? 1 : 0;
					AddParam(info, d == 0 ? DspParameter::a : DspParameter::b);
					int s = (instrBits >> 5) & 3;
					AddParam(info, topreg[s]);
					break;
				}
				case 9:		// max
				{
					info.instr = DspRegularInstruction::max;
					int d = (instrBits & 0x100) ? 1 : 0;
					AddParam(info, d == 0 ? DspParameter::a : DspParameter::b);
					int s = (instrBits >> 5) & 3;
					AddParam(info, topreg[s]);
					break;
				}
				}
				break;
			case 3:
			case 5:
				// Reserved
				break;

			case 4:		// anli + div, max + lsf, asf
				switch (instrBits & 0xf)
				{
				case 0:		// anli
				{
					info.instr = DspRegularInstruction::anli;
					int d = (instrBits & 0x100) ? 1 : 0;
					AddParam(info, d == 0 ? DspParameter::a1 : DspParameter::b1);
					uint16_t imm = _BYTESWAP_UINT16(*(uint16_t*)instrPtr);
					AddBytes(instrPtr, sizeof(uint16_t), info);
					AddImmOperand(info, DspParameter::UnsignedShort, imm);
					break;
				}
				case 8:		// div
				{
					info.instr = DspRegularInstruction::div;
					int d = (instrBits & 0x100) ? 1 : 0;
					AddParam(info, d == 0 ? DspParameter::a : DspParameter::b);
					int s = (instrBits >> 5) & 3;
					AddParam(info, topreg[s]);
					break;
				}
				case 9:		// max
				{
					info.instr = DspRegularInstruction::max;
					int d = (instrBits & 0x100) ? 1 : 0;
					AddParam(info, d == 0 ? DspParameter::a : DspParameter::b);
					int s = (instrBits >> 5) & 3;
					AddParam(info, topreg[s]);
					break;
				}
				case 0xa:		// lsf
				{
					info.instr = DspRegularInstruction::lsf;
					int d = (instrBits & 0x100) ? 1 : 0;
					AddParam(info, d == 0 ? DspParameter::a : DspParameter::b);
					int s = (instrBits & 0x20) ? 1 : 0;
					AddParam(info, s == 0 ? DspParameter::x1 : DspParameter::y1);
					break;
				}
				case 0xb:		// asf
				{
					info.instr = DspRegularInstruction::asf;
					int d = (instrBits & 0x100) ? 1 : 0;
					AddParam(info, d == 0 ? DspParameter::a : DspParameter::b);
					int s = (instrBits & 0x20) ? 1 : 0;
					AddParam(info, s == 0 ? DspParameter::x1 : DspParameter::y1);
					break;
				}
				}
				break;
			case 6:		// orli + div, max + lsf, asf
				switch (instrBits & 0xf)
				{
				case 0:		// orli
				{
					info.instr = DspRegularInstruction::orli;
					int d = (instrBits & 0x100) ? 1 : 0;
					AddParam(info, d == 0 ? DspParameter::a1 : DspParameter::b1);
					uint16_t imm = _BYTESWAP_UINT16(*(uint16_t*)instrPtr);
					AddBytes(instrPtr, sizeof(uint16_t), info);
					AddImmOperand(info, DspParameter::UnsignedShort, imm);
					break;
				}
				case 8:		// div
				{
					info.instr = DspRegularInstruction::div;
					int d = (instrBits & 0x100) ? 1 : 0;
					AddParam(info, d == 0 ? DspParameter::a : DspParameter::b);
					int s = (instrBits >> 5) & 3;
					AddParam(info, topreg[s]);
					break;
				}
				case 9:		// max
				{
					info.instr = DspRegularInstruction::max;
					int d = (instrBits & 0x100) ? 1 : 0;
					AddParam(info, d == 0 ? DspParameter::a : DspParameter::b);
					int s = (instrBits >> 5) & 3;
					AddParam(info, topreg[s]);
					break;
				}
				case 0xa:		// lsf
				{
					info.instr = DspRegularInstruction::lsf;
					int d = (instrBits & 0x100) ? 1 : 0;
					AddParam(info, d == 0 ? DspParameter::a : DspParameter::b);
					int s = (instrBits & 0x20) ? 1 : 0;
					AddParam(info, s == 0 ? DspParameter::x1 : DspParameter::y1);
					break;
				}
				case 0xb:		// asf
				{
					info.instr = DspRegularInstruction::asf;
					int d = (instrBits & 0x100) ? 1 : 0;
					AddParam(info, d == 0 ? DspParameter::a : DspParameter::b);
					int s = (instrBits & 0x20) ? 1 : 0;
					AddParam(info, s == 0 ? DspParameter::x1 : DspParameter::y1);
					break;
				}
				}
				break;

			case 7:		// exec
			{
				info.instr = DspRegularInstruction::exec;
				info.flowControl = true;
				info.cc = (ConditionCode)(instrBits & 0xf);
				break;
			}
			case 8:		// cmpli + addc, subc
				switch (instrBits & 0xf)
				{
				case 0:		// cmpli
				{
					info.instr = DspRegularInstruction::cmpli;
					int d = (instrBits & 0x100) ? 1 : 0;
					AddParam(info, d == 0 ? DspParameter::a : DspParameter::b);
					int16_t imm = _BYTESWAP_UINT16(*(uint16_t*)instrPtr);
					AddBytes(instrPtr, sizeof(uint16_t), info);
					AddImmOperand(info, DspParameter::SignedShort, imm);
					break;
				}
				case 0xc:		// addc
				{
					info.instr = DspRegularInstruction::addc;
					int d = (instrBits & 0x100) ? 1 : 0;
					AddParam(info, d == 0 ? DspParameter::a : DspParameter::b);
					int s = (instrBits & 0x20) ? 1 : 0;
					AddParam(info, s == 0 ? DspParameter::x : DspParameter::y);
					break;
				}
				case 0xd:		// subc
				{
					info.instr = DspRegularInstruction::subc;
					int d = (instrBits & 0x100) ? 1 : 0;
					AddParam(info, d == 0 ? DspParameter::a : DspParameter::b);
					int s = (instrBits & 0x20) ? 1 : 0;
					AddParam(info, s == 0 ? DspParameter::x : DspParameter::y);
					break;
				}
				}
				break;
			case 9:		// jmp
			{
				info.instr = DspRegularInstruction::jmp;
				info.flowControl = true;
				info.cc = (ConditionCode)(instrBits & 0xf);
				DspAddress addr = _BYTESWAP_UINT16(*(uint16_t*)instrPtr);
				AddBytes(instrPtr, sizeof(uint16_t), info);
				AddImmOperand(info, DspParameter::Address, addr);
				break;
			}
			case 0xa:	// btstl + addc, subc
				switch (instrBits & 0xf)
				{
				case 0:		// btstl
				{
					info.instr = DspRegularInstruction::btstl;
					int d = (instrBits & 0x100) ? 1 : 0;
					AddParam(info, d == 0 ? DspParameter::a1 : DspParameter::b1);
					uint16_t imm = _BYTESWAP_UINT16(*(uint16_t*)instrPtr);
					AddBytes(instrPtr, sizeof(uint16_t), info);
					AddImmOperand(info, DspParameter::UnsignedShort, imm);
					break;
				}
				case 0xc:		// addc
				{
					info.instr = DspRegularInstruction::addc;
					int d = (instrBits & 0x100) ? 1 : 0;
					AddParam(info, d == 0 ? DspParameter::a : DspParameter::b);
					int s = (instrBits & 0x20) ? 1 : 0;
					AddParam(info, s == 0 ? DspParameter::x : DspParameter::y);
					break;
				}
				case 0xd:		// subc
				{
					info.instr = DspRegularInstruction::subc;
					int d = (instrBits & 0x100) ? 1 : 0;
					AddParam(info, d == 0 ? DspParameter::a : DspParameter::b);
					int s = (instrBits & 0x20) ? 1 : 0;
					AddParam(info, s == 0 ? DspParameter::x : DspParameter::y);
					break;
				}
				}
				break;
			case 0xb:	// call
			{
				info.instr = DspRegularInstruction::call;
				info.flowControl = true;
				info.cc = (ConditionCode)(instrBits & 0xf);
				DspAddress addr = _BYTESWAP_UINT16(*(uint16_t*)instrPtr);
				AddBytes(instrPtr, sizeof(uint16_t), info);
				AddImmOperand(info, DspParameter::Address, addr);
				break;
			}
			case 0xc:	// btsth, lsf, asf
				switch (instrBits & 0xf)
				{
				case 0:		// btsth
				{
					info.instr = DspRegularInstruction::btsth;
					int d = (instrBits & 0x100) ? 1 : 0;
					AddParam(info, d == 0 ? DspParameter::a1 : DspParameter::b1);
					uint16_t imm = _BYTESWAP_UINT16(*(uint16_t*)instrPtr);
					AddBytes(instrPtr, sizeof(uint16_t), info);
					AddImmOperand(info, DspParameter::UnsignedShort, imm);
					break;
				}
				case 0xa:		// lsf
				{
					info.instr = DspRegularInstruction::lsf;
					int d = (instrBits & 0x100) ? 1 : 0;
					AddParam(info, d == 0 ? DspParameter::a : DspParameter::b);
					AddParam(info, d == 0 ? DspParameter::b1 : DspParameter::a1);
					break;
				}
				case 0xb:		// asf
				{
					info.instr = DspRegularInstruction::asf;
					int d = (instrBits & 0x100) ? 1 : 0;
					AddParam(info, d == 0 ? DspParameter::a : DspParameter::b);
					AddParam(info, d == 0 ? DspParameter::b1 : DspParameter::a1);
					break;
				}
				}
				break;
			case 0xd:	// rets
			{
				info.instr = DspRegularInstruction::rets;
				info.flowControl = true;
				info.cc = (ConditionCode)(instrBits & 0xf);
				break;
			}
			case 0xe:
				// Reserved
				break;
			case 0xf:	// reti
			{
				info.instr = DspRegularInstruction::reti;
				info.flowControl = true;
				info.cc = (ConditionCode)(instrBits & 0xf);
				break;
			}
			}
			break;

			//|adsi d,si   |0000 010d iiii iiii|

		case 4:
		case 5:
		{
			info.instr = DspRegularInstruction::adsi;
			int r = (instrBits & 0x100) ? 1 : 0;
			AddParam(info, r == 0 ? DspParameter::a : DspParameter::b);
			AddImmOperand(info, DspParameter::SignedByte, (int8_t)instrBits);
			break;
		}

		//|cmpsi s,si  |0000 011s iiii iiii|

		case 6:
		case 7:
		{
			info.instr = DspRegularInstruction::cmpsi;
			int r = (instrBits & 0x100) ? 1 : 0;
			AddParam(info, r == 0 ? DspParameter::a : DspParameter::b);
			AddImmOperand(info, DspParameter::SignedByte, (int8_t)instrBits);
			break;
		}

		//|mvsi d,si   |0000 1ddd iiii iiii|

		case 8:
		case 9:
		case 0xa:
		case 0xb:
		case 0xc:
		case 0xd:
		case 0xe:
		case 0xf:
		{
			info.instr = DspRegularInstruction::mvsi;
			AddParam(info, topreg[(instrBits >> 8) & 7]);
			AddImmOperand(info, DspParameter::SignedByte, (int8_t)instrBits);
			break;
		}
		}
	}

	void Decoder::Group1(uint8_t* instrPtr, size_t instrMaxSize, DecoderInfo& info, uint16_t instrBits)
	{
		static DspParameter addressreg[] = {
			DspParameter::r0,
			DspParameter::r1,
			DspParameter::r2,
			DspParameter::r3,
		};

		static DspParameter modifier[] = {
			DspParameter::mod_none,
			DspParameter::mod_dec,
			DspParameter::mod_inc,
			DspParameter::mod_plus_m,
		};

		static DspParameter modifier_n[] = {
			DspParameter::mod_plus_m0,
			DspParameter::mod_plus_m1,
			DspParameter::mod_plus_m2,
			DspParameter::mod_plus_m3,
		};

		//|rep rc      |0001 0000 cccc cccc|
		//|loop lc,ea  |0001 0001 cccc cccc aaaa aaaa aaaa aaaa|
		//|clr b       |0001 0010 0000 0bbb|
		//|set b       |0001 0011 0000 0bbb|
		//|lsfi d,si   |0001 010d 0iii iiii|
		//|asfi d,si   |0001 010d 1iii iiii|
		//|stli sa,li  |0001 0110 aaaa aaaa iiii iiii iiii iiii|
		//|jmp(cc) rn  |0001 0111 0rr0 cccc|
		//|call(cc) rn |0001 0111 0rr1 cccc|
		//|ld d,r,m    |0001 100m mrrd dddd|
		//|st r,m,s    |0001 101m mrrs ssss|
		//|mv d,s      |0001 11dd ddds ssss|

		switch ((instrBits >> 8) & 0xf)
		{
		case 0:		// rep
		{
			info.instr = DspRegularInstruction::rep;
			info.flowControl = true;
			AddImmOperand(info, DspParameter::Byte, (uint8_t)instrBits);
			break;
		}
		case 1:		// loop
		{
			info.instr = DspRegularInstruction::loop;
			info.flowControl = true;
			AddImmOperand(info, DspParameter::Byte, (uint8_t)instrBits);
			DspAddress addr = _BYTESWAP_UINT16(*(uint16_t*)instrPtr);
			AddBytes(instrPtr, sizeof(uint16_t), info);
			AddImmOperand(info, DspParameter::Address2, addr);
			break;
		}
		case 2:		// clr
		{
			int b = instrBits & 7;
			if (b != 7)
			{
				info.instr = DspRegularInstruction::clr;
				AddParam(info, (DspParameter)((int)DspParameter::psr_tb + b));
			}
			break;
		}
		case 3:		// set
		{
			int b = instrBits & 7;
			if (b != 7)
			{
				info.instr = DspRegularInstruction::set;
				AddParam(info, (DspParameter)((int)DspParameter::psr_tb + b));
			}
			break;
		}
		case 4:		// lsfi, asfi
		case 5:
		{
			int d = (instrBits & 0x100) ? 1 : 0;

			int8_t si = instrBits & 0x7f;
			if (si & 0x40)
			{
				si |= 0x80;
			}

			info.instr = (instrBits & 0x80) == 0 ? DspRegularInstruction::lsfi : DspRegularInstruction::asfi;
			AddParam(info, d == 0 ? DspParameter::a : DspParameter::b);
			AddImmOperand(info, DspParameter::SignedByte, si);
			break;
		}
		case 6:		// stli
		{
			info.instr = DspRegularInstruction::stli;
			DspAddress addr = 0xff00 | (instrBits & 0xff);
			AddImmOperand(info, DspParameter::Address, addr);
			uint16_t imm = _BYTESWAP_UINT16(*(uint16_t*)instrPtr);
			AddBytes(instrPtr, sizeof(uint16_t), info);
			AddImmOperand(info, DspParameter::UnsignedShort2, imm);
			break;
		}
		case 7:		// jmp, call
		{
			info.instr = (instrBits & 0x10) == 0 ? DspRegularInstruction::jmp : DspRegularInstruction::call;
			info.flowControl = true;
			info.cc = (ConditionCode)(instrBits & 0xf);
			int r = (instrBits >> 5) & 3;
			AddParam(info, addressreg[r]);
			break;
		}
		case 8:		// ld
		case 9:
		{
			info.instr = DspRegularInstruction::ld;
			int d = instrBits & 0x1f;
			AddParam(info, (DspParameter)((int)DspParameter::regs + d));
			int r = (instrBits >> 5) & 3;
			AddParam(info, addressreg[r]);
			int m = (instrBits >> 7) & 3;
			AddParam(info, modifier[m] == DspParameter::mod_plus_m ? modifier_n[r] : modifier[m]);
			break;
		}
		case 0xa:	// st
		case 0xb:
		{
			info.instr = DspRegularInstruction::st;
			int r = (instrBits >> 5) & 3;
			AddParam(info, addressreg[r]);
			int m = (instrBits >> 7) & 3;
			AddParam(info, modifier[m] == DspParameter::mod_plus_m ? modifier_n[r] : modifier[m]);
			int s = instrBits & 0x1f;
			AddParam(info, (DspParameter)((int)DspParameter::regs + s));
			break;
		}
		case 0xc:	// mv
		case 0xd:
		case 0xe:
		case 0xf:
		{
			info.instr = DspRegularInstruction::mv;
			int d = (instrBits >> 5) & 0x1f;
			AddParam(info, (DspParameter)((int)DspParameter::regs + d));
			int s = instrBits & 0x1f;
			AddParam(info, (DspParameter)((int)DspParameter::regs + s));
			break;
		}
		}
	}

	void Decoder::Group2(DecoderInfo& info, uint16_t instrBits)
	{
		static DspParameter ldsa_reg[] = {
			DspParameter::x0,
			DspParameter::y0,
			DspParameter::x1,
			DspParameter::y1,
			DspParameter::a0,
			DspParameter::b0,
			DspParameter::a1,
			DspParameter::b1,
		};

		static DspParameter stsa_reg[] = {
			DspParameter::a2,
			DspParameter::b2,
			DspParameter::Unknown,
			DspParameter::Unknown,
			DspParameter::a0,
			DspParameter::b0,
			DspParameter::a1,
			DspParameter::b1,
		};

		//|ldsa d,sa   |0010 0ddd aaaa aaaa|
		//|stsa sa,s   |0010 1sss aaaa aaaa|

		if ((instrBits & 0x800) == 0)
		{
			// ldsa
			info.instr = DspRegularInstruction::ldsa;
			int d = (instrBits >> 8) & 7;
			AddParam(info, ldsa_reg[d]);
			AddImmOperand(info, DspParameter::Address, (DspAddress)(uint8_t)instrBits);
		}
		else
		{
			// stsa
			info.instr = DspRegularInstruction::stsa;
			AddImmOperand(info, DspParameter::Address, (DspAddress)(uint8_t)instrBits);
			int s = (instrBits >> 8) & 7;
			AddParam(info, stsa_reg[s]);
		}
	}

	void Decoder::Group3(DecoderInfo& info, uint16_t instrBits)
	{
		//|xor d,s      |0011 00sd 0xxx xxxx|
		//|xor d,s      |0011 000d 1xxx xxxx|
		//|not d        |0011 001d 1xxx xxxx|
		//|and d,s      |0011 01sd 0xxx xxxx|
		//|lsf d,s      |0011 01sd 1xxx xxxx|
		//|or d,s       |0011 10sd 0xxx xxxx|
		//|asf d,s      |0011 10sd 1xxx xxxx|
		//|and d,s      |0011 110d 0xxx xxxx|
		//|lsf d,s      |0011 110d 1xxx xxxx|
		//|or d,s       |0011 111d 0xxx xxxx|
		//|asf d,s      |0011 111d 1xxx xxxx|

		bool msb = (instrBits & 0x80) ? true : false;

		switch ((instrBits >> 9) & 7)
		{
		case 0:		// xor
		{
			info.parallelInstr = DspParallelInstruction::_xor;
			if (!msb)
			{
				int s = (instrBits >> 9) & 1;
				int d = (instrBits >> 8) & 1;
				AddParam(info, d == 0 ? DspParameter::a1 : DspParameter::b1);
				AddParam(info, s == 0 ? DspParameter::x1 : DspParameter::y1);
			}
			else
			{
				int d = (instrBits >> 8) & 1;
				AddParam(info, d == 0 ? DspParameter::a1 : DspParameter::b1);
				AddParam(info, d == 0 ? DspParameter::b1 : DspParameter::a1);
			}
			break;
		}
		case 1:		// not + xor
		{
			if (!msb)
			{
				// xor
				info.parallelInstr = DspParallelInstruction::_xor;
				int s = (instrBits >> 9) & 1;
				int d = (instrBits >> 8) & 1;
				AddParam(info, d == 0 ? DspParameter::a1 : DspParameter::b1);
				AddParam(info, s == 0 ? DspParameter::x1 : DspParameter::y1);
			}
			else
			{
				// not
				info.parallelInstr = DspParallelInstruction::_not;
				int d = (instrBits >> 8) & 1;
				AddParam(info, d == 0 ? DspParameter::a1 : DspParameter::b1);
			}
			break;
		}
		case 2:		// and, lsf
		case 3:
		{
			if (!msb)
			{
				// and
				info.parallelInstr = DspParallelInstruction::_and;
				int s = (instrBits >> 9) & 1;
				int d = (instrBits >> 8) & 1;
				AddParam(info, d == 0 ? DspParameter::a1 : DspParameter::b1);
				AddParam(info, s == 0 ? DspParameter::x1 : DspParameter::y1);
			}
			else
			{
				// lsf
				info.parallelInstr = DspParallelInstruction::lsf;
				int s = (instrBits >> 9) & 1;
				int d = (instrBits >> 8) & 1;
				AddParam(info, d == 0 ? DspParameter::a : DspParameter::b);
				AddParam(info, s == 0 ? DspParameter::x1 : DspParameter::y1);
			}
			break;
		}
		case 4:		// or, asf
		case 5:
		{
			if (!msb)
			{
				// or
				info.parallelInstr = DspParallelInstruction::_or;
				int s = (instrBits >> 9) & 1;
				int d = (instrBits >> 8) & 1;
				AddParam(info, d == 0 ? DspParameter::a1 : DspParameter::b1);
				AddParam(info, s == 0 ? DspParameter::x1 : DspParameter::y1);
			}
			else
			{
				// asf
				info.parallelInstr = DspParallelInstruction::asf;
				int s = (instrBits >> 9) & 1;
				int d = (instrBits >> 8) & 1;
				AddParam(info, d == 0 ? DspParameter::a : DspParameter::b);
				AddParam(info, s == 0 ? DspParameter::x1 : DspParameter::y1);
			}
			break;
		}
		case 6:		// and, lsf
		{
			if (!msb)
			{
				// and
				info.parallelInstr = DspParallelInstruction::_and;
				int d = (instrBits >> 8) & 1;
				AddParam(info, d == 0 ? DspParameter::a1 : DspParameter::b1);
				AddParam(info, d == 0 ? DspParameter::b1 : DspParameter::a1);
			}
			else
			{
				// lsf
				info.parallelInstr = DspParallelInstruction::lsf;
				int d = (instrBits >> 8) & 1;
				AddParam(info, d == 0 ? DspParameter::a : DspParameter::b);
				AddParam(info, d == 0 ? DspParameter::x1 : DspParameter::y1);
			}
			break;
		}
		case 7:		// or, asf
		{
			if (!msb)
			{
				// or
				info.parallelInstr = DspParallelInstruction::_or;
				int d = (instrBits >> 8) & 1;
				AddParam(info, d == 0 ? DspParameter::a1 : DspParameter::b1);
				AddParam(info, d == 0 ? DspParameter::b1 : DspParameter::a1);
			}
			else
			{
				// asf
				info.parallelInstr = DspParallelInstruction::asf;
				int d = (instrBits >> 8) & 1;
				AddParam(info, d == 0 ? DspParameter::a : DspParameter::b);
				AddParam(info, d == 0 ? DspParameter::x1 : DspParameter::y1);
			}
			break;
		}
		}

		GroupMemOps3(info, instrBits);
	}

	void Decoder::Group4_6(DecoderInfo& info, uint16_t instrBits)
	{
		//|add d,s      |0100 sssd xxxx xxxx|
		//|sub d,s      |0101 sssd xxxx xxxx|
		//|amv d,s      |0110 sssd xxxx xxxx|

		static DspParameter srcreg[] = {
			DspParameter::x0,
			DspParameter::y0,
			DspParameter::x1,
			DspParameter::y1,
			DspParameter::x,
			DspParameter::y,
			DspParameter::a,
			DspParameter::prod,
		};

		switch (instrBits >> 12)
		{
		case 4:
			info.parallelInstr = DspParallelInstruction::add;
			break;
		case 5:
			info.parallelInstr = DspParallelInstruction::sub;
			break;
		case 6:
			info.parallelInstr = DspParallelInstruction::amv;
			break;
		}

		int d = (instrBits & 0x100) ? 1 : 0;
		int s = (instrBits >> 9) & 7;

		AddParam(info, d == 0 ? DspParameter::a : DspParameter::b);
		AddParam(info, srcreg[s] == DspParameter::a ? (d == 0 ? DspParameter::b : DspParameter::a) : srcreg[s]);

		GroupMemOps4_F(info, instrBits);
	}

	void Decoder::Group7(DecoderInfo& info, uint16_t instrBits)
	{
		//|addl d,s     |0111 00sd xxxx xxxx|
		//|inc d        |0111 01dd xxxx xxxx|
		//|dec d        |0111 10dd xxxx xxxx|
		//|neg d        |0111 110d xxxx xxxx|
		//|neg d,p      |0111 111d xxxx xxxx|

		static DspParameter increg[] = {
			DspParameter::a1,
			DspParameter::b1,
			DspParameter::a,
			DspParameter::b
		};

		int s = (instrBits & 0x200) ? 1 : 0;
		int d = (instrBits & 0x100) ? 1 : 0;
		int dd = (instrBits >> 8) & 3;

		switch ((instrBits >> 9) & 7)
		{
		case 0:
		case 1:
			info.parallelInstr = DspParallelInstruction::addl;
			AddParam(info, d == 0 ? DspParameter::a : DspParameter::b);
			AddParam(info, s == 0 ? DspParameter::x0 : DspParameter::y0);
			break;
		case 2:
		case 3:
			info.parallelInstr = DspParallelInstruction::inc;
			AddParam(info, increg[dd]);
			break;
		case 4:
		case 5:
			info.parallelInstr = DspParallelInstruction::dec;
			AddParam(info, increg[dd]);
			break;
		case 6:
			info.parallelInstr = DspParallelInstruction::neg;
			AddParam(info, d == 0 ? DspParameter::a : DspParameter::b);
			break;
		case 7:
			info.parallelInstr = DspParallelInstruction::neg;
			AddParam(info, d == 0 ? DspParameter::a : DspParameter::b);
			AddParam(info, DspParameter::prod);
			break;
		}

		GroupMemOps4_F(info, instrBits);
	}

	void Decoder::Group8(DecoderInfo& info, uint16_t instrBits)
	{
		//|nop          |1000 0000 xxxx xxxx|
		//|clr d        |1000 d001 xxxx xxxx|
		//|cmp a,b      |1000 0010 xxxx xxxx|
		//|mpy x1,x1    |1000 0011 xxxx xxxx|
		//|clr p        |1000 0100 xxxx xxxx|
		//|tst p        |1000 0101 xxxx xxxx|
		//|tst s        |1000 011s xxxx xxxx|
		//|clr im       |1000 1010 xxxx xxxx|
		//|set im       |1000 1011 xxxx xxxx|
		//|clr dp       |1000 1100 xxxx xxxx|
		//|set dp       |1000 1101 xxxx xxxx|
		//|clr xl       |1000 1110 xxxx xxxx|
		//|set xl       |1000 1111 xxxx xxxx|

		switch ((instrBits >> 8) & 0xf)
		{
		case 0:		// nop
			info.parallelInstr = DspParallelInstruction::nop;
			break;
		case 1:		// clr d
		case 9:
		{
			int d = (instrBits >> 11) & 1;
			info.parallelInstr = DspParallelInstruction::clr;
			AddParam(info, d == 0 ? DspParameter::a : DspParameter::b);
			break;
		}
		case 2:		// cmp a,b
			info.parallelInstr = DspParallelInstruction::cmp;
			AddParam(info, DspParameter::a);
			AddParam(info, DspParameter::b);
			break;
		case 3:		// mpy x1,x1
			info.parallelInstr = DspParallelInstruction::mpy;
			AddParam(info, DspParameter::x1);
			AddParam(info, DspParameter::x1);
			break;
		case 4:		// clr p
			info.parallelInstr = DspParallelInstruction::clr;
			AddParam(info, DspParameter::prod);
			break;
		case 5:		// tst p
			info.parallelInstr = DspParallelInstruction::tst;
			AddParam(info, DspParameter::prod);
			break;
		case 6:		// tst s (Form 2)
		case 7:
		{
			int s = (instrBits >> 8) & 1;
			info.parallelInstr = DspParallelInstruction::tst;
			AddParam(info, s == 0 ? DspParameter::x1 : DspParameter::y1);
			break;
		}
		case 8:
			// Reserved
			break;
		case 0xa:	// clr im
			info.parallelInstr = DspParallelInstruction::clr;
			AddParam(info, DspParameter::psr_im);
			break;
		case 0xb:	// set im
			info.parallelInstr = DspParallelInstruction::set;
			AddParam(info, DspParameter::psr_im);
			break;
		case 0xc:	// clr dp
			info.parallelInstr = DspParallelInstruction::clr;
			AddParam(info, DspParameter::psr_dp);
			break;
		case 0xd:	// set dp
			info.parallelInstr = DspParallelInstruction::set;
			AddParam(info, DspParameter::psr_dp);
			break;
		case 0xe:	// clr xl
			info.parallelInstr = DspParallelInstruction::clr;
			AddParam(info, DspParameter::psr_xl);
			break;
		case 0xf:	// set xl
			info.parallelInstr = DspParallelInstruction::set;
			AddParam(info, DspParameter::psr_xl);
			break;
		}

		GroupMemOps4_F(info, instrBits);
	}

	void Decoder::Group9_B(DecoderInfo& info, uint16_t instrBits)
	{
		//|asr16 d      |1001 d001 xxxx xxxx|
		//|abs d        |1010 d001 xxxx xxxx|
		//|tst s        |1011 s001 xxxx xxxx|

		if (((instrBits >> 8) & 7) == 1)
		{
			switch (instrBits >> 12)
			{
			case 9:
				info.parallelInstr = DspParallelInstruction::asr16;
				break;
			case 0xa:
				info.parallelInstr = DspParallelInstruction::abs;
				break;
			case 0xb:
				info.parallelInstr = DspParallelInstruction::tst;
				break;
			}

			int d = (instrBits & 0x800) ? 1 : 0;
			AddParam(info, d == 0 ? DspParameter::a : DspParameter::b);
		}

		GroupMemOps4_F(info, instrBits);
	}

	void Decoder::GroupCD(DecoderInfo& info, uint16_t instrBits)
	{
		//|cmp d,s      |110s d001 xxxx xxxx|

		if (((instrBits >> 8) & 7) == 1)
		{
			info.parallelInstr = DspParallelInstruction::cmp;

			int s = (instrBits & 0x1000) ? 1 : 0;
			int d = (instrBits & 0x800) ? 1 : 0;

			AddParam(info, d == 0 ? DspParameter::a : DspParameter::b);
			AddParam(info, s == 0 ? DspParameter::x1 : DspParameter::y1);
		}

		GroupMemOps4_F(info, instrBits);
	}

	void Decoder::GroupE(DecoderInfo& info, uint16_t instrBits)
	{
		static DspParameter form1_regs[][2] = {
			{ DspParameter::x0, DspParameter::y0 },
			{ DspParameter::x0, DspParameter::y1 },
			{ DspParameter::x1, DspParameter::y0 },
			{ DspParameter::x1, DspParameter::y1 },
		};

		static DspParameter form2_regs[][2] = {
			{ DspParameter::a1, DspParameter::x1 },
			{ DspParameter::a1, DspParameter::y1 },
			{ DspParameter::b1, DspParameter::x1 },
			{ DspParameter::b1, DspParameter::y1 },
		};

		//|mac s1,s2    |1110 00ss xxxx xxxx|
		//|mac s1,s2    |1110 10ss xxxx xxxx|
		//|macn s1,s2   |1110 01ss xxxx xxxx|
		//|macn s1,s2   |1110 11ss xxxx xxxx|

		info.parallelInstr = (instrBits & 0x400) == 0 ? DspParallelInstruction::mac : DspParallelInstruction::macn;

		int ss = (instrBits >> 8) & 3;
		bool form1 = (instrBits & 0x800) == 0;

		AddParam(info, form1 ? form1_regs[ss][0] : form2_regs[ss][0]);
		AddParam(info, form1 ? form1_regs[ss][1] : form2_regs[ss][1]);

		GroupMemOps4_F(info, instrBits);
	}

	void Decoder::GroupF(DecoderInfo& info, uint16_t instrBits)
	{
		//|lsl16 d      |1111 000d xxxx xxxx|
		//|mac s1,s2    |1111 001s xxxx xxxx|
		//|lsr16 d      |1111 010d xxxx xxxx|
		//|macn s1,s2   |1111 011s xxxx xxxx|
		//|addp d,s     |1111 10sd xxxx xxxx|
		//|rnd d        |1111 110d xxxx xxxx|
		//|rndp d       |1111 111d xxxx xxxx|

		int s = (instrBits & 0x200) ? 1 : 0;
		int d = (instrBits & 0x100) ? 1 : 0;

		switch ((instrBits >> 9) & 7)
		{
		case 0:
			info.parallelInstr = DspParallelInstruction::lsl16;
			AddParam(info, d == 0 ? DspParameter::a : DspParameter::b);
			break;
		case 1:
			info.parallelInstr = DspParallelInstruction::mac;
			AddParam(info, d == 0 ? DspParameter::x1 : DspParameter::y1);
			AddParam(info, d == 0 ? DspParameter::x0 : DspParameter::y0);
			break;
		case 2:
			info.parallelInstr = DspParallelInstruction::lsr16;
			AddParam(info, d == 0 ? DspParameter::a : DspParameter::b);
			break;
		case 3:
			info.parallelInstr = DspParallelInstruction::macn;
			AddParam(info, d == 0 ? DspParameter::x1 : DspParameter::y1);
			AddParam(info, d == 0 ? DspParameter::x0 : DspParameter::y0);
			break;
		case 4:
		case 5:
			info.parallelInstr = DspParallelInstruction::addp;
			AddParam(info, d == 0 ? DspParameter::a : DspParameter::b);
			AddParam(info, s == 0 ? DspParameter::x1 : DspParameter::y1);
			break;
		case 6:
			info.parallelInstr = DspParallelInstruction::rnd;
			AddParam(info, d == 0 ? DspParameter::a : DspParameter::b);
			break;
		case 7:
			info.parallelInstr = DspParallelInstruction::rndp;
			AddParam(info, d == 0 ? DspParameter::a : DspParameter::b);
			break;
		}

		GroupMemOps4_F(info, instrBits);
	}

	void Decoder::GroupMpy(DecoderInfo& info, uint16_t instrBits)
	{
		static DspParameter mpy_regs[][2] = {
			{ DspParameter::Unknown, DspParameter::Unknown },
			{ DspParameter::Unknown, DspParameter::Unknown },
			{ DspParameter::x1, DspParameter::x0 },
			{ DspParameter::y1, DspParameter::y0 },
			{ DspParameter::x0, DspParameter::y0 },
			{ DspParameter::x0, DspParameter::y1 },
			{ DspParameter::x1, DspParameter::y0 },
			{ DspParameter::x1, DspParameter::y1 },
			{ DspParameter::a1, DspParameter::x1 },
			{ DspParameter::a1, DspParameter::y1 },
			{ DspParameter::b1, DspParameter::x1 },
			{ DspParameter::b1, DspParameter::y1 },
			{ DspParameter::Unknown, DspParameter::Unknown },
			{ DspParameter::Unknown, DspParameter::Unknown },
			{ DspParameter::Unknown, DspParameter::Unknown },
			{ DspParameter::Unknown, DspParameter::Unknown },
		};

		//|mpy s1,s2    |1sss s000 xxxx xxxx|
		//|rnmpy d,s1,s2|1sss s01d xxxx xxxx|
		//|admpy d,s1,s2|1sss s10d xxxx xxxx|
		//|mvmpy d,s1,s2|1sss s11d xxxx xxxx|

		int d = (instrBits & 0x100) ? 1 : 0;
		int s = (instrBits >> 11) & 0xf;

		switch ((instrBits >> 9) & 3)
		{
		case 0:
			if (d == 0)
			{
				info.parallelInstr = DspParallelInstruction::mpy;
				AddParam(info, mpy_regs[s][0]);
				AddParam(info, mpy_regs[s][1]);
			}
			break;
		case 1:
			info.parallelInstr = DspParallelInstruction::rnmpy;
			AddParam(info, d == 0 ? DspParameter::a : DspParameter::b);
			AddParam(info, mpy_regs[s][0]);
			AddParam(info, mpy_regs[s][1]);
			break;
		case 2:
			info.parallelInstr = DspParallelInstruction::admpy;
			AddParam(info, d == 0 ? DspParameter::a : DspParameter::b);
			AddParam(info, mpy_regs[s][0]);
			AddParam(info, mpy_regs[s][1]);
			break;
		case 3:
			info.parallelInstr = DspParallelInstruction::mvmpy;
			AddParam(info, d == 0 ? DspParameter::a : DspParameter::b);
			AddParam(info, mpy_regs[s][0]);
			AddParam(info, mpy_regs[s][1]);
			break;
		}

		GroupMemOps4_F(info, instrBits);
	}

	void Decoder::GroupMemOps3(DecoderInfo& info, uint16_t instrBits)
	{
		static DspParameter addressreg[] = {
			DspParameter::r0,
			DspParameter::r1,
			DspParameter::r2,
			DspParameter::r3,
		};

		static DspParameter modifier[] = {
			DspParameter::mod_none,
			DspParameter::mod_dec,
			DspParameter::mod_inc,
			DspParameter::mod_plus_m,
		};

		static DspParameter modifier_n[] = {
			DspParameter::mod_plus_m0,
			DspParameter::mod_plus_m1,
			DspParameter::mod_plus_m2,
			DspParameter::mod_plus_m3,
		};

		static DspParameter dstreg[] = {
			DspParameter::x0,
			DspParameter::y0,
			DspParameter::x1,
			DspParameter::y1,
		};

		static DspParameter srcreg[] = {
			DspParameter::a0,
			DspParameter::b0,
			DspParameter::a1,
			DspParameter::b1,
		};

		static DspParameter topreg[] = {
			DspParameter::x0,
			DspParameter::y0,
			DspParameter::x1,
			DspParameter::y1,
			DspParameter::a0,
			DspParameter::b0,
			DspParameter::a1,
			DspParameter::b1,
		};

		//|mr rn,mn               |0011 xxxx x000 mmrr|
		//|mv d,s                 |0011 xxxx x001 ddss|
		//|st rn,mn,s             |0011 xxxx x01s smrr|
		//|ld d,rn,mn             |0011 xxxx x1dd dmrr|

		switch ((instrBits >> 4) & 7)
		{
		case 0:		// mr
		{
			int r = instrBits & 3;
			int m = (instrBits >> 2) & 3;

			if (r == 0 && m == 0)
			{
				info.parallelMemInstr = DspParallelMemInstruction::nop;
			}
			else
			{
				info.parallelMemInstr = DspParallelMemInstruction::mr;
				AddParamEx(info, addressreg[r]);
				AddParamEx(info, modifier[m] == DspParameter::mod_plus_m ? modifier_n[r] : modifier[m]);
			}
			break;
		}
		case 1:		// mv
		{
			info.parallelMemInstr = DspParallelMemInstruction::mv;
			int d = (instrBits >> 2) & 3;
			AddParamEx(info, dstreg[d]);
			int s = instrBits & 3;
			AddParamEx(info, srcreg[s]);
			break;
		}
		case 2:		// st
		case 3:
		{
			info.parallelMemInstr = DspParallelMemInstruction::st;
			int r = instrBits & 3;
			AddParamEx(info, addressreg[r]);
			int m = (instrBits & 4) != 0 ? 1 : 0;
			AddParamEx(info, m == 0 ? DspParameter::mod_inc : modifier_n[r]);
			int s = (instrBits >> 3) & 3;
			AddParamEx(info, srcreg[s]);
			break;
		}
		case 4:		// ld
		case 5:
		case 6:
		case 7:
		{
			info.parallelMemInstr = DspParallelMemInstruction::ld;
			int d = (instrBits >> 3) & 7;
			AddParamEx(info, topreg[d]);
			int r = instrBits & 3;
			AddParamEx(info, addressreg[r]);
			int m = (instrBits & 4) != 0 ? 1 : 0;
			AddParamEx(info, m == 0 ? DspParameter::mod_inc : modifier_n[r]);
			break;
		}
		}
	}

	void Decoder::GroupMemOps4_F(DecoderInfo& info, uint16_t instrBits)
	{
		static DspParameter addressreg[] = {
			DspParameter::r0,
			DspParameter::r1,
			DspParameter::r2,
			DspParameter::r3,
		};

		static DspParameter modifier[] = {
			DspParameter::mod_plus_m0,
			DspParameter::mod_plus_m1,
			DspParameter::mod_plus_m2,
			DspParameter::mod_plus_m3,
		};

		static DspParameter dstreg[] = {
			DspParameter::x0,
			DspParameter::y0,
			DspParameter::x1,
			DspParameter::y1,
		};

		static DspParameter ldd_regs[][2] = {
			{ DspParameter::x0, DspParameter::y0 },
			{ DspParameter::x0, DspParameter::y1 },
			{ DspParameter::x1, DspParameter::y0 },
			{ DspParameter::x1, DspParameter::y1 },
		};

		if (instrBits & 0x80)
		{
			//|ls d,r,m r,m,s         |01xx xxxx 10dd mn0s|
			//|ls d,r,m r,m,s         |1xxx xxxx 10dd mn0s|
			//|ls2 d,r,m r,m,s        |01xx xxxx 10dd mn1s|
			//|ls2 d,r,m r,m,s        |1xxx xxxx 10dd mn1s|

			if ((instrBits & 0x40) == 0)
			{
				int d = (instrBits >> 4) & 3;
				int m = (instrBits & 8) ? 1 : 0;
				int n = (instrBits & 4) ? 1 : 0;
				int s = instrBits & 1;

				info.parallelMemInstr = DspParallelMemInstruction::ls;

				if ((instrBits & 2) == 0)		// ls
				{
					// Load by r0, store by r3
					// d, r0, n, r3, m, s

					AddParamEx(info, dstreg[d]);
					AddParamEx(info, DspParameter::r0);
					AddParamEx(info, n == 0 ? DspParameter::mod_inc : DspParameter::mod_plus_m0);

					AddParamEx(info, DspParameter::r3);
					AddParamEx(info, m == 0 ? DspParameter::mod_inc : DspParameter::mod_plus_m3);
					AddParamEx(info, s == 0 ? DspParameter::a1 : DspParameter::b1);
				}
				else		// ls2
				{
					// Load by r3, store by r0
					// d, r3, m, r0, n, s

					AddParamEx(info, dstreg[d]);
					AddParamEx(info, DspParameter::r3);
					AddParamEx(info, m == 0 ? DspParameter::mod_inc : DspParameter::mod_plus_m3);

					AddParamEx(info, DspParameter::r0);
					AddParamEx(info, n == 0 ? DspParameter::mod_inc : DspParameter::mod_plus_m0);
					AddParamEx(info, s == 0 ? DspParameter::a1 : DspParameter::b1);
				}
			}
			else
			{
				//|ldd d1,rn,mn d2,r3,m3|1xxx xxxx 11dd mnrr|-|-|-|-|-|-|Load dual data (Form 1a)|1|
				//|ldd d1,rn,mn d2,r3,m3|01xx xxxx 11dd mnrr|-|-|-|-|-|-|Load dual data (Form 1b)|1|
				//|ldd2 d1,rn,mn d2,r3,m3|1xxx xxxx 11rd mn11|-|-|-|-|-|-|Load dual data (Form 2)|1|

				int d = (instrBits >> 4) & 3;
				int m = (instrBits & 8) ? 1 : 0;
				int n = (instrBits & 4) ? 1 : 0;
				int r = instrBits & 3;

				info.parallelMemInstr = DspParallelMemInstruction::ldd;

				if (r == 3 && (instrBits & 0x8000) != 0)
				{
					// ldd2

					r = (instrBits & 0x20) ? 1 : 0;
					d = (instrBits & 0x10) ? 1 : 0;

					AddParamEx(info, d == 0 ? DspParameter::x1 : DspParameter::y1);
					AddParamEx(info, addressreg[r]);
					AddParamEx(info, n == 0 ? DspParameter::mod_inc : modifier[r]);
					AddParamEx(info, d == 0 ? DspParameter::x0 : DspParameter::y0);
					AddParamEx(info, DspParameter::r3);
					AddParamEx(info, m == 0 ? DspParameter::mod_inc : DspParameter::mod_plus_m3);
				}
				else
				{
					// ldd

					AddParamEx(info, ldd_regs[d][0]);
					AddParamEx(info, addressreg[r]);
					AddParamEx(info, n == 0 ? DspParameter::mod_inc : modifier[r]);
					AddParamEx(info, ldd_regs[d][1]);
					AddParamEx(info, DspParameter::r3);
					AddParamEx(info, m == 0 ? DspParameter::mod_inc : DspParameter::mod_plus_m3);
				}
			}
		}
		else
		{
			//|mr rn,mn               |01xx xxxx 0000 mmrr|
			//|mv d,s                 |01xx xxxx 0001 ddss|
			//|st rn,mn,s             |01xx xxxx 001s smrr|
			//|ld d,rn,mn             |01xx xxxx 01dd dmrr|

			GroupMemOps3(info, instrBits);
		}
	}

	void Decoder::Decode(uint8_t* instrPtr, size_t instrMaxSize, DecoderInfo& info)
	{
		// Reset info

		ResetInfo(info);

		// Get opcode and determine its group

		uint16_t instrBits = _BYTESWAP_UINT16(*(uint16_t*)instrPtr);

		AddBytes(instrPtr, sizeof(uint16_t), info);

		instrPtr += sizeof(instrBits);
		instrMaxSize -= sizeof(instrBits);

		int group = (instrBits >> 12);

		// Select by group. Groups 3-F are parallel instructions.

		info.parallel = group >= 3;

		// Group mixed multiply (8-F)?

		if ((instrBits & 0x8000) != 0)
		{
			int s = (instrBits >> 11) & 0xf;

			if (s >= 2 && s <= 0xb)
			{
				switch ((instrBits >> 8) & 7)
				{
				case 0:
				case 2:
				case 3:
				case 4:
				case 5:
				case 6:
				case 7:
					GroupMpy(info, instrBits);
					return;
				}
			}
		}

		// Other

		switch (group)
		{
		case 0:
			Group0(instrPtr, instrMaxSize, info, instrBits);
			break;
		case 1:
			Group1(instrPtr, instrMaxSize, info, instrBits);
			break;
		case 2:
			Group2(info, instrBits);
			break;
		case 3:
			Group3(info, instrBits);
			break;
		case 4:
		case 5:
		case 6:
			Group4_6(info, instrBits);
			break;
		case 7:
			Group7(info, instrBits);
			break;
		case 8:
			Group8(info, instrBits);
			break;
		case 9:
		case 0xa:
		case 0xb:
			Group9_B(info, instrBits);
			break;
		case 0xc:
		case 0xd:
			GroupCD(info, instrBits);
			break;
		case 0xe:
			GroupE(info, instrBits);
			break;
		case 0xf:
			GroupF(info, instrBits);
			break;
		}
	}

}
