mirror of
https://github.com/danbulant/Cosmos
synced 2026-05-19 12:30:32 +00:00
280 lines
5.2 KiB
C#
280 lines
5.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using Cosmos.Core;
|
|
|
|
namespace Cosmos.HAL
|
|
{
|
|
/// <summary>
|
|
/// Programmable Interval Timer
|
|
/// with 1,193181818... MHz
|
|
/// </summary>
|
|
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<PITTimer> ActiveHandlers = new List<PITTimer>();
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|