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

#if (FPU_MODULE)
// разрешение и подключение см. в файле cpu.h
#include "ExceptionHalt.h"

// Модель эмуляции математического сопроцессора
// проходит тесты xxdp: dffpa, dffpb, cffpc
// но т.к. сами эти тесты не охватывают все возможные ситуации,
// особенно в плане исключительных ситуаций, то некоторые места просто подогнаны
// под тесты.

CFPU::CFPU(CCPU *pCPU)
	: m_pCPU(pCPU)
	, m_pExecuteMethodMap(nullptr)
{
	init();
	reset();
	PrepareCPU();
}

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

void CFPU::init()
{
	for (auto &i : $AC)
	{
		i = 0;
	}
}


void CFPU::reset()
{
	$FPSR = 0;
	$FEC = 0;
	$FEA = 0;
	m_nDSTEA = 0;
	m_nDSTLen = 0;
	m_bError = false;
}

int CFPU::TranslateInstruction(uint16_t nInstr)
{
	m_nOldPC = m_pCPU->m_RON[static_cast<int>(CCPU::REGISTER::PC)];
	//обрабатываем только опкоды, начинающиеся с 0170000, остальные игнорируем.
//  if ((nInstr & 0170000) == 0170000)
//  {
	m_pCPU->m_RON[static_cast<int>(CCPU::REGISTER::PC)] += 2;
	register int t = nInstr;
	m_nReg = static_cast<CCPU::REGISTER>(t & 7);
	t >>= 3;
	m_nMeth = t & 7;
	t >>= 3;
	m_nAc = t & 3;

	try
	{
		m_bError = false;
		// Find command implementation using the command map
		(this->*(m_pExecuteMethodMap[nInstr]))();  // Call command implementation method

		if (m_bError)
		{
			throw CFPUException(static_cast<FEC_CODES>($FEC));
		}
	}
	catch (CFPUException &e)
	{
		$FEC = e.m_code;
		$FEA = m_nOldPC;
		$FPSR |= FSR_BITS::$FER;
		TRACE(_T("FPU Exception:: address: %06o %s\n"), m_nOldPC, e.m_info);

		if (!($FPSR & FSR_BITS::$FID))
		{
			TRACE(_T("FPU Interrupt enable\n"));
			//m_pCPU->InterruptVIRQ(0244);
			m_pCPU->UserInterrupt(0244); //это блин не маскируемое прерывание, или у него приоритет должен быть высокий
		}
	}

//	}
	// TODO надо возвращать время выполнения инструкций, надо узнать, где найти это время
	return 8;
}


bool CFPU::MakeThrow(FEC_CODES code)
{
	m_bError = true; //после выполнения надо сделать обработку ошибок
	$FEC = code;
	$FEA = m_nOldPC;
	$FPSR |= FSR_BITS::$FER;
	return !($FPSR & FSR_BITS::$FID); //возвращаем true - прерывания разрешены, false - запрещены
}



uint16_t CFPU::GetArgAddr(int &len)
{
	uint16_t addr = 0;
	uint16_t index;
	auto nReg = static_cast<int>(m_nReg);

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

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

			if (m_nReg == CCPU::REGISTER::PC && len == 4)
			{
				len = 2;
			}

			m_pCPU->m_RON[nReg] += len; // инкремент
			break;

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

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

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

		case 6: // режим адресации 6
			index = m_pCPU->GetWord(m_pCPU->m_RON[static_cast<int>(CCPU::REGISTER::PC)]);
			m_pCPU->m_RON[static_cast<int>(CCPU::REGISTER::PC)] += 2;
			addr = uint16_t(index + m_pCPU->m_RON[nReg]);
			break;

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

	return addr;
}

uint16_t CFPU::CalcArgAddrX(int &len)
{
	uint16_t addr = 0, index;
	auto nReg = static_cast<int>(m_nReg);

	switch (m_nMeth)
	{
		case 0:
			if (m_nReg > CCPU::REGISTER::R5)
			{
				throw CFPUException(FEC_CODES::$FOP);    // BADI - неверный номер аккумулятора
			}

			addr = nReg;
			break;

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

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

			if (m_nReg == CCPU::REGISTER::PC)
			{
				len = 2;
			}

			m_pCPU->m_RON[nReg] += len; // инкремент
			break;

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

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

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

		case 6: // режим адресации 6
			index = m_pCPU->GetWord(m_pCPU->m_RON[static_cast<int>(CCPU::REGISTER::PC)]);
			m_pCPU->m_RON[static_cast<int>(CCPU::REGISTER::PC)] += 2;
			addr = uint16_t(index + m_pCPU->m_RON[nReg]);
			break;

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

	return addr;
}

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

		if (addr & 1)
		{
			MakeThrow(FEC_CODES::$FOP);    // BADI по нечётному адресу аргумента
			//throw CExceptionHalt(addr, _T("Odd address halt."));
		}

		uint32_t hi = m_pCPU->GetWord(addr);
		uint32_t lo = 0;

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

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

	// режим адресации 0
	uint32_t ret = uint32_t(m_pCPU->m_RON[static_cast<int>(m_nReg)]) << 16; // get high-order word, clear low-order word
	return ret;
}

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

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

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

	return GetI();
}

uint64_t CFPU::GetXdef()
{
	return GetX($FPSR);
}

uint64_t CFPU::GetX(uint16_t nTempFPSR)
{
	uint64_t ret = 0;
	int len = (nTempFPSR & FSR_BITS::$FD) ? 8 : 4; // double:float
	uint16_t addr = CalcArgAddrX(len);
	m_nDSTEA = addr;
	m_nDSTLen = len;

	if (m_nMeth)
	{
		if (addr & 1)
		{
			MakeThrow(FEC_CODES::$FOP);    // BADI по нечётному адресу аргумента
			//throw CExceptionHalt(addr, _T("Odd address halt."));
		}

		uint16_t w3 = m_pCPU->GetWord(addr);
		uint16_t w2 = 0;
		uint16_t w1 = 0;
		uint16_t w0 = 0;

		if (len > 2)
		{
			addr += 2;
			w2 = m_pCPU->GetWord(addr);

			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;
	}
	else
	{
		ret = $AC[addr];

		if (len == 4)
		{
			ret &= 0xffffffff00000000ull;
		}
	}

	if ($FPSR & FSR_BITS::$FIUV)
	{
		if ((ret & 0xff80000000000000ull) == 0x8000000000000000ull)
		{
			//throw CFPUException(FEC_CODES::$FUNDF);
			MakeThrow(FEC_CODES::$FUNDF);
		}
	}

	return ret;
}

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

		if (addr & 1)
		{
			MakeThrow(FEC_CODES::$FOP);    // BADI по нечётному адресу аргумента
			//throw CExceptionHalt(addr, _T("Odd address halt."));
		}

		m_pCPU->SetWord(addr, (arg >> 16) & 0xffff);

		if (len == 4)
		{
			addr += 2;
			m_pCPU->SetWord(addr, arg & 0xffff);
		}
	}
	else
	{
		// режим адресации 0
		m_pCPU->m_RON[static_cast<int>(m_nReg)] = (arg >> 16) & 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 & FSR_BITS::$FL)
	{
		PutL(arg);
	}
	else
	{
		PutI(arg);
	}
}

void CFPU::PutX(uint16_t nTempFPSR, uint64_t arg)
{
	int len = (nTempFPSR & FSR_BITS::$FD) ? 8 : 4; // double:float
	uint16_t addr = CalcArgAddrX(len);
	WriteDst(nTempFPSR, len, addr, arg);
}

void CFPU::PutXDst(uint64_t arg)
{
	WriteDst($FPSR, m_nDSTLen, m_nDSTEA, arg);
}

void CFPU::WriteDst(uint16_t nTempFPSR, int len, uint16_t addr, uint64_t arg)
{
	if (m_nMeth)
	{
		if (addr & 1)
		{
			MakeThrow(FEC_CODES::$FOP);    // BADI по нечётному адресу аргумента
			//throw CExceptionHalt(addr, _T("Odd address halt."));
		}

		m_pCPU->SetWord(addr, (arg >> 48) & 0xffff);

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

			if (len == 8)
			{
				addr += 2;
				m_pCPU->SetWord(addr, (arg >> 16) & 0xffff);
				addr += 2;
				m_pCPU->SetWord(addr, arg & 0xffff);
			}
		}
	}
	else
	{
		PutAC(nTempFPSR, addr, arg);
	}
}

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

	if ($FPSR & FSR_BITS::$FIV)
	{
		return MakeThrow(FEC_CODES::$FOVER);
	}

	return false;
}

bool CFPU::Under()
{
	if ($FPSR & FSR_BITS::$FIU)
	{
		return MakeThrow(FEC_CODES::$FUNDER);
	}

	return false;
}

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

	if ($FPSR & FSR_BITS::$FIV)
	{
		MakeThrow(FEC_CODES::$FOVER);
		return true;
	}

	return !($FPSR & FSR_BITS::$FID); //возвращаем true - прерывания разрешены, false - запрещены
}

bool CFPU::Under2()
{
	if ($FPSR & FSR_BITS::$FIU)
	{
		MakeThrow(FEC_CODES::$FUNDER);
		return true;
	}

	return !($FPSR & FSR_BITS::$FID); //возвращаем true - прерывания разрешены, false - запрещены
}


void CFPU::SFNZ(uint64_t arg)
{
	int nExp = ((arg >> 55) & 0377);

	if (nExp)
	{
		$FPSR &= ~FSR_BITS::$FZ;
	}
	else
	{
		$FPSR |= FSR_BITS::$FZ;
	}

	if (__int64(arg) < 0)
	{
		$FPSR |= FSR_BITS::$FN;
	}
	else
	{
		$FPSR &= ~FSR_BITS::$FN;
	}
}

void CFPU::SFNZi(uint32_t arg)
{
	if (arg)
	{
		$FPSR &= ~FSR_BITS::$FZ;
	}
	else
	{
		$FPSR |= FSR_BITS::$FZ;
	}

	if (int(arg) < 0)
	{
		$FPSR |= FSR_BITS::$FN;
	}
	else
	{
		$FPSR &= ~FSR_BITS::$FN;
	}
}

void CFPU::SFNZ00(uint64_t arg)
{
	$FPSR &= ~FSR_BITS::$FNZVC;
	SFNZ(arg);
}

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


uint64_t CFPU::GetACdef()
{
	return GetAC(m_nAc);
}

uint64_t CFPU::GetAC(int nAC)
{
	if ($FPSR & FSR_BITS::$FD)
	{
		return $AC[nAC];
	}

	return $AC[nAC] & 0xffffffff00000000ull;
}

void CFPU::PutACdef(uint64_t arg)
{
	PutAC($FPSR, m_nAc, arg);
}

void CFPU::PutAC(uint16_t nTempFPSR, int nAC, uint64_t arg)
{
	if (nTempFPSR & FSR_BITS::$FD)
	{
		$AC[nAC] = arg;
	}
	else
	{
		$AC[nAC] &= 0xffffffff;
		$AC[nAC] |= (arg & 0xffffffff00000000ull);
	}
}

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

	if (nArg->nExp == 0)
	{
		nArg->nFrac = 0;
		nArg->bSign = false;
	}
	else
	{
		nArg->nExp -= 0200; // remove excess 128, если при этом будет 0, то это нормально, это 2^0
		nArg->nFrac = d & 0x00ffffffffffffffull;
		nArg->nFrac |=    0x0100000000000000ull;    // set hidden bit
		nArg->nFrac <<= 1;  // shift again to provide 2 guard bits
	}
}

// сборка знака экспоненты и мантиссы в единое целое
// для команды Modx целых два исключения.
// bRoundTrunk - при сохраниении целой части, когда есть дробная - не нужно делвть округления, и не нужно делать прерываний
// bModx - при обработке переполнений - логика когда сохранять 0, а когда резултат как есть - отличается.
uint64_t CFPU::Pack(UnpackedFLT *nArg, bool bRoundTrunk, OVR_FUNC func)
{
	Normalize(nArg);
	uint64_t res = 0;

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

		if (nArg->nExp <= 0)
		{
			switch (func)
			{
				case OVR_FUNC::MODx:
					// в команде MODx при переполнениях, нужно сохранять тот результат
					// что получился. В отличии от других команд
					Under();

					if (!($FPSR & FSR_BITS::$FID)) //возвращаем true - прерывания разрешены, false - запрещены
					{
						goto l_packex; //если прерывания не будет - надо сохранить 0
					}

					break;

				case OVR_FUNC::LDEXP:

					// в команде LDEXP при переполнениях сохраняется полученный результ
					// когда флаг переполнения включен, и 0, когда выключен.
					// когда флаг переполнения выкл и прерывания разрешены  непонятно, но сейчас
					// сохраняется результат.
					if (!Under2())
					{
						goto l_packex;
					}

					break;

				default:
					if (!Under())
					{
						goto l_packex; //если прерывания не будет - надо сохранить 0
					}

					// если прерывание будет, сохраним результат как есть
			}
		}
		else if (nArg->nExp >= 0400)
		{
			switch (func)
			{
				case OVR_FUNC::MODx:
					Over();

					if (!($FPSR & FSR_BITS::$FID)) //возвращаем true - прерывания разрешены, false - запрещены
					{
						goto l_packex; //если прерывания не будет - надо сохранить 0
					}

					break;

				case OVR_FUNC::LDEXP:
					if (!Over2())
					{
						goto l_packex;
					}

					break;

				default:
					if (!Over())
					{
						goto l_packex;  //если прерывания не будет - надо сохранить 0
						//!!!(FP11B говорит, что результат будет тот, что есть)
					}

					// если прерывание будет, сохраним результат как есть
			}
		}

		nArg->nFrac >>= 1;  // delete extra guard bit
		res = nArg->nFrac & 0x00ffffffffffffffull;  // hidden bit disappears

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

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

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

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

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

						if (nArg->nExp >= 0400)
						{
							if (!Over())
							{
								res = 0;
								goto l_packex;
							}
						}
					}
				}
			}
		}

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

l_packex:

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

	return res;
}


void CFPU::ExecuteILLEGAL()
{
	throw CFPUException(FEC_CODES::$FOP);
}

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

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

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

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

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

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

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

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

void CFPU::ExecuteCLRX()
{
	uint64_t a = GetXdef();
	a = 0;
	PutXDst(a);
	SFNZ00(a);
}

void CFPU::ExecuteTSTX()
{
	uint64_t a = GetXdef();
	SFNZ00(a);
}

void CFPU::ExecuteABSX()
{
	uint64_t a = GetXdef();
	UnpackedFLT ac;
	Unpack(a, &ac);
	ac.bSign = false;
	a = Pack(&ac, true);
	PutXDst(a);
	SFNZ00(a);
}

void CFPU::ExecuteNEGX()
{
	uint64_t a = GetXdef();
	UnpackedFLT ac;
	Unpack(a, &ac);

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

	a = Pack(&ac, true);
	PutXDst(a);
	SFNZ00(a);
}

void CFPU::Normalize(UnpackedFLT *uf)
{
	if (uf->nFrac == 0)
	{
		uf->nExp = 0;
		uf->bSign = false;
	}
	else
	{
		while (uf->nFrac & 0xfc00000000000000ull)
		{
			uf->nFrac >>= 1;
			uf->nExp++;
		}

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

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

	if (fsrc.nExp)
	{
		$FPSR &= ~FSR_BITS::$FNZVC;

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

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

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

			uint64_t res = Pack(&sRes, true);
			PutACdef(res);
			SFNZ(res);
		}
		else
		{
			SFNZ(a);
		}
	}
	else
	{
		MakeThrow(FEC_CODES::$FDIVZ);
	}
}

CFPU::UnpackedFLT CFPU::I_mul()
{
	UnpackedFLT fsrc;
	UnpackedFLT ac;
	UnpackedFLT sRes{ false, 0, 0 };
	uint64_t f = GetXdef(); // get fsrc
	uint64_t a = GetACdef(); // get ac
	Unpack(f, &fsrc);
	Unpack(a, &ac);

	if ((!fsrc.nExp && !fsrc.nFrac) || (!ac.nExp && !ac.nFrac))
	{
		return sRes;
	}

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

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

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

		m <<= 1;
	}

	return sRes;
}

void CFPU::ExecuteMULX()
{
	UnpackedFLT sRes = I_mul();
	$FPSR &= ~FSR_BITS::$FNZVC;
	uint64_t res = Pack(&sRes, true);
	PutACdef(res);
	SFNZ(res);
}

// функция криво работает, или тесты кривые.
void CFPU::ExecuteMODX()
{
	UnpackedFLT sRes = I_mul();
	Normalize(&sRes);
	uint64_t nInt = 0;
	uint64_t nFrac = 0;
	$FPSR &= ~FSR_BITS::$FNZVC;

	if (!(sRes.nExp == 0 && sRes.nFrac == 0))
	{
		if (sRes.nExp < -127)
		{
			//underflow
			nFrac = Pack(&sRes, true, OVR_FUNC::MODx);
		}
		else if (sRes.nExp > 127)
		{
			//overflow
			nInt = Pack(&sRes, true, OVR_FUNC::MODx);
		}
		else
		{
			UnpackedFLT fint = sRes; // целая часть
			UnpackedFLT ffrac = sRes; // дробная часть
			uint64_t m = 0x0200000000000000ull;
			int nMaxExp = ($FPSR & FSR_BITS::$FD) ? 56 : 24;

			if (sRes.nExp > nMaxExp)
			{
				ffrac.nFrac = 0;
			}
			else
			{
				int R0 = 0;

				while (m)
				{
					if (R0 < sRes.nExp)
					{
						ffrac.nFrac &= ~m;
					}

					R0++;
					m >>= 1;
				}

				fint.nFrac &= ~ffrac.nFrac;
			}

			nInt = Pack(&fint, false);
			nFrac = Pack(&ffrac, true);
		}
	}

	// если номер аккумулятора нечётный, то остаток перезаписывает целую часть.
	// т.е. сперва сохраняется целая часть, потом остаток.
	// поэтому мы, если что вообще целую часть сохранять не будем
	if (!(m_nAc & 1)) // если номер аккумулятора чётный
	{
		// обработаем целую часть
		PutAC($FPSR, m_nAc | 1, nInt);
	}

	// обработаем остаток
	PutACdef(nFrac); // сохраним результат
	SFNZ(nFrac);
}


void CFPU::ExecuteSUBX()
{
	I_add(false);
}


void CFPU::ExecuteADDX()
{
	I_add(true);
}

void CFPU::I_add(bool bAdding)
{
	uint64_t f = GetXdef(); // get fsrc
	uint64_t a = GetACdef();

	if (!bAdding)
	{
		// для вычитания меняем знак
		f ^= 0x8000000000000000ull;
	}

	UnpackedFLT fsrc;
	UnpackedFLT ac;
	UnpackedFLT sRes{ false, 0, 0 };
	Unpack(f, &fsrc);
	Unpack(a, &ac);
	$FPSR &= ~FSR_BITS::$FNZVC;

	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);
			}

			sRes.nExp = resExp;
			sRes.bSign = ac.bSign;

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

			if (__int64(sRes.nFrac) < 0)
			{
				sRes.nFrac = -sRes.nFrac;
				sRes.bSign = !sRes.bSign;
			}

			uint64_t res = Pack(&sRes, true);
			PutACdef(res);
			SFNZ(res);
		}
		else
		{
l_101:
			PutACdef(f);
			SFNZ(f);
		}
	}
	else
	{
l_99:
		PutACdef(a);
		SFNZ(a);
	}
}

void CFPU::ExecuteLDX()
{
	uint64_t f = GetXdef();
	SFNZ00(f);
	PutACdef(f);
}

void CFPU::ExecuteSTX()
{
	uint64_t f = GetACdef();
	PutX($FPSR, f);
}

void CFPU::ExecuteCMPX()
{
	uint64_t f = GetXdef();
	uint64_t a = GetACdef();
	UnpackedFLT sRes{ false, 0, 0 };
	UnpackedFLT ac;
	UnpackedFLT fsrc;
	Unpack(a, &ac);
	Unpack(f, &fsrc);

	if (ac.nExp)
	{
		ac.bSign = !ac.bSign;   //там алгортим сложения, поэтому чтобы вычесть, надо сменить знак
	}

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

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

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

				if (tmp > 58)
				{
					goto l_101;
				}

				resExp = ac.nExp;

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

			sRes.nExp = resExp;
			sRes.bSign = fsrc.bSign;

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

			if (__int64(sRes.nFrac) < 0)
			{
				sRes.nFrac = -sRes.nFrac;
				sRes.bSign = !sRes.bSign;
			}
		}
		else
		{
l_101:
			sRes = fsrc;
		}
	}
	else
	{
l_99:
		sRes = ac;
	}

	uint64_t res = Pack(&sRes, true);
	SFNZ00(res);
}

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

void CFPU::ExecuteLDEXP()
{
	uint32_t i = GetI();            // get new exponent
	UnpackedFLT ac;
	uint64_t a = GetACdef();        // get value from accumulator
	Unpack(a, &ac);                 // unpack the value
	ac.nExp = int(short(i >> 16));  // replace the exponent
	$FPSR &= ~FSR_BITS::$FNZVC;
	a = Pack(&ac, true, OVR_FUNC::LDEXP);           // re-pack the value
	PutACdef(a);                    // store it
	SFNZ(a);
}

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

	if (__int64(sRes.nFrac) < 0)
	{
		sRes.nFrac = -sRes.nFrac;
		sRes.bSign = !sRes.bSign;
	}

	$FPSR &= ~FSR_BITS::$FNZVC;
	uint64_t res = Pack(&sRes, true);
	PutACdef(res);
	SFNZ(res);
}

void CFPU::ExecuteLDCYX()
{
	uint64_t f = 0;
	uint16_t nTempFPSR = $FPSR;

	if ($FPSR & FSR_BITS::$FD)    // is current mode real*8?
	{
		nTempFPSR &= ~FSR_BITS::$FD;  // source is real*4
		f = GetX(nTempFPSR);
	}
	else
	{
		nTempFPSR |= FSR_BITS::$FD;   // source is real*8
		f = GetX(nTempFPSR);
	}

	UnpackedFLT fsrc;
	Unpack(f, &fsrc);
	$FPSR &= ~FSR_BITS::$FNZVC;
	f = Pack(&fsrc, true);
	PutACdef(f);
	SFNZ(f);
}

void CFPU::ExecuteSTCXJ()
{
	UnpackedFLT ac;
	uint64_t a = GetACdef();    // get value from accumulator
	Unpack(a, &ac);             // unpack the value
	$FPSR &= ~FSR_BITS::$FNZVC;
	uint32_t res = 0;

	if (ac.nExp >= 0)
	{
		int nMaxExp = ($FPSR & FSR_BITS::$FL) ? 31 : 15;
		int nUpLim = ($FPSR & FSR_BITS::$FL) ? 42 : 26;

		if (ac.nExp > nMaxExp)
		{
			if (ac.bSign
			        && ac.nExp == nMaxExp + 1
			        && (ac.nFrac & (($FPSR & FSR_BITS::$FL) ? 0x03fffffffffffff8ull : 0x03fffff800000000ull)) == 0x0200000000000000ull)
			{
				// максимальное отрицательное число
				res |= 0x80000000;
			}
			else
			{
				$FPSR |= FSR_BITS::$FC;

				if ($FPSR & FSR_BITS::$FIC)
				{
					MakeThrow(FEC_CODES::$FINT);
				}
			}
		}
		else
		{
			while (ac.nExp < nUpLim)
			{
				ac.nFrac >>= 1;
				ac.nExp++;
			}

			if (ac.nExp > nUpLim || (ac.nFrac & 0xffff000000000000ull))
			{
				$FPSR |= FSR_BITS::$FC;

				if ($FPSR & FSR_BITS::$FIC)
				{
					MakeThrow(FEC_CODES::$FINT);
				}
			}
			else
			{
				res = ac.nFrac >> 16;

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

	PutDst(res);
	SFNZi(res);
	CFCC();
}

void CFPU::ExecuteSTCXY()
{
	uint64_t a = GetACdef();                    // get value from accumulator
	UnpackedFLT ac;
	Unpack(a, &ac);
	$FPSR &= ~FSR_BITS::$FNZVC;
	uint16_t nTempFPSR;

	if (!($FPSR & FSR_BITS::$FD))               // real*8?
	{
		// tell putx to store a real*8
		$FPSR |= FSR_BITS::$FD;
		nTempFPSR = $FPSR;
		a = Pack(&ac, true);
		$FPSR &= ~FSR_BITS::$FD;
	}
	else
	{
		// tell putx to store a real*4
		$FPSR &= ~FSR_BITS::$FD;
		nTempFPSR = $FPSR;
		a = Pack(&ac, true);
		$FPSR |= FSR_BITS::$FD;
	}

	PutX(nTempFPSR, a);
	SFNZ(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)
	{
		// Сначала заполняем таблицу ссылками на метод ExecuteILLEGAL, выполняющий trap to 244
		RegisterMethodRef(0000000, 0177777, &CFPU::ExecuteILLEGAL);
		// инструкции класса 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, 0171377, &CFPU::ExecuteMULX);    // 1710XX
		RegisterMethodRef(0171400, 0171777, &CFPU::ExecuteMODX);    // 1714XX
		RegisterMethodRef(0172000, 0172377, &CFPU::ExecuteADDX);    // 1720XX
		RegisterMethodRef(0172400, 0172777, &CFPU::ExecuteLDX);     // 1724XX
		RegisterMethodRef(0173000, 0173377, &CFPU::ExecuteSUBX);    // 1730XX
		RegisterMethodRef(0173400, 0173777, &CFPU::ExecuteCMPX);    // 1734XX
		RegisterMethodRef(0174000, 0174377, &CFPU::ExecuteSTX);     // 1740XX
		RegisterMethodRef(0174400, 0174777, &CFPU::ExecuteDIVX);    // 1744XX
		RegisterMethodRef(0175000, 0175377, &CFPU::ExecuteSTEXP);   // 1750XX
		RegisterMethodRef(0175400, 0175777, &CFPU::ExecuteSTCXJ);   // 1754XX STCDI/STCDL/STCFI/STCFL
		RegisterMethodRef(0176000, 0176377, &CFPU::ExecuteSTCXY);   // 1760XX STCDF/STCFD
		RegisterMethodRef(0176400, 0176777, &CFPU::ExecuteLDEXP);   // 1764XX
		RegisterMethodRef(0177000, 0177377, &CFPU::ExecuteLDCJX);   // 1770XX LDCID/LDCLD/LDCIF/LDCLF
		RegisterMethodRef(0177400, 0177777, &CFPU::ExecuteLDCYX);   // 1774XX LDCFD/LDCDF
	}
	else
	{
		// g_BKMsgBoxer.Show(IDS_BK_ERROR_NOTENMEMR, MB_OK);
	}
}

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

#endif
