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