#include "pch.h"
#include "FPU.h"

// чисто теоретическая модель
// ни одна функция не протестирована
// и что-то незнай как скомбинировать CPU и FPU

// тут такие недоделки:
// 1. выход при возникновении ошибки неправильный
// 2. сама обработка ошибок тоже неправильная.
// там надо прерывать выполнение и начинать обработку ошибочных ситуаций и выходить


CFPU::CFPU(CCPU *pCPU)
	: m_pCPU(pCPU)
{
	init();
	PrepareCPU();
}

CFPU::~CFPU()
{
	DoneCPU();
}

void CFPU::init()
{
	$FPSR = 0;
	$FEC = 0;
	$FEA = 0;
}


int CFPU::TranslateInstruction()
{
	m_nOldPC = m_pCPU->m_RON[CCPU::R_PC];
	m_instruction = m_pCPU->GetWord(m_nOldPC); // берём следующую инструкцию.
	m_pCPU->m_RON[CCPU::R_PC] += 2;
	register int t = m_instruction;
	m_nReg = t & 7;
	t >>= 3;
	m_nMeth = t & 7;
	t >>= 3;
	m_nAc = t & 3;
// тут ещё не доделано
	// Find command implementation using the command map
	(this->*(m_pExecuteMethodMap[m_instruction]))();  // Call command implementation method
	return 0;
}

uint16_t CFPU::GetArgAddr(int &len)
{
	uint16_t addr = 0;
	uint16_t index;

	switch (m_nMeth)
	{
		case 1: // режим адресации 1
			addr = m_pCPU->m_RON[m_nReg]; // адрес аргумента
			break;

		case 2: // режим адресации 2
			addr = m_pCPU->m_RON[m_nReg]; // адрес аргумента

			if (m_nReg == CCPU::R_PC && len == 4)
			{
				m_pCPU->m_RON[m_nReg] += 2;
				len = 2;
			}
			else
			{
				m_pCPU->m_RON[m_nReg] += len; // инкремент
			}

			break;

		case 3: // режим адресации 3
			addr = m_pCPU->GetWord(m_pCPU->m_RON[m_nReg]); // адрес аргумента
			m_pCPU->m_RON[m_nReg] += 2;
			break;

		case 4: // режим адресации 4
			m_pCPU->m_RON[m_nReg] -= len;
			addr = m_pCPU->m_RON[m_nReg]; // адрес аргумента
			break;

		case 5: // режим адресации 5
			m_pCPU->m_RON[m_nReg] -= 2;
			addr = m_pCPU->GetWord(m_pCPU->m_RON[m_nReg]);  // адрес аргумента
			break;

		case 6: // режим адресации 6
			index = m_pCPU->GetWord(m_pCPU->m_RON[CCPU::R_PC]);
			m_pCPU->m_RON[CCPU::R_PC] += 2;
			addr = uint16_t(index + m_pCPU->m_RON[m_nReg]);
			break;

		case 7: // режим адресации 7
			index = m_pCPU->GetWord(m_pCPU->m_RON[CCPU::R_PC]);
			m_pCPU->m_RON[CCPU::R_PC] += 2;
			addr = uint16_t(index + m_pCPU->m_RON[m_nReg]);
			addr = m_pCPU->GetWord(addr);
			break;
	}

	return addr;
}

// получение целого из памяти.
// вход: len - длина целого: 2 - 1 слово
//                          4 - 2 слова
// выход: результат, для len=2 младшее слово равно 0
uint32_t CFPU::GetInt(int len)
{
	uint32_t ret = 0;
	uint16_t addr;

	if (m_nMeth)
	{
		addr = GetArgAddr(len);
	}
	else
	{
		// режим адресации 0
		ret = uint32_t(m_pCPU->m_RON[m_nReg]) << 16; // get high-order word, clear low-order word
		return ret;
	}

	if (addr & 1)
	{
		BADFP();    // BADI по нечётному адресу аргумента
	}
	else
	{
		uint32_t hi = m_pCPU->GetWord(addr);
		uint32_t lo = 0;

		if (len == 4)
		{
			addr += 2;
			lo = m_pCPU->GetWord(addr);
		}

		ret = (hi << 16) | lo;
	}

	return ret;
}

uint32_t CFPU::GetI()
{
	return GetInt(2);
}

uint32_t CFPU::GetL()
{
	return GetInt(4);
}

uint32_t CFPU::GetSrc()
{
	if ($FPSR & $FL)
	{
		return GetL();
	}
	else
	{
		return GetI();
	}
}

uint64_t CFPU::GetX()
{
	uint64_t ret = 0;
	int len = ($FPSR & $FD) ? 8 : 4; // double:float
	uint16_t addr = 0, index;

	switch (m_nMeth)
	{
		case 0:
			if (m_nReg > 5)
			{
				BADFP();    // BADI - неверный номер аккумулятора
			}
			else
			{
				m_nAc = m_nReg;
				ret = $AC[m_nReg];
			}

			return ret;

		case 1:
			addr = m_pCPU->m_RON[m_nReg]; // адрес аргумента
			break;

		case 2: // режим адресации 2
			addr = m_pCPU->m_RON[m_nReg]; // адрес аргумента

			if (m_nReg == CCPU::R_PC)
			{
				m_pCPU->m_RON[m_nReg] += 2;
				len = 2;
				ret = uint64_t(m_pCPU->GetWord(addr)) << 48;
				return ret;
			}
			else
			{
				m_pCPU->m_RON[m_nReg] += len; // инкремент
			}

			break;

		case 3: // режим адресации 3
			addr = m_pCPU->GetWord(m_pCPU->m_RON[m_nReg]); // адрес аргумента
			m_pCPU->m_RON[m_nReg] += 2;
			break;

		case 4: // режим адресации 4
			m_pCPU->m_RON[m_nReg] -= len;
			addr = m_pCPU->m_RON[m_nReg]; // адрес аргумента
			break;

		case 5: // режим адресации 5
			m_pCPU->m_RON[m_nReg] -= 2;
			addr = m_pCPU->GetWord(m_pCPU->m_RON[m_nReg]);  // адрес аргумента
			break;

		case 6: // режим адресации 6
			index = m_pCPU->GetWord(m_pCPU->m_RON[CCPU::R_PC]);
			m_pCPU->m_RON[CCPU::R_PC] += 2;
			addr = uint16_t(index + m_pCPU->m_RON[m_nReg]);
			break;

		case 7: // режим адресации 7
			index = m_pCPU->GetWord(m_pCPU->m_RON[CCPU::R_PC]);
			m_pCPU->m_RON[CCPU::R_PC] += 2;
			addr = uint16_t(index + m_pCPU->m_RON[m_nReg]);
			addr = m_pCPU->GetWord(addr);
			break;
	}

	if (addr & 1)
	{
		BADFP();    // BADI по нечётному адресу аргумента
	}
	else
	{
		m_nDSTEA = addr;
		uint16_t w3 = m_pCPU->GetWord(addr); addr += 2;
		uint16_t w2 = m_pCPU->GetWord(addr);
		uint16_t w1 = 0;
		uint16_t w0 = 0;

		if (len == 8)
		{
			addr += 2;
			w1 = m_pCPU->GetWord(addr); addr += 2;
			w0 = m_pCPU->GetWord(addr);
		}

		ret = (uint64_t(w3) << 48) | (uint64_t(w2) << 32) | (uint64_t(w1) << 16) | w0;
	}

	return ret;
}

void CFPU::PutInt(uint32_t arg, int len)
{
	uint16_t addr = 0;

	if (m_nMeth)
	{
		addr = GetArgAddr(len);
	}
	else
	{
		// режим адресации 0
		m_pCPU->m_RON[m_nReg] = (arg >> 16) & 0xffff; // сохраняем старшее слово
	}

	if (addr & 1)
	{
		BADFP();    // BADI по нечётному адресу аргумента
	}
	else
	{
		m_pCPU->SetWord(addr, (arg >> 16) & 0xffff);

		if (len == 4)
		{
			addr += 2;
			m_pCPU->SetWord(addr, arg & 0xffff);
		}
	}
}

void CFPU::PutI(uint32_t arg)
{
	PutInt(arg, 2);
}

void CFPU::PutL(uint32_t arg)
{
	PutInt(arg, 4);
}

void CFPU::PutDst(uint32_t arg)
{
	if ($FPSR & $FL)
	{
		PutL(arg);
	}
	else
	{
		PutI(arg);
	}
}

void CFPU::PutX(uint64_t arg)
{
	int R5 = $FPSR;
	SFCC(arg);

	if (m_nMeth)
	{
		uint16_t addr = m_nDSTEA;
		m_pCPU->SetWord(addr, (arg >> 48) & 0xffff);

		if (!(m_nMeth == 2 && m_nReg == CCPU::R_PC))
		{
			addr += 2;
			m_pCPU->SetWord(addr, (arg >> 32) & 0xffff);

			if (R5 & $FD)
			{
				addr += 2;
				m_pCPU->SetWord(addr, (arg >> 16) & 0xffff);
				addr += 2;
				m_pCPU->SetWord(addr, arg & 0xffff);
			}
		}
	}
	else
	{
		$AC[m_nAc] = arg;
	}
}

bool CFPU::Over()
{
	$FPSR |= $FV;

	if ($FPSR & $FIV)
	{
		m_sRes.nExp -= 0200;// adjust exponent for return
		$FEC = $FOVER;
		return FPEXC();
	}
	else
	{
		PutZer();
	}

	return false;
}

bool CFPU::Under()
{
	if ($FPSR &$FIU)
	{
		m_sRes.nExp += 0200;// adjust exponent for return
		$FEC = $FUNDER;
		return FPEXC();
	}
	else
	{
		PutZer();
	}

	return false;
}

bool CFPU::DivZer()
{
	$FEC = $FDIVZ;
	return FPEXC();
}

bool CFPU::BADFP()
{
	$FEC = $FOP;
	return FPEXC();
}

bool CFPU::FPEXC()
{
	$FEA = m_nOldPC;
	$FPSR |= $FER;

	if ($FPSR & $FID)
	{
		return false;
	}
	else
	{
		m_pCPU->InterruptVIRQ(0244);
		return true;
	}
}

void CFPU::SFCC(uint64_t arg)
{
	if (!arg)
	{
		$FPSR |= $FZ;
	}

	if (__int64(arg) < 0)
	{
		$FPSR |= $FN;
	}
}

void CFPU::CFCC()
{
	uint16_t fps = $FPSR & 017;
	uint16_t ps = m_pCPU->GetPSW() & ~017;
	m_pCPU->SetPSW(ps | fps);
}

void CFPU::PutZer()
{
	uint64_t a = 0;
	PutAC(a);
}

uint64_t CFPU::GetAC()
{
	return $AC[m_nAc];
}

void CFPU::PutAC(uint64_t arg)
{
	SFCC(arg);
	$AC[m_nAc] = arg;
}

// разделение числа d на:
// bSign - знак,
// nExp - экспоненту,
// nFrac - мантиссу
void CFPU::Unpack(uint64_t d, UnpackedFLT *nArg)
{
	nArg->bSign = (__int64(d) < 0);
	d <<= 1;
	nArg->nExp = ((d >> 56) & 0377);
	nArg->nExp -= 0200; // remove excess 128

	if (nArg->nExp == 0)
	{
		nArg->nFrac = 0;
	}
	else
	{
		nArg->nFrac = d & 0x0fffffffffffffffull;
		nArg->nFrac |=    0x0100000000000000ull;    // set hidden bit
		nArg->nFrac <<= 1;  // shift again to provide 2 guard bits
	}
}

// сборка знака экспоненты и мантиссы в единое целое
uint64_t CFPU::Pack(UnpackedFLT *nArg)
{
	uint64_t res = 0;

	if (nArg->nFrac & 0xffff000000000000ull)
	{
		nArg->nExp += 0200; // make it excess 128

		if (nArg->nExp <= 0)
		{
			Under();
			return res;
		}
		else if (nArg->nExp >= 0400)
		{
			Over();
			return res;
		}
		else
		{
			nArg->nFrac >>= 1;  // delete extra guard bit
			res = nArg->nFrac & 0x00ffffffffffffffull;  // hidden bit disappears

			if ($FPSR & $FD)
			{
				// если double
				if ($FPSR & $FT)
				{
					// если truncate
				}
				else
				{
					// если округление
					bool c = !!(res & 1); // будущий бит С
					res += (c ? 2 : 0);
					c = !!(res & 0xff00000000000000ull);

					if (c)
					{
						res >>= 1;
						nArg->nExp++;

						if (nArg->nExp >= 0400)
						{
							Over();
							return res;
						}
					}
				}
			}
			else
			{
				// если float
				res &= 0xffffffff00000000ull;

				if ($FPSR & $FT)
				{
					// если truncate
				}
				else
				{
					// если округление
					bool c = !!(res & 0x100000000); // будущий бит С
					res += (c ? 0x200000000 : 0);
					c = !!(res & 0xff00000000000000ull);

					if (c)
					{
						res >>= 1;
						nArg->nExp++;

						if (nArg->nExp >= 0400)
						{
							Over();
							return res;
						}
					}
				}
			}
		}
	}
	else
	{
		return 0;
	}

	res >>= 1;
	res |= (uint64_t(nArg->nExp) << 55); // insert exponent

	if (nArg->bSign)
	{
		res += 0x8000000000000000ull;
	}

	return res;
}


void CFPU::ExecuteUNKNOWN()
{
}

// Скопировать флаги условий FPU в флаги CPU
void CFPU::ExecuteCFCC()
{
	CFCC();
}

// Установить режим float
void CFPU::ExecuteSETF()
{
	$FPSR &= ~$FD;
}

// Установить режим integer
void CFPU::ExecuteSETI()
{
	$FPSR &= ~$FL;
}

// Установить режим double
void CFPU::ExecuteSETD()
{
	$FPSR |= $FD;
}

// Установить режим long integer
void CFPU::ExecuteSETL()
{
	$FPSR |= $FL;
}

// Загрузить регистр состояния FPP
void CFPU::ExecuteLDFPS()
{
	uint32_t a = GetI();
	$FPSR = (a >> 16) & 0xffff;
}

// Сохранить регистр состояния FPP
void CFPU::ExecuteSTFPS()
{
	uint32_t a = $FPSR & ~030010;
	a <<= 16;
	PutI(a);
}

// Сохранить состояние FPP
void CFPU::ExecuteSTST()
{
	uint32_t a = $FEC;
	a <<= 16;
	a |= $FEA;
	PutL(a);
}

void CFPU::ExecuteCLRX()
{
	$FPSR &= ~017;
	uint64_t a = GetX();
	a = 0;
	PutX(a);
}

void CFPU::ExecuteTSTX()
{
	$FPSR &= ~017;
	uint64_t a = GetX();
	SFCC(a);
}

void CFPU::ExecuteABSX()
{
	$FPSR &= ~017;
	uint64_t a = GetX();
	a &= 0x7fffffffffffffffull;
	PutX(a);
}

void CFPU::ExecuteNEGX()
{
	$FPSR &= ~017;
	uint64_t a = GetX();

	if (a & 0x7f80000000000000ull)
	{
		a += 0x8000000000000000ull;
	}

	PutX(a);
}


void CFPU::I_mul(uint64_t f)
{
	UnpackedFLT fsrc;
	UnpackedFLT ac;
	Unpack(f, &fsrc);

	if (!fsrc.nExp)
	{
		m_sRes.bSign = false;
		m_sRes.nExp = 0;
		m_sRes.nFrac = 0;
		return;
	}

	Unpack($AC[m_nAc], &ac);

	if (!ac.nExp)
	{
		m_sRes.bSign = false;
		m_sRes.nExp = 0;
		m_sRes.nFrac = 0;
		return;
	}

	m_sRes.nExp = fsrc.nExp + ac.nExp - 1;
	m_sRes.bSign = fsrc.bSign ^ ac.bSign;
	m_sRes.nFrac = 0;
	uint64_t m = 1; // вот такое вот умножение.

	for (int i = 0; i < 58; ++i)
	{
		m_sRes.nFrac >>= 1;

		if (m & fsrc.nFrac)
		{
			m_sRes.nFrac += ac.nFrac;
		}

		m <<= 1;
	}
}

void CFPU::Saret()
{
	if (__int64(m_sRes.nFrac) < 0)
	{
		m_sRes.nFrac = -m_sRes.nFrac;
		m_sRes.bSign = !m_sRes.bSign;
	}

	Norm();
	uint64_t res = Pack(&m_sRes);
	PutAC(res);
}

void CFPU::Norm()
{
	if (m_sRes.nFrac == 0)
	{
		m_sRes.nExp = 0;
		m_sRes.bSign = false;
	}
	else
	{
		while (m_sRes.nFrac & 0xfc00000000000000ull)
		{
			m_sRes.nFrac >>= 1;
			m_sRes.nExp++;
		}

		while (!(m_sRes.nFrac & 0x0200000000000000ull))
		{
			m_sRes.nFrac <<= 1;
			m_sRes.nExp--;
		}
	}
}

void CFPU::ExecuteDIVX()
{
	UnpackedFLT fsrc;
	UnpackedFLT ac;
	uint64_t f = GetX(); // get fsrc
	Unpack(f, &fsrc);
	uint64_t a = GetAC();
	Unpack(a, &ac);

	if (fsrc.nExp)
	{
		if (ac.nExp)
		{
			m_sRes.nExp = ac.nExp + 1 - fsrc.nExp;
			m_sRes.bSign = fsrc.bSign ^ fsrc.bSign;
			uint64_t frac = 0;
			uint64_t R4 = 1ull << 57;

			do
			{
				if (ac.nFrac > fsrc.nFrac)
				{
					ac.nFrac -= fsrc.nFrac;
					frac |= R4;
				}

				ac.nFrac <<= 1;
				R4 >>= 1;
			}
			while (R4);

			m_sRes.nFrac = frac;
			Saret();
		}
		else
		{
			SFCC(a);
		}
	}
	else
	{
		DivZer();
	}
}

void CFPU::ExecuteMULX()
{
	uint64_t fsrc = GetX(); // get fsrc
	I_mul(fsrc);
	Saret();
}

void CFPU::ExecuteMODX()
{
	uint64_t fsrc = GetX(); // get fsrc
	I_mul(fsrc);
	Norm();
	uint64_t nInt = m_sRes.nFrac; // целая часть
	uint64_t nFrac = m_sRes.nFrac; // дробная часть
	uint64_t m = 0x0200000000000000ull;
	int R0 = 0;

	while (m)
	{
		if (R0 < m_sRes.nExp)
		{
			nInt &= ~m;
		}
		else
		{
			nFrac &= ~m;
		}

		R0++;
		m >>= 1;
	}

	bool bSign = m_sRes.bSign; // сохраним значения
	int nExp = m_sRes.nExp;
	m_sRes.nFrac = nFrac; // обработаем остаток
	Norm();
	nFrac = Pack(&m_sRes);
	SFCC(nFrac);
	$AC[m_nAc] = nFrac; // сохраним результат

	if (!(m_nAc & 1)) // если номер аккумулятора чётный
	{
		m_sRes.bSign = bSign; // обработаем целую часть
		m_sRes.nExp = nExp;
		m_sRes.nFrac = nInt;
		Norm();
		nInt = Pack(&m_sRes);
		$AC[m_nAc + 1] = nInt;
	}
}


void CFPU::ExecuteSUBX()
{
	UnpackedFLT fsrc;
	uint64_t f = GetX(); // get fsrc
	Unpack(f, &fsrc);

	if (fsrc.nExp)
	{
		fsrc.bSign = !fsrc.bSign;
	}

	I_add(f, fsrc);
}


void CFPU::ExecuteADDX()
{
	UnpackedFLT fsrc;
	uint64_t f = GetX(); // get fsrc
	Unpack(f, &fsrc);
	I_add(f, fsrc);
}

void CFPU::I_add(uint64_t f, UnpackedFLT &fsrc)
{
	UnpackedFLT ac;
	uint64_t a = GetAC();
	Unpack(a, &ac);

	if (fsrc.nExp)
	{
		if (ac.nExp)
		{
			int resExp = ac.nExp;
			int tmp = ac.nExp - fsrc.nExp;

			if (tmp > 0)
			{
				if (tmp > 58)
				{
					goto l_99;
				}

				do
				{
					fsrc.nFrac >>= 1;
				}
				while (--tmp > 0);
			}
			else if (tmp < 0)
			{
				tmp = -tmp;

				if (tmp > 58)
				{
					goto l_101;
				}

				resExp = fsrc.nExp;

				do
				{
					ac.nFrac >>= 1;
				}
				while (--tmp > 0);
			}

			m_sRes.nExp = resExp;
			m_sRes.bSign = ac.bSign;

			if (fsrc.bSign == ac.bSign)
			{
				m_sRes.nFrac = ac.nFrac + fsrc.nFrac;
			}
			else
			{
				m_sRes.nFrac = ac.nFrac - fsrc.nFrac;
			}

			Saret();
		}
		else
		{
l_101:
			f = Pack(&fsrc);
			PutAC(f);
		}
	}
	else
	{
l_99:
		PutAC(a);
	}
}

void CFPU::ExecuteLDX()
{
	uint64_t f = GetX();
	PutAC(f);
}

void CFPU::ExecuteSTX()
{
	uint64_t f = GetX();
	f = GetAC();
	PutX(f);
}

void CFPU::ExecuteCMPX()
{
	$FPSR &= ~017;
	uint64_t f = GetX();
	uint64_t a = GetAC();

	if (!($FPSR & $FD))
	{
		f >>= 32;
		a >>= 32;
	}

	if (a == f)
	{
		$FPSR |= $FZ;
	}
	else if (a < f)
	{
		$FPSR |= $FN;
	}
}

void CFPU::ExecuteSTEXP()
{
	UnpackedFLT ac;
	uint64_t a = GetAC();       // get value from accumulator
	Unpack(a, &ac);             // unpack the value
	a = uint64_t(ac.nExp) << 56;            // move exponent to destination
	SFCC(a);                    // set floating condition codes
	CFCC();                     // copy fcc to PSW
	PutI(ac.nExp << 16);        // return an integer
}

void CFPU::ExecuteLDEXP()
{
	uint32_t i = GetI();        // get new exponent
	UnpackedFLT ac;
	uint64_t a = GetAC();       // get value from accumulator
	Unpack(a, &ac);             // unpack the value
	ac.nExp = i >> 16;          // replace the exponent
	a = Pack(&ac);              // re-pack the value
	PutAC(a);                   // store it
}

void CFPU::ExecuteLDCJX()
{
	uint32_t i = GetSrc();
	m_sRes.nFrac = uint64_t(i) << 32;
	m_sRes.bSign = false;
	m_sRes.nExp = ($FPSR & $FL) ? 26 : 10;
	Saret();
}

void CFPU::ExecuteLDCYX()
{
	uint64_t f = 0;

	if ($FPSR & $FD)    // is current mode real*8?
	{
		$FPSR &= ~$FD;  // source is real*4
		f = GetX();
		$FPSR |= $FD;   // restore floating status
	}
	else
	{
		$FPSR |= $FD;   // source is real*8
		f = GetX();
		$FPSR &= ~$FD;  // restore floating status
		UnpackedFLT fsrc;
		Unpack(f, &fsrc);
		f = Pack(&fsrc);
	}

	PutAC(f);
}

void CFPU::ExecuteSTCJ()
{
	UnpackedFLT ac;
	uint64_t a = GetAC();       // get value from accumulator
	Unpack(a, &ac);             // unpack the value

	while (ac.nExp < 42)
	{
		ac.nFrac >>= 1;
		ac.nExp++;
	}

	if (ac.nExp > 42 || ac.nFrac & 0xffff000000000000ull)
	{
		ac.nFrac = 0;
		SFCC(ac.nFrac);
		$FPSR |= $FC;
		CFCC();

		if ($FPSR & $FIC)
		{
			$FEC = $FINT;
			FPEXC();
			return;
		}
	}
	else
	{
		ac.nFrac <<= 16;

		if (ac.bSign)
		{
			ac.nFrac = -ac.nFrac;
		}
	}

	SFCC(ac.nFrac);
	CFCC();
	PutDst((ac.nFrac >> 32) & 0xffffffff);
}

void CFPU::ExecuteSTCY()
{
	uint64_t a = GetAC();       // get value from accumulator

	if (!($FPSR & $FD))         // real*8?
	{
		$FPSR |= $FD;           // tell putx to store a real*8
	}
	else
	{
		$FPSR &= ~$FD;              // tell putx to store a real*4

		if (!($FPSR & $FT))         // floating chop mode?
		{
			int c = (a & 0x80000000) ? 1 : 0;   // put rounding bit in carry
			a >>= 32;
			uint64_t prev = a;
			a += c;

			if (((a & 0x80000000) && !(prev & 0x80000000)) // переполнение V
			        || (a & 0xffffffff00000000ull)) // переполнение C
			{
				Over();
				return;
			}
		}

		a &= 0xffffffff00000000ull; // clear lower half to convert
	}

	PutX(a);
}


void CFPU::RegisterMethodRef(uint16_t start, uint16_t end, ExecuteMethodRef methodref)
{
	for (int opcode = start; opcode <= end; ++opcode)
	{
		m_pExecuteMethodMap[opcode] = methodref;
	}
}

void CFPU::PrepareCPU()
{
	if (m_pExecuteMethodMap)
	{
		return;
	}

	m_pExecuteMethodMap = new ExecuteMethodRef[65536];

	if (m_pExecuteMethodMap)
	{
		// Сначала заполняем таблицу ссылками на метод ExecuteUNKNOWN, выполняющий TRAP 10
		RegisterMethodRef(0000000, 0177777, &CFPU::ExecuteUNKNOWN);
		// инструкции класса 0
		RegisterMethodRef(0170000, 0170000, &CFPU::ExecuteCFCC); // 170000
		RegisterMethodRef(0170001, 0170001, &CFPU::ExecuteSETF); // 170001
		RegisterMethodRef(0170002, 0170002, &CFPU::ExecuteSETI); // 170002
		RegisterMethodRef(0170011, 0170011, &CFPU::ExecuteSETD); // 170011
		RegisterMethodRef(0170012, 0170012, &CFPU::ExecuteSETL); // 170012
		// инструкции класса 1
		RegisterMethodRef(0170100, 0170177, &CFPU::ExecuteLDFPS); // 1701XX
		RegisterMethodRef(0170200, 0170277, &CFPU::ExecuteSTFPS); // 1702XX
		RegisterMethodRef(0170300, 0170377, &CFPU::ExecuteSTST); // 1703XX
		RegisterMethodRef(0170400, 0170477, &CFPU::ExecuteCLRX); // 1704XX
		RegisterMethodRef(0170500, 0170577, &CFPU::ExecuteTSTX); // 1705XX
		RegisterMethodRef(0170600, 0170677, &CFPU::ExecuteABSX); // 1706XX
		RegisterMethodRef(0170700, 0170777, &CFPU::ExecuteNEGX); // 1707XX
		// инструкции класса 2
		RegisterMethodRef(0171000, 0171077, &CFPU::ExecuteMULX); // 1710XX
		RegisterMethodRef(0171400, 0171477, &CFPU::ExecuteMODX); // 1714XX
		RegisterMethodRef(0172000, 0172077, &CFPU::ExecuteADDX); // 1720XX
		RegisterMethodRef(0172400, 0172477, &CFPU::ExecuteLDX); // 1724XX
		RegisterMethodRef(0173000, 0173077, &CFPU::ExecuteSUBX); // 1730XX
		RegisterMethodRef(0173400, 0173477, &CFPU::ExecuteCMPX); // 1734XX
		RegisterMethodRef(0174000, 0174077, &CFPU::ExecuteSTX); // 1740XX
		RegisterMethodRef(0174400, 0174477, &CFPU::ExecuteDIVX); // 1744XX
		RegisterMethodRef(0175000, 0175077, &CFPU::ExecuteSTEXP); // 1750XX
		RegisterMethodRef(0175400, 0175477, &CFPU::ExecuteSTCJ); // 1754XX
		RegisterMethodRef(0176000, 0176077, &CFPU::ExecuteSTCY); // 1760XX
		RegisterMethodRef(0176400, 0176477, &CFPU::ExecuteLDEXP); // 1764XX
		RegisterMethodRef(0177000, 0177077, &CFPU::ExecuteLDCJX); // 1770XX
		RegisterMethodRef(0177400, 0177477, &CFPU::ExecuteLDCYX); // 1774XX
	}
	else
	{
		// g_BKMsgBoxer.Show(g_pstrErrors[BK_ERROR_NOTENMEMR], MB_OK);
	}
}

void CFPU::DoneCPU()
{
	SAFE_DELETE_ARRAY(m_pExecuteMethodMap);
}


