using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Cosmos.Core;
namespace Cosmos.HAL
{
///
/// Programmable Interval Timer
/// with 1,193181818... MHz
///
public class PIT : Device
{
public class PITTimer : IDisposable
{
internal int NSRemaining;
public int NanosecondsTimeout;
public bool Recuring;
internal int ID = -1;
public int TimerID
{
get
{
return ID;
}
}
public delegate void dOnTrigger();
public dOnTrigger HandleTrigger;
public PITTimer(dOnTrigger HandleOnTrigger, int NanosecondsTimeout, bool Recuring)
{
this.HandleTrigger = HandleOnTrigger;
this.NanosecondsTimeout = NanosecondsTimeout;
this.NSRemaining = this.NanosecondsTimeout;
this.Recuring = Recuring;
}
public PITTimer(dOnTrigger HandleOnTrigger, int NanosecondsTimeout, int NanosecondsLeft)
{
this.HandleTrigger = HandleOnTrigger;
this.NanosecondsTimeout = NanosecondsTimeout;
this.NSRemaining = NanosecondsLeft;
this.Recuring = true;
}
~PITTimer()
{
Dispose();
}
public void Dispose()
{
if (ID != -1)
{
Global.PIT.UnregisterTimer(ID);
}
}
}
protected Core.IOGroup.PIT IO = Core.Global.BaseIOGroups.PIT;
private List ActiveHandlers = new List();
private ushort _T0Countdown = 65535;
private ushort _T2Countdown = 65535;
private int TimerCounter = 0;
private bool WaitSignaled = false;
public const uint PITFrequency = 1193180;
public const uint PITDelayNS = 838;
public bool T0RateGen = false;
public PIT()
{
INTs.SetIrqHandler(0x00, HandleIRQ);
T0Countdown = 65535;
}
public ushort T0Countdown
{
get
{
return _T0Countdown;
}
set
{
_T0Countdown = value;
IO.Command.Byte = (byte)(T0RateGen ? 0x34 : 0x30);
IO.Data0.Byte = (byte)(value & 0xFF);
IO.Data0.Byte = (byte)(value >> 8);
}
}
public uint T0Frequency
{
get
{
return (PITFrequency / ((uint)_T0Countdown));
}
set
{
if (value < 19 || value > 1193180)
{
throw new ArgumentException("Frequency must be between 19 and 1193180!");
}
T0Countdown = (ushort)(PITFrequency / value);
}
}
public uint T0DelyNS
{
get
{
return (PITDelayNS * _T0Countdown);
}
set
{
if (value > 54918330)
throw new ArgumentException("Delay must be no greater that 54918330");
T0Countdown = (ushort)(value / PITDelayNS);
}
}
public ushort T2Countdown
{
get
{
return _T2Countdown;
}
set
{
_T2Countdown = value;
IO.Command.Byte = 0xB6;
IO.Data0.Byte = (byte)(value & 0xFF);
IO.Data0.Byte = (byte)(value >> 8);
}
}
public uint T2Frequency
{
get
{
return (PITFrequency / ((uint)_T2Countdown));
}
set
{
if (value < 19 || value > 1193180)
{
throw new ArgumentException("Frequency must be between 19 and 1193180!");
}
T2Countdown = (ushort)(PITFrequency / value);
}
}
public uint T2DelyNS
{
get
{
return (PITDelayNS * _T2Countdown);
}
set
{
if (value > 54918330)
throw new ArgumentException("Delay must be no greater than 54918330");
T2Countdown = (ushort)(value / PITDelayNS);
}
}
//TODO: Why is sound in PIT? Is it a function of the PIT?
//Channel 3 is for the pc speaker ^
public void EnableSound()
{
//IO.Port61.Byte = (byte)(IO.Port61.Byte | 0x03);
}
public void DisableSound()
{
//IO.Port61.Byte = (byte)(IO.Port61.Byte | 0xFC);
}
public void PlaySound(int aFreq)
{
EnableSound();
T2Frequency = (uint)aFreq;
}
public void MuteSound()
{
DisableSound();
}
private void SignalWait()
{
WaitSignaled = true;
}
public void Wait(uint TimeoutMS)
{
WaitSignaled = false;
RegisterTimer(new PITTimer(SignalWait, (int)(TimeoutMS * 1000000), false));
while (!WaitSignaled)
{
Core.Global.CPU.Halt();
}
}
public void WaitNS(int TimeoutNS)
{
WaitSignaled = false;
RegisterTimer(new PITTimer(SignalWait, TimeoutNS, false));
while (!WaitSignaled)
{
Core.Global.CPU.Halt();
}
}
private void HandleIRQ(ref INTs.IRQContext aContext)
{
int T0Delay = (int)T0DelyNS;
PITTimer hndlr = null;
if (ActiveHandlers.Count > 0)
{
T0Countdown = 65535;
}
for (int i = ActiveHandlers.Count - 1; i >= 0; i--)
{
hndlr = ActiveHandlers[i];
hndlr.NSRemaining -= T0Delay;
if (hndlr.NSRemaining < 1)
{
if (hndlr.Recuring)
{
hndlr.NSRemaining = hndlr.NanosecondsTimeout;
}
else
{
hndlr.ID = -1;
ActiveHandlers.RemoveAt(i);
}
hndlr.HandleTrigger();
}
}
}
public int RegisterTimer(PITTimer timer)
{
if (timer.ID != -1)
{
throw new InvalidOperationException("Timer has already been registered!");
}
timer.ID = (TimerCounter++);
ActiveHandlers.Add(timer);
T0Countdown = 65535;
return timer.ID;
}
public void UnregisterTimer(int timerid)
{
for (int i = 0; i < ActiveHandlers.Count; i++)
{
if (ActiveHandlers[i].ID == timerid)
{
ActiveHandlers[i].ID = -1;
ActiveHandlers.RemoveAt(i);
return;
}
}
}
}
}