#pragma once

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


class CFPU
{
		CCPU    *m_pCPU;
		uint16_t $FPSR;         // регистр состояния FPP
		enum FSR_BITS : uint16_t
		{
			$FER   = 0100000,   // Floating ERror
			$FID   = 040000,    // Interrupt Disable
			$FPRES = 030020,    // reserved bits
			$FIUV  = 04000,     // Interrupt on Undefined Variable
			$FIU   = 02000,     // Interrupt on Underflow
			$FIV   = 01000,     // Interrupt on oVerflow
			$FIC   = 0400,      // Interrupt on integer Conversion error
			$FD    = 0200,      // floating Double precision mode
			$FL    = 0100,      // floating Long integer mode
			$FT    = 040,       // floating chop mode
			$FMM   = 020,       // Floating Maintenance Mode
			$FN    = 010,       // floating Negative
			$FZ    = 04,        // floating Zero
			$FV    = 02,        // floating oVerflow
			$FC    = 01         // floating Carry
		};

		uint16_t $FEC;          // Floating Exception Code register
		enum FEC_CODES : uint16_t
		{
			$FOP    = 2,        // floating opcode error
			$FDIVZ  = 4,        // floating divide by 0
			$FINT   = 6,        // floating to integer conversion error
			$FOVER  = 8,        // floating overflow
			$FUNDER = 10,       // floating underflow
			$FUNDF  = 12,       // floating undefined variable
			$FMMT   = 14        // Maintenance trap
		};

		uint16_t $FEA;          // Floating Exception Address register

		uint64_t $AC[6];        // storage for floating ACcumulators

		uint16_t        m_instruction;  // текущая инструкция
		int             m_nMeth;
		CCPU::REGISTER  m_nReg;
		int             m_nAc;

		struct UnpackedFLT
		{
			bool     bSign; // знак, true - минус, false - плюс
			int      nExp;  // порядок числа
			uint64_t nFrac; // мантисса числа
		};

		// тут собирается результат
		UnpackedFLT     m_sRes;

		uint16_t        m_nOldPC;
		uint16_t        m_nDSTEA;

		void            ExecuteUNKNOWN();
		// инструкции класса 0
		void            ExecuteCFCC(); // 170000
		void            ExecuteSETF(); // 170001
		void            ExecuteSETI(); // 170002
		void            ExecuteSETD(); // 170011
		void            ExecuteSETL(); // 170012

		// инструкции класса 1
		void            ExecuteLDFPS(); // 1701XX
		void            ExecuteSTFPS(); // 1702XX
		void            ExecuteSTST();  // 1703XX
		void            ExecuteCLRX();  // 1704XX
		void            ExecuteTSTX();  // 1705XX
		void            ExecuteABSX();  // 1706XX
		void            ExecuteNEGX();  // 1707XX

		// инструкции класса 2
		void            ExecuteMULX();  // 1710XX
		void            ExecuteMODX();  // 1714XX
		void            ExecuteADDX();  // 1720XX
		void            ExecuteLDX();   // 1724XX
		void            ExecuteSUBX();  // 1730XX
		void            ExecuteCMPX();  // 1734XX
		void            ExecuteSTX();   // 1740XX
		void            ExecuteDIVX();  // 1744XX
		void            ExecuteSTEXP(); // 1750XX
		void            ExecuteSTCJ();  // 1754XX
		void            ExecuteSTCY();  // 1760XX
		void            ExecuteLDEXP(); // 1764XX
		void            ExecuteLDCJX(); // 1770XX
		void            ExecuteLDCYX(); // 1774XX

		uint16_t        GetArgAddr(int &len);

		uint32_t        GetInt(int len);
		uint32_t        GetI();
		uint32_t        GetL();
		uint32_t        GetSrc();

		uint64_t        GetX();

		void            PutInt(uint32_t arg, int len);
		void            PutI(uint32_t arg);
		void            PutL(uint32_t arg);
		void            PutDst(uint32_t arg);

		void            PutX(uint64_t arg);

		bool            Over();
		bool            Under();
		bool            DivZer();
		bool            BADFP();
		bool            FPEXC();

		void            SFCC(uint64_t arg);
		void            CFCC();

		void            PutZer();

		uint64_t        GetAC();
		void            PutAC(uint64_t arg);

		void            Unpack(uint64_t d, UnpackedFLT *nArg);
		uint64_t        Pack(UnpackedFLT *nArg);

		void            I_add(uint64_t f, UnpackedFLT &fsrc);
		void            I_mul(uint64_t fsrc);
		void            Saret();
		void            Norm();

		using ExecuteMethodRef = void (CFPU::*)();
		ExecuteMethodRef *m_pExecuteMethodMap;
		void            RegisterMethodRef(uint16_t start, uint16_t end, ExecuteMethodRef methodref);
		void            PrepareCPU();   // Инициализация таблицы декодирования команд
		void            DoneCPU();      // удаление таблицы декодирования команд и освобождение памяти

	public:
		CFPU(CCPU *pCPU);
		~CFPU();
		void            init();
		int             TranslateInstruction();

};
