
namespace PIC_Simulator.PIC
{
	class PICTimer
	{
		private PICProgramm programm;

		public PICTimer(PICProgramm p)
		{
			programm = p;
		}

		private bool prev_RA4 = false;
		public void TimerBerechnen(uint cycles)
		{
			bool tmr_mode = programm.GetRegisterOhneBank(PICProgramm.ADDR_OPTION, PICProgramm.OPTION_BIT_T0CS);
			bool edge_mode = programm.GetRegisterOhneBank(PICProgramm.ADDR_OPTION, PICProgramm.OPTION_BIT_T0SE);

			if (!tmr_mode)
			{
				bool curr_A4 = programm.GetRegisterOhneBank(PICProgramm.ADDR_PORT_A, 4);

				if (edge_mode)
				{
					if (prev_RA4 && !curr_A4)
					{
						TimerHochzaehlen(cycles);
					}
				}
				else
				{
					if (!prev_RA4 && curr_A4)
					{
						TimerHochzaehlen(cycles);
					}
				}
			}
			else
			{
				TimerHochzaehlen(cycles);
			}

			prev_RA4 = programm.GetRegisterOhneBank(PICProgramm.ADDR_PORT_A, 4);
		}

		private uint prescale_cntr = 0; // Zähler für timer

		private void TimerHochzaehlen(uint cycles)
		{
			uint current = programm.GetRegisterOhneBank(PICProgramm.ADDR_TMR0);
			uint scale = BerechneVorskalierung();

			prescale_cntr += cycles;

			while (prescale_cntr >= scale)
			{
				prescale_cntr -= scale;

				uint Result = current + 1;
				if (Result > 0xFF)
				{
					programm.TimerInterrupt();
				}

				Result %= 0x100;

				uint tmp_psc = prescale_cntr;
				programm.SetRegisterOhneBank(PICProgramm.ADDR_TMR0, Result);
				prescale_cntr = tmp_psc;
			}
		}

		private uint BerechneVorskalierung()
		{
			bool prescale_mode = programm.GetRegisterOhneBank(PICProgramm.ADDR_OPTION, PICProgramm.OPTION_BIT_PSA);

			uint scale = 0;
			scale += programm.GetRegisterOhneBank(PICProgramm.ADDR_OPTION, PICProgramm.OPTION_BIT_PS2) ? 1U : 0U;
			scale *= 2;
			scale += programm.GetRegisterOhneBank(PICProgramm.ADDR_OPTION, PICProgramm.OPTION_BIT_PS1) ? 1U : 0U;
			scale *= 2;
			scale += programm.GetRegisterOhneBank(PICProgramm.ADDR_OPTION, PICProgramm.OPTION_BIT_PS0) ? 1U : 0U;

			return prescale_mode ? 1 : (PICProgramm.SHL(2, scale));
		}
	}
}
