#include "Emulator.h"
#include "Linker.h"
#include <stdio.h>

Emulator::Emulator(Linker* linker)
	: mLinker(linker)
{
	for (int i = 0; i < 0x10000; i++)
		mMemory[i] = 0;
	mJiffies = true;
}


Emulator::~Emulator(void)
{

}

static const uint8 STATUS_SIGN = 0x80;
static const uint8 STATUS_OVERFLOW = 0x40;
static const uint8 STATUS_ZERO = 0x02;
static const uint8 STATUS_CARRY = 0x01;

void Emulator::UpdateStatus(uint8 result)
{
	mRegP &= ~(STATUS_ZERO | STATUS_SIGN);
	if (result == 0) mRegP |= STATUS_ZERO;
	if (result & 0x80) mRegP |= STATUS_SIGN;
}

void Emulator::UpdateStatusCarry(uint8 result, bool carry) 
{
	mRegP &= ~(STATUS_ZERO | STATUS_SIGN | STATUS_CARRY);
	if (result == 0) mRegP |= STATUS_ZERO;
	if (result & 0x80) mRegP |= STATUS_SIGN;
	if (carry)
		mRegP |= STATUS_CARRY;
}

void Emulator::DumpCycles(void)
{
	int		numTops	= 0;
	int		topIP[101], topCycles[101];

	int		totalCycles = 0;
	
	GrowingArray<LinkerObject*> lobjs(nullptr);
	GrowingArray<int>			lobjc(0);

	for (int i = 0; i < 0x10000; i++)
	{
		int	cycles = mCycles[i];
		totalCycles += cycles;
		if (cycles > 0)
		{
			if (mLinker)
			{
				LinkerObject* lobj = mLinker->FindObjectByAddr(i);
				if (lobj)
				{
					lobjs[lobj->mID] = lobj;
					lobjc[lobj->mID] += cycles;
				}
			}

			int j = numTops;
			while (j > 0 && topCycles[j-1] < cycles)
			{
				topCycles[j] = topCycles[j - 1];
				topIP[j] = topIP[j - 1];
				j--;
			}
			topCycles[j] = cycles;
			topIP[j] = i;

			if (numTops < 40)
				numTops++;
		}
	}
	
	printf("Total Cycles %d\n", totalCycles);
//	return;

	for (int i = 0; i < numTops; i++)
	{
		printf("  %2d : %04x : %d\n", i, topIP[i], topCycles[i]);
	}

	numTops = 0;
	for (int i = 0; i < lobjc.Size(); i++)
	{
		int	cycles = lobjc[i];
		if (cycles > 0)
		{
			int j = numTops;
			while (j > 0 && topCycles[j - 1] < cycles)
			{
				topCycles[j] = topCycles[j - 1];
				topIP[j] = topIP[j - 1];
				j--;
			}
			topCycles[j] = cycles;
			topIP[j] = i;

			if (numTops < 40)
				numTops++;
		}

	}

	for (int i = 0; i < numTops; i++)
	{
		if (lobjs[topIP[i]]->mIdent)
			printf("  %2d : %s : %d\n", i, lobjs[topIP[i]]->mIdent->mString, topCycles[i]);
	}
}

bool Emulator::EmulateInstruction(AsmInsType type, AsmInsMode mode, int addr, int & cycles, bool cross, bool indexed)
{
	int	t;

	switch (type)
	{
	case ASMIT_ADC:
		if (mode != ASMIM_IMMEDIATE)
			addr = ReadMemory(addr);
		t = mRegA + addr + (mRegP & STATUS_CARRY);

		mRegP = 0;

		if ((mRegA & 0x80) && (addr & 0x80) && !(t & 0x80) ||
			!(mRegA & 0x80) && !(addr & 0x80) && (t & 0x80))
			mRegP |= STATUS_OVERFLOW;

		mRegA = (t & 255);
		UpdateStatusCarry(mRegA, t >= 256);
		if (cross) cycles++;
		break;
	case ASMIT_AND:
		if (mode != ASMIM_IMMEDIATE)
			addr = ReadMemory(addr);
		mRegA &= addr;
		UpdateStatus(mRegA);
		if (cross) cycles++;
		break;
	case ASMIT_ASL:
		if (mode == ASMIM_IMPLIED)
		{
			t = mRegA << 1;
			mRegA = (t & 255);
			UpdateStatusCarry(mRegA, t >= 256);
		}
		else
		{
			t = ReadMemory(addr) << 1;
			WriteMemory(addr, t & 255);
			UpdateStatusCarry(t & 255, t >= 256);
			cycles += 2;
			if (indexed) cycles++;
		}
		break;
	case ASMIT_BCC:
		if (!(mRegP & STATUS_CARRY))
		{
			mIP = addr;
			cycles++;
		}
		break;
	case ASMIT_BCS:
		if ((mRegP & STATUS_CARRY))
		{
			mIP = addr;
			cycles++;
		}
		break;
	case ASMIT_BEQ:
		if ((mRegP & STATUS_ZERO))
		{
			mIP = addr;
			cycles++;
		}
		break;
	case ASMIT_BIT:
		t = ReadMemory(addr);
		mRegP &= ~(STATUS_ZERO | STATUS_SIGN | STATUS_OVERFLOW);
		if (t & 0x80) mRegP |= STATUS_SIGN;
		if (t & 0x40) mRegP |= STATUS_OVERFLOW;
		if (!(t & mRegA)) mRegP |= STATUS_ZERO;
		break;
	case ASMIT_BMI:
		if ((mRegP & STATUS_SIGN))
		{
			mIP = addr;
			cycles++;
		}
		break;
	case ASMIT_BNE:
		if (!(mRegP & STATUS_ZERO))
		{
			mIP = addr;
			cycles++;
		}
		break;
	case ASMIT_BPL:
		if (!(mRegP & STATUS_SIGN))
		{
			mIP = addr;
			cycles++;
		}
		break;
	case ASMIT_BRK:
		return false;
		break;
	case ASMIT_BVC:
		if (!(mRegP & STATUS_OVERFLOW))
		{
			mIP = addr;
			cycles++;
		}
		break;
	case ASMIT_BVS:
		if ((mRegP & STATUS_OVERFLOW))
		{
			mIP = addr;
			cycles++;
		}
		break;
	case ASMIT_CLC:
		mRegP &= ~STATUS_CARRY;
		break;
	case ASMIT_CLD:
		break;
	case ASMIT_CLI:
		break;
	case ASMIT_CLV:
		mRegP &= ~STATUS_OVERFLOW;
		break;
	case ASMIT_CMP:
		if (mode != ASMIM_IMMEDIATE)
			addr = ReadMemory(addr);
		t = mRegA + (addr ^ 0xff) + 1;

		mRegP = 0;

		if ((mRegA & 0x80) && !(addr & 0x80) && !(t & 0x80) ||
			!(mRegA & 0x80) && (addr & 0x80) && (t & 0x80))
			mRegP |= STATUS_OVERFLOW;

		UpdateStatusCarry(t & 255, t >= 256);
		if (cross) cycles++;
		break;
	case ASMIT_CPX:
		if (mode != ASMIM_IMMEDIATE)
			addr = ReadMemory(addr);
		t = mRegX + (addr ^ 0xff) + 1;

		mRegP = 0;

		if ((mRegX & 0x80) && !(addr & 0x80) && !(t & 0x80) ||
			!(mRegX & 0x80) && (addr & 0x80) && (t & 0x80))
			mRegP |= STATUS_OVERFLOW;

		UpdateStatusCarry(t & 255, t >= 256);
		break;
	case ASMIT_CPY:
		if (mode != ASMIM_IMMEDIATE)
			addr = ReadMemory(addr);
		t = mRegY + (addr ^ 0xff) + 1;

		mRegP = 0;

		if ((mRegY & 0x80) && !(addr & 0x80) && !(t & 0x80) ||
			!(mRegY & 0x80) && (addr & 0x80) && (t & 0x80))
			mRegP |= STATUS_OVERFLOW;

		UpdateStatusCarry(t & 255, t >= 256);
		break;
	case ASMIT_DEC:
		if (mode == ASMIM_IMPLIED)
		{
			t = mRegA - 1;
			mRegA = (t & 255);
			UpdateStatus(mRegA);
		}
		else
		{
			t = ReadMemory(addr) - 1;
			WriteMemory(addr, t & 255);
			UpdateStatus(t & 255);
			cycles += 2;
			if (indexed) cycles++;
		}
		break;
	case ASMIT_DEX:
		t = mRegX - 1;
		mRegX = (t & 255);
		UpdateStatus(mRegX);
		break;
	case ASMIT_DEY:
		t = mRegY - 1;
		mRegY = (t & 255);
		UpdateStatus(mRegY);
		break;
	case ASMIT_EOR:
		if (mode != ASMIM_IMMEDIATE)
			addr = ReadMemory(addr);
		mRegA ^= addr;
		UpdateStatus(mRegA);
		if (cross) cycles++;
		break;
	case ASMIT_INC:
		if (mode == ASMIM_IMPLIED)
		{
			t = mRegA + 1;
			mRegA = (t & 255);
			UpdateStatus(mRegA);
		}
		else
		{
			t = ReadMemory(addr) + 1;
			WriteMemory(addr, t & 255);
			UpdateStatus(t & 255);
			cycles += 2;
			if (indexed) cycles++;
		}
		break;
	case ASMIT_INX:
		t = mRegX + 1;
		mRegX = (t & 255);
		UpdateStatus(mRegX);
		break;
	case ASMIT_INY:
		t = mRegY + 1;
		mRegY = (t & 255);
		UpdateStatus(mRegY);
		break;
	case ASMIT_JMP:
		mIP = addr;
		break;
	case ASMIT_JSR:
		mMemory[0x100 + mRegS] = (mIP - 1) >> 8;
		mRegS--;
		mMemory[0x100 + mRegS] = (mIP - 1) & 0xff;
		mCalls[mRegS] = true;
		mRegS--;
		mIP = addr;
		cycles += 2;
		break;
	case ASMIT_LDA:
		if (mode != ASMIM_IMMEDIATE)
			addr = ReadMemory(addr);
		mRegA = addr;
		UpdateStatus(mRegA);
		if (cross) cycles++;
		break;
	case ASMIT_LDX:
		if (mode != ASMIM_IMMEDIATE)
			addr = ReadMemory(addr);
		mRegX = addr;
		UpdateStatus(mRegX);
		if (cross) cycles++;
		break;
	case ASMIT_LDY:
		if (mode != ASMIM_IMMEDIATE)
			addr = ReadMemory(addr);
		mRegY = addr;
		UpdateStatus(mRegY);
		if (cross) cycles++;
		break;
	case ASMIT_LSR:
		if (mode == ASMIM_IMPLIED)
		{
			int	c = mRegA & 1;
			t = mRegA >> 1;
			mRegA = (t & 255);
			UpdateStatusCarry(mRegA, c != 0);
		}
		else
		{
			t = ReadMemory(addr);
			int	c = t & 1;
			t >>= 1;
			WriteMemory(addr, t & 255);
			UpdateStatusCarry(t & 255, c != 0);
			cycles += 2;
			if (indexed) cycles++;
		}
		break;
	case ASMIT_NOP:
		break;
	case ASMIT_ORA:
		if (mode != ASMIM_IMMEDIATE)
			addr = ReadMemory(addr);
		mRegA |= addr;
		UpdateStatus(mRegA);
		if (cross) cycles++;
		break;
	case ASMIT_PHA:
		mCalls[mRegS] = false;
		mMemory[0x100 + mRegS] = mRegA;
		mRegS--;
		cycles ++;
		break;
	case ASMIT_PHP:
		mCalls[mRegS] = false;
		mMemory[0x100 + mRegS] = mRegP;
		mRegS--;
		cycles++;
		break;
	case ASMIT_PLA:
		mRegS++;
		mRegA = mMemory[0x100 + mRegS];
		cycles++;
		break;
	case ASMIT_PLP:
		mRegS++;
		mRegP = mMemory[0x100 + mRegS];
		cycles++;
		break;
	case ASMIT_ROL:
		if (mode == ASMIM_IMPLIED)
		{
			t = (mRegA << 1) | (mRegP & STATUS_CARRY);
			mRegA = (t & 255);
			UpdateStatusCarry(mRegA, t >= 256);
		}
		else
		{
			t = (ReadMemory(addr) << 1) | (mRegP & STATUS_CARRY);;
			WriteMemory(addr, t & 255);
			UpdateStatusCarry(t & 255, t >= 256);
			cycles+=2;
			if (indexed) cycles++;
		}
		break;
	case ASMIT_ROR:
		if (mode == ASMIM_IMPLIED)
		{
			int	c = mRegA & 1;
			t = (mRegA >> 1) | ((mRegP & STATUS_CARRY) << 7);
			mRegA = (t & 255);
			UpdateStatusCarry(mRegA, c != 0);
		}
		else
		{
			t = ReadMemory(addr);
			int	c = t & 1;
			t = (t >> 1) | ((mRegP & STATUS_CARRY) << 7);
			WriteMemory(addr, t & 255);
			UpdateStatusCarry(t & 255, c != 0);
			cycles += 2;
			if (indexed) cycles++;
		}
		break;
	case ASMIT_RTI:
		break;
	case ASMIT_RTS:
		mIP = (mMemory[0x101 + mRegS] + 256 * mMemory[0x102 + mRegS] + 1) & 0xffff;
		mRegS += 2;
		cycles += 4;
		break;
	case ASMIT_SBC:
		if (mode != ASMIM_IMMEDIATE)
			addr = ReadMemory(addr);
		t = mRegA + (addr ^ 0xff) + (mRegP & STATUS_CARRY);

		mRegP = 0;

		if ((mRegA & 0x80) && !(addr & 0x80) && !(t & 0x80) ||
			!(mRegA & 0x80) && (addr & 0x80) && (t & 0x80))
			mRegP |= STATUS_OVERFLOW;

		mRegA = (t & 255);
		UpdateStatusCarry(t & 255, t >= 256);
		if (cross) cycles++;
		break;
	case ASMIT_SEC:
		mRegP |= STATUS_CARRY;
		break;
	case ASMIT_SED:
		break;
	case ASMIT_SEI:
		break;
	case ASMIT_STA:
		WriteMemory(addr, mRegA);
		if (indexed) cycles++;
		break;
	case ASMIT_STX:
		WriteMemory(addr, mRegX);
		break;
	case ASMIT_STY:
		WriteMemory(addr, mRegY);
		break;
	case ASMIT_TAX:
		mRegX = mRegA;
		UpdateStatus(mRegX);
		break;
	case ASMIT_TAY:
		mRegY = mRegA;
		UpdateStatus(mRegY);
		break;
	case ASMIT_TSX:
		mRegX = mRegS;
		UpdateStatus(mRegX);
		break;
	case ASMIT_TXA:
		mRegA = mRegX;
		UpdateStatus(mRegA);
		break;
	case ASMIT_TXS:
		mRegS = mRegX;
		break;
	case ASMIT_TYA:
		mRegA = mRegY;
		UpdateStatus(mRegA);
		break;
	case ASMIT_INV:
		return false;
		break;
	}
		
	return true;
}

void Emulator::DumpProfile(void)
{
	DumpCycles();
}

void Emulator::DumpCallstack(void)
{
	int	ip = mIP;
	int sp = mRegS;

	printf("Callstack:\n");
	while (sp < 0xfd)
	{
		printf("%02x [%04x] : ", sp, ip);

		const LinkerObject* lobj = nullptr;
		if (mLinker)
		{
			lobj = mLinker->FindObjectByAddr(ip);

			const Location* loc = &(lobj->mLocation);

			if (lobj && lobj->mCodeLocations.Size())
			{
				int j = 0;
				int	rip = ip - lobj->mAddress;
				while (j < lobj->mCodeLocations.Size() && (rip < lobj->mCodeLocations[j].mStart || rip >= lobj->mCodeLocations[j].mEnd))
					j++;

				if (j < lobj->mCodeLocations.Size())
					loc = &(lobj->mCodeLocations[j].mLocation);
			}

			while (loc)
			{
				printf("(%s,%d) ", loc->mFileName, loc->mLine);
				loc = loc->mFrom;
			}
		}
		printf("\n");

		while (sp < 0xfd && !mCalls[sp])
			sp++;
		ip = (mMemory[0x100 + sp] + 256 * mMemory[0x101 + sp] - 2) & 0xffff;
		sp += 2;
	}
}

uint8 Emulator::ReadMemory(uint16 addr)
{
	if (mUseIORange && addr >= 0xdd80 && addr < 0xde00)
	{
		switch (addr & 15)
		{
		case 0:
			return mIORange.mCount0++;
		case 1:
			return mIORange.mCount1++;
		case 2:
			return mIORange.mDMAAddress & 0xff;
		case 3:
			return mIORange.mDMAAddress >> 8;
		case 4:
			return mMemory[mIORange.mDMAAddress++];
		case 5:
		case 6:
		case 7:
			return mIORange.mMirror;
		case 8:
		case 9:
		case 10:
		case 11:
			return mIORange.mCycleCount >> 8 * (addr & 3);
		case 12:
		case 13:
		case 14:
		case 15:
			return mIORange.mScratch[addr & 3];
		default:
			return 0;
		}
	}
	else
		return mMemory[addr];
}

void Emulator::WriteMemory(uint16 addr, uint8 data)
{
	if (mUseIORange && addr >= 0xdd80 && addr < 0xde00)
	{
		switch (addr & 15)
		{
		case 0:
			mIORange.mCount0++;
			break;
		case 1:
			mIORange.mCount1++;
			break;
		case 2:
			mIORange.mDMAAddress = (mIORange.mDMAAddress & 0xff00) | data;
			break;
		case 3:
			mIORange.mDMAAddress = (mIORange.mDMAAddress & 0x00ff) | (data << 8);
			break;
		case 4:
			mMemory[mIORange.mDMAAddress++] = data;
			break;
		case 5:
		case 6:
		case 7:
			mIORange.mMirror = data;;
			break;
		case 8:
		case 9:
		case 10:
		case 11:
			mIORange.mCycleCount = mCycleCount;
			break;
		case 12:
		case 13:
		case 14:
		case 15:
			mIORange.mScratch[addr & 3] = data;
			break;
		}
	}
	else
		mMemory[addr] = data;
}

int Emulator::Emulate(int startIP, int exitIP, int trace, bool iorange)
{
	mUseIORange = iorange;
	mIORange.mCount0 = mIORange.mCount1 = 0;
	mIORange.mMirror = 0;
	mIORange.mScratch[0] = mIORange.mScratch[1] = mIORange.mScratch[2] =mIORange.mScratch[3] = 0;

	mCycleCount = 0;
	for (int i = 0; i < 0x10000; i++)
		mCycles[i] = 0;
	for (int i = 0; i < 0x100; i++)
		mCalls[i] = false;

	if (exitIP)
	{
		while (mMemory[exitIP] != 0x9a)
			exitIP++;
	}

	mExitIP = exitIP;
	mIP = startIP;
	mRegA = 0;
	mRegX = 0;
	mRegY = 0;
	mRegP = 0;
	mRegS = 0xfd;

	mMemory[0x1fe] = 0xff;
	mMemory[0x1ff] = 0xff;

	mCycleCount = 0;

	int		tcycles = 0;
	int		iip = 0;
	while (mIP != 0)
	{
		if (mJiffies)
		{
			if (mCycleCount >= tcycles + 16667)
			{
				mMemory[0xa2]++;
				if (!mMemory[0xa2])
				{
					mMemory[0xa1]++;
					if (!mMemory[0xa1])
					{
						mMemory[0xa0]++;
					}
				}
				tcycles += 16667;
			}
		}

		if (mIP == mExitIP)
		{
			if (mMemory[BC_REG_ACCU + 1] & 0x80)
				DumpCallstack();
		}
		else if (mIP == 0xffd2)
		{
			if (mRegA == 13)
				putchar('\n');
			else
				putchar(mRegA);
			mIP = mMemory[0x101 + mRegS] + 256 * mMemory[0x102 + mRegS] + 1;
			mRegS += 2;
		}
		else if (mIP == 0xffcf)
		{
			int ch = getchar();
			mRegA = ch;
			mIP = mMemory[0x101 + mRegS] + 256 * mMemory[0x102 + mRegS] + 1;
			mRegS += 2;
		}
		else if (mIP == 0xff81)
		{
			printf("------------------ CLEAR ---------------\n");
			mIP = mMemory[0x101 + mRegS] + 256 * mMemory[0x102 + mRegS] + 1;
			mRegS += 2;
		}

		uint8		opcode = mMemory[mIP];
		AsmInsData	d = DecInsData[opcode];
		int			addr = 0, taddr;
		int			ip = mIP;
		
		if (ip == 0x0862)
			iip = mMemory[BC_REG_IP] + 256 * mMemory[BC_REG_IP + 1] + mRegY;

		bool		cross = false, indexed = false;
		int			icycles = 0;

		mIP++;
		switch (d.mMode)
		{
			case ASMIM_IMPLIED:
				if (trace & TRACEF_NATIVE)
					printf("%04x : %04x %02x __ __ %s         (A:%02x X:%02x Y:%02x P:%02x S:%02x)\n", iip, ip, mMemory[ip], AsmInstructionNames[d.mType], mRegA, mRegX, mRegY, mRegP, mRegS);
				icycles = 2;
				break;
			case ASMIM_IMMEDIATE:
				addr = mMemory[mIP++];
				if (trace & TRACEF_NATIVE)
					printf("%04x : %04x %02x %02x __ %s #$%02x    (A:%02x X:%02x Y:%02x P:%02x S:%02x)\n", iip, ip, mMemory[ip], mMemory[ip+1], AsmInstructionNames[d.mType], addr, mRegA, mRegX, mRegY, mRegP, mRegS);
				icycles = 2;
				break;
			case ASMIM_ZERO_PAGE:
				addr = mMemory[mIP++];
				if (trace & TRACEF_NATIVE)
					printf("%04x : %04x %02x %02x __ %s $%02x     (A:%02x X:%02x Y:%02x P:%02x S:%02x M:%02x)\n", iip, ip, mMemory[ip], mMemory[ip + 1], AsmInstructionNames[d.mType], addr, mRegA, mRegX, mRegY, mRegP, mRegS, mMemory[addr]);
				icycles = 3;
				break;
			case ASMIM_ZERO_PAGE_X:
				taddr = mMemory[mIP++];
				addr = (taddr + mRegX) & 0xff;
				if (trace & TRACEF_NATIVE)
					printf("%04x : %04x %02x %02x __ %s $%02x,x   (A:%02x X:%02x Y:%02x P:%02x S:%02x %04x M:%02x)\n", iip, ip, mMemory[ip], mMemory[ip + 1], AsmInstructionNames[d.mType], taddr, mRegA, mRegX, mRegY, mRegP, mRegS, addr, mMemory[addr]);
				icycles = 4;
				break;
			case ASMIM_ZERO_PAGE_Y:
				taddr = mMemory[mIP++];
				addr = (taddr + mRegY) & 0xff;
				if (trace & TRACEF_NATIVE)
					printf("%04x : %04x %02x %02x __ %s $%02x,y   (A:%02x X:%02x Y:%02x P:%02x S:%02x %04x M:%02x)\n", iip, ip, mMemory[ip], mMemory[ip + 1], AsmInstructionNames[d.mType], taddr, mRegA, mRegX, mRegY, mRegP, mRegS, addr, mMemory[addr]);
				icycles = 4;
				break;
			case ASMIM_ABSOLUTE:
				addr = mMemory[mIP] + 256 * mMemory[mIP + 1];
				if (trace & TRACEF_NATIVE)
					printf("%04x : %04x %02x %02x %02x %s $%04x   (A:%02x X:%02x Y:%02x P:%02x S:%02x M:%02x)\n", iip, ip, mMemory[ip], mMemory[ip + 1], mMemory[ip + 2], AsmInstructionNames[d.mType], addr, mRegA, mRegX, mRegY, mRegP, mRegS, mMemory[addr]);
				mIP += 2;
				icycles = 4;
				break;
			case ASMIM_ABSOLUTE_X:
				taddr = mMemory[mIP] + 256 * mMemory[mIP + 1];
				addr = (taddr + mRegX) & 0xffff;
				cross = mMemory[mIP] + mRegX >= 256;
				indexed = true;
				if (trace & TRACEF_NATIVE)
					printf("%04x : %04x %02x %02x %02x %s $%04x,x (A:%02x X:%02x Y:%02x P:%02x S:%02x %04x M:%02x)\n", iip, ip, mMemory[ip], mMemory[ip + 1], mMemory[ip + 2], AsmInstructionNames[d.mType], taddr, mRegA, mRegX, mRegY, mRegP, mRegS, addr, mMemory[addr]);
				mIP += 2;
				icycles = 4;
				break;
			case ASMIM_ABSOLUTE_Y:
				taddr = mMemory[mIP] + 256 * mMemory[mIP + 1];
				addr = (taddr + mRegY) & 0xffff;
				cross = mMemory[mIP] + mRegY >= 256;
				indexed = true;
				if (trace & TRACEF_NATIVE)
					printf("%04x : %04x %02x %02x %02x %s $%04x,y (A:%02x X:%02x Y:%02x P:%02x S:%02x %04x M:%02x)\n", iip, ip, mMemory[ip], mMemory[ip + 1], mMemory[ip + 2], AsmInstructionNames[d.mType], taddr, mRegA, mRegX, mRegY, mRegP, mRegS, addr, mMemory[addr]);
				mIP += 2;
				icycles = 4;
				break;
			case ASMIM_INDIRECT:
				taddr = mMemory[mIP] + 256 * mMemory[mIP + 1];
				mIP += 2;
				addr = mMemory[taddr] + 256 * mMemory[taddr + 1];
				if (trace & TRACEF_NATIVE)
					printf("%04x : %04x %02x %02x %02x %s ($%04x) (A:%02x X:%02x Y:%02x P:%02x S:%02x %04x)\n", iip, ip, mMemory[ip], mMemory[ip + 1], mMemory[ip + 2], AsmInstructionNames[d.mType], taddr, mRegA, mRegX, mRegY, mRegP, mRegS, addr);
				icycles = 6;
				break;
			case ASMIM_INDIRECT_X:
				taddr = (mMemory[mIP++] + mRegX) & 0xff;
				addr = mMemory[taddr] + 256 * mMemory[taddr + 1];
				if (trace & TRACEF_NATIVE)
					printf("%04x : %04x %02x %02x __ %s ($%02x,x) (A:%02x X:%02x Y:%02x P:%02x S:%02x %02x %04x M:%02x)\n", iip, ip, mMemory[ip], mMemory[ip + 1], AsmInstructionNames[d.mType], mMemory[ip + 1], mRegA, mRegX, mRegY, mRegP, mRegS, taddr, addr, mMemory[addr]);
				icycles = 6;
				break;
			case ASMIM_INDIRECT_Y:
				taddr = mMemory[mIP++];
				addr = (mMemory[taddr] + 256 * mMemory[taddr + 1] + mRegY) & 0xffff;
				cross = mMemory[taddr] + mRegY >= 256;
				indexed = true;
				if (trace & TRACEF_NATIVE)
					printf("%04x : %04x %02x %02x __ %s ($%02x),y (A:%02x X:%02x Y:%02x P:%02x S:%02x %04x M:%02x)\n", iip, ip, mMemory[ip], mMemory[ip + 1], AsmInstructionNames[d.mType], taddr, mRegA, mRegX, mRegY, mRegP, mRegS, addr, mMemory[addr]);
				icycles = 5;
				break;
			case ASMIM_RELATIVE:
				taddr = mMemory[mIP++];
				if (taddr & 0x80)
					addr = taddr + mIP - 256;
				else
					addr = taddr + mIP;
				if (trace & TRACEF_NATIVE)
					printf("%04x : %04x %02x %02x __ %s $%02x     (A:%02x X:%02x Y:%02x P:%02x S:%02x %04x)\n", iip, ip, mMemory[ip], mMemory[ip + 1], AsmInstructionNames[d.mType], taddr, mRegA, mRegX, mRegY, mRegP, mRegS, addr);
				icycles = 2;
				break;
		}

		if ((trace & TRACEF_BYTECODE) && ip == 0x0862)
		{
			unsigned	accu = mMemory[BC_REG_ACCU] + (mMemory[BC_REG_ACCU + 1] << 8) + (mMemory[BC_REG_ACCU + 2] << 16) + (mMemory[BC_REG_ACCU + 3] << 24);
			int	ptr = mMemory[BC_REG_ADDR] + 256 * mMemory[BC_REG_ADDR + 1];
			int	sp = mMemory[BC_REG_STACK] + 256 * mMemory[BC_REG_STACK + 1];
			printf("%04x  (A:%08x P:%04x S:%04x) %04x %04x %04x %04x  %04x %04x %04x %04x  %04x %04x %04x %04x  %04x %04x %04x %04x : %04x\n", addr, accu, ptr, sp,
				mMemory[BC_REG_TMP +  0] + 256 * mMemory[BC_REG_TMP +  1],
				mMemory[BC_REG_TMP +  2] + 256 * mMemory[BC_REG_TMP +  3],
				mMemory[BC_REG_TMP +  4] + 256 * mMemory[BC_REG_TMP +  5],
				mMemory[BC_REG_TMP +  6] + 256 * mMemory[BC_REG_TMP +  7],

				mMemory[BC_REG_TMP +  8] + 256 * mMemory[BC_REG_TMP +  9],
				mMemory[BC_REG_TMP + 10] + 256 * mMemory[BC_REG_TMP + 11],
				mMemory[BC_REG_TMP + 12] + 256 * mMemory[BC_REG_TMP + 13],
				mMemory[BC_REG_TMP + 14] + 256 * mMemory[BC_REG_TMP + 15],

				mMemory[BC_REG_TMP + 16] + 256 * mMemory[BC_REG_TMP + 17],
				mMemory[BC_REG_TMP + 18] + 256 * mMemory[BC_REG_TMP + 19],
				mMemory[BC_REG_TMP + 20] + 256 * mMemory[BC_REG_TMP + 21],
				mMemory[BC_REG_TMP + 22] + 256 * mMemory[BC_REG_TMP + 23],

				mMemory[BC_REG_TMP + 24] + 256 * mMemory[BC_REG_TMP + 25],
				mMemory[BC_REG_TMP + 26] + 256 * mMemory[BC_REG_TMP + 27],
				mMemory[BC_REG_TMP + 28] + 256 * mMemory[BC_REG_TMP + 29],
				mMemory[BC_REG_TMP + 30] + 256 * mMemory[BC_REG_TMP + 31],

				mMemory[0x9f9e] + 256 * mMemory[0x9f9f]


			);
		}

		if (!EmulateInstruction(d.mType, d.mMode, addr, icycles, cross, indexed))
		{
			if (exitIP)
				DumpCallstack();
			return -1;
		}

		mCycles[ip] += icycles;
		mCycleCount += icycles;
	}

	if (mRegS == 0xff)
	{
#if 0
		for (int i = 0; i < 256; i++)
			if (mMemory[i] != 0)
				printf("ZP %02x : %02x\n", i, mMemory[i]);
#endif
		return int16(mMemory[BC_REG_ACCU] + 256 * mMemory[BC_REG_ACCU + 1]);
	}

	return -1;
}
