mirror of
https://github.com/reactos/reactos.git
synced 2025-08-05 21:12:59 +00:00
[NTVDM]
Implement Programmable Interval Timer (PIT) emulation. Fix bugs in interrupts and PIC emulation. svn path=/branches/ntvdm/; revision=59271
This commit is contained in:
parent
7765319949
commit
da877e495e
4 changed files with 219 additions and 12 deletions
|
@ -71,6 +71,12 @@ BOOLEAN BiosInitialize()
|
||||||
/* Make sure the PIC is in 8086 mode */
|
/* Make sure the PIC is in 8086 mode */
|
||||||
PicWriteData(PIC_MASTER_DATA, PIC_ICW4_8086);
|
PicWriteData(PIC_MASTER_DATA, PIC_ICW4_8086);
|
||||||
PicWriteData(PIC_SLAVE_DATA, PIC_ICW4_8086);
|
PicWriteData(PIC_SLAVE_DATA, PIC_ICW4_8086);
|
||||||
|
|
||||||
|
/* Clear the masks for both PICs */
|
||||||
|
PicWriteData(PIC_MASTER_DATA, 0x00);
|
||||||
|
PicWriteData(PIC_SLAVE_DATA, 0x00);
|
||||||
|
|
||||||
|
PitInitialize();
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
@ -251,4 +257,9 @@ VOID BiosVideoService()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VOID BiosHandleIrq(BYTE IrqNumber)
|
||||||
|
{
|
||||||
|
PicWriteCommand(PIC_MASTER_CMD, PIC_OCW2_EOI);
|
||||||
|
}
|
||||||
|
|
||||||
/* EOF */
|
/* EOF */
|
||||||
|
|
|
@ -56,12 +56,58 @@ static VOID EmulatorWriteMemory(PVOID Context, UINT Address, LPBYTE Buffer, INT
|
||||||
|
|
||||||
static VOID EmulatorReadIo(PVOID Context, UINT Address, LPBYTE Buffer, INT Size)
|
static VOID EmulatorReadIo(PVOID Context, UINT Address, LPBYTE Buffer, INT Size)
|
||||||
{
|
{
|
||||||
// TODO: NOT IMPLEMENTED!
|
switch (Address)
|
||||||
|
{
|
||||||
|
case PIC_MASTER_CMD:
|
||||||
|
case PIC_SLAVE_CMD:
|
||||||
|
{
|
||||||
|
*Buffer = PicReadCommand(Address);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case PIC_MASTER_DATA:
|
||||||
|
case PIC_SLAVE_DATA:
|
||||||
|
{
|
||||||
|
*Buffer = PicReadData(Address);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static VOID EmulatorWriteIo(PVOID Context, UINT Address, LPBYTE Buffer, INT Size)
|
static VOID EmulatorWriteIo(PVOID Context, UINT Address, LPBYTE Buffer, INT Size)
|
||||||
{
|
{
|
||||||
// TODO: NOT IMPLEMENTED!
|
BYTE Byte = *Buffer;
|
||||||
|
|
||||||
|
switch (Address)
|
||||||
|
{
|
||||||
|
case PIT_COMMAND_PORT:
|
||||||
|
{
|
||||||
|
PitWriteCommand(Byte);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case PIT_DATA_PORT(0):
|
||||||
|
case PIT_DATA_PORT(1):
|
||||||
|
case PIT_DATA_PORT(2):
|
||||||
|
{
|
||||||
|
PitWriteData(Address - PIT_DATA_PORT(0), Byte);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case PIC_MASTER_CMD:
|
||||||
|
case PIC_SLAVE_CMD:
|
||||||
|
{
|
||||||
|
PicWriteCommand(Address, Byte);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case PIC_MASTER_DATA:
|
||||||
|
case PIC_SLAVE_DATA:
|
||||||
|
{
|
||||||
|
PicWriteData(Address, Byte);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static VOID EmulatorSoftwareInt(PVOID Context, BYTE Number)
|
static VOID EmulatorSoftwareInt(PVOID Context, BYTE Number)
|
||||||
|
@ -101,6 +147,18 @@ static VOID EmulatorSoftwareInt(PVOID Context, BYTE Number)
|
||||||
VdmRunning = FALSE;
|
VdmRunning = FALSE;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Check if this was an PIC IRQ */
|
||||||
|
if (IntNum >= BIOS_PIC_MASTER_INT && IntNum < BIOS_PIC_MASTER_INT + 8)
|
||||||
|
{
|
||||||
|
/* It was an IRQ from the master PIC */
|
||||||
|
BiosHandleIrq(IntNum - BIOS_PIC_MASTER_INT);
|
||||||
|
}
|
||||||
|
else if (IntNum >= BIOS_PIC_SLAVE_INT && IntNum < BIOS_PIC_SLAVE_INT + 8)
|
||||||
|
{
|
||||||
|
/* It was an IRQ from the slave PIC */
|
||||||
|
BiosHandleIrq(IntNum - BIOS_PIC_SLAVE_INT + 8);
|
||||||
|
}
|
||||||
|
|
||||||
switch (IntNum)
|
switch (IntNum)
|
||||||
{
|
{
|
||||||
|
@ -183,7 +241,7 @@ VOID EmulatorExecute(WORD Segment, WORD Offset)
|
||||||
|
|
||||||
VOID EmulatorInterrupt(BYTE Number)
|
VOID EmulatorInterrupt(BYTE Number)
|
||||||
{
|
{
|
||||||
LPWORD IntVecTable = (LPWORD)((ULONG_PTR)BaseAddress);
|
LPDWORD IntVecTable = (LPDWORD)((ULONG_PTR)BaseAddress);
|
||||||
UINT Segment, Offset;
|
UINT Segment, Offset;
|
||||||
|
|
||||||
/* Get the segment and offset */
|
/* Get the segment and offset */
|
||||||
|
|
|
@ -24,7 +24,64 @@ typedef struct _PIC
|
||||||
BOOLEAN ReadIsr;
|
BOOLEAN ReadIsr;
|
||||||
} PIC, *PPIC;
|
} PIC, *PPIC;
|
||||||
|
|
||||||
|
typedef struct _PIT_CHANNEL
|
||||||
|
{
|
||||||
|
BOOLEAN RateGenerator;
|
||||||
|
BOOLEAN Pulsed;
|
||||||
|
BOOLEAN FlipFlop;
|
||||||
|
BYTE AccessMode;
|
||||||
|
WORD ReloadValue;
|
||||||
|
} PIT_CHANNEL, *PPIT_CHANNEL;
|
||||||
|
|
||||||
static PIC MasterPic, SlavePic;
|
static PIC MasterPic, SlavePic;
|
||||||
|
static PIT_CHANNEL PitChannels[PIT_CHANNELS];
|
||||||
|
|
||||||
|
static DWORD WINAPI PitThread(PVOID Parameter)
|
||||||
|
{
|
||||||
|
LARGE_INTEGER Frequency, CurrentTime, LastTickTime;
|
||||||
|
LONGLONG Elapsed, Milliseconds, TicksNeeded;
|
||||||
|
UNREFERENCED_PARAMETER(Parameter);
|
||||||
|
|
||||||
|
/* Get the performance counter frequency */
|
||||||
|
if (!QueryPerformanceFrequency(&Frequency)) return EXIT_FAILURE;
|
||||||
|
if (!QueryPerformanceCounter(&LastTickTime)) return EXIT_FAILURE;
|
||||||
|
|
||||||
|
while (VdmRunning)
|
||||||
|
{
|
||||||
|
if (!QueryPerformanceCounter(&CurrentTime)) return EXIT_FAILURE;
|
||||||
|
|
||||||
|
/* Calculate the elapsed time, in PIT ticks */
|
||||||
|
Elapsed = ((CurrentTime.QuadPart - LastTickTime.QuadPart)
|
||||||
|
* PIT_BASE_FREQUENCY)
|
||||||
|
/ Frequency.QuadPart;
|
||||||
|
|
||||||
|
/* A reload value of 0 indicates 65536 */
|
||||||
|
if (PitChannels[0].ReloadValue) TicksNeeded = PitChannels[0].ReloadValue;
|
||||||
|
else TicksNeeded = 65536;
|
||||||
|
|
||||||
|
if (Elapsed < TicksNeeded)
|
||||||
|
{
|
||||||
|
/* Get the number of milliseconds */
|
||||||
|
Milliseconds = (Elapsed * 1000LL) / PIT_BASE_FREQUENCY;
|
||||||
|
|
||||||
|
/* If this number is non-zero, put the thread in the waiting state */
|
||||||
|
if (Milliseconds > 0LL) Sleep((DWORD)Milliseconds);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
LastTickTime = CurrentTime;
|
||||||
|
|
||||||
|
/* Do the IRQ */
|
||||||
|
if (PitChannels[0].RateGenerator || !PitChannels[0].Pulsed)
|
||||||
|
{
|
||||||
|
PitChannels[0].Pulsed = TRUE;
|
||||||
|
PicInterruptRequest(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
/* PUBLIC FUNCTIONS ***********************************************************/
|
/* PUBLIC FUNCTIONS ***********************************************************/
|
||||||
|
|
||||||
|
@ -165,14 +222,15 @@ VOID PicInterruptRequest(BYTE Number)
|
||||||
{
|
{
|
||||||
if (Number >= 0 && Number < 8)
|
if (Number >= 0 && Number < 8)
|
||||||
{
|
{
|
||||||
/* Check if the interrupt is busy or in a cascade */
|
/* Check if the interrupt is busy, in a cascade or masked */
|
||||||
if (MasterPic.CascadeRegister & (1 << Number)
|
if (MasterPic.CascadeRegister & (1 << Number)
|
||||||
|| MasterPic.InServiceRegister & (1 << Number))
|
|| MasterPic.InServiceRegister & (1 << Number)
|
||||||
|
|| MasterPic.MaskRegister & (1 << Number))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MasterPic.InServiceRegister |= 1 << Number;
|
if (!MasterPic.AutoEoi) MasterPic.InServiceRegister |= 1 << Number;
|
||||||
EmulatorInterrupt(MasterPic.IntOffset + Number);
|
EmulatorInterrupt(MasterPic.IntOffset + Number);
|
||||||
}
|
}
|
||||||
else if (Number >= 8 && Number < 16)
|
else if (Number >= 8 && Number < 16)
|
||||||
|
@ -189,20 +247,96 @@ VOID PicInterruptRequest(BYTE Number)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check the if the slave PIC is busy */
|
/* Check the if the slave PIC is busy or masked */
|
||||||
if (MasterPic.InServiceRegister & (1 << 2)) return;
|
if (MasterPic.InServiceRegister & (1 << 2)
|
||||||
|
|| MasterPic.MaskRegister & (1 << 2)) return;
|
||||||
|
|
||||||
/* Set the IRQ 2 bit in the master ISR */
|
/* Set the IRQ 2 bit in the master ISR */
|
||||||
MasterPic.InServiceRegister |= 1 << 2;
|
if (!MasterPic.AutoEoi) MasterPic.InServiceRegister |= 1 << 2;
|
||||||
|
|
||||||
/* Check if the interrupt is busy or in a cascade */
|
/* Check if the interrupt is busy, in a cascade or masked */
|
||||||
if (SlavePic.CascadeRegister & (1 << Number)
|
if (SlavePic.CascadeRegister & (1 << Number)
|
||||||
|| SlavePic.InServiceRegister & (1 << Number))
|
|| SlavePic.InServiceRegister & (1 << Number)
|
||||||
|
|| SlavePic.MaskRegister & (1 << Number))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SlavePic.InServiceRegister |= 1 << Number;
|
if (!SlavePic.AutoEoi) SlavePic.InServiceRegister |= 1 << Number;
|
||||||
EmulatorInterrupt(SlavePic.IntOffset + Number);
|
EmulatorInterrupt(SlavePic.IntOffset + Number);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VOID PitWriteCommand(BYTE Value)
|
||||||
|
{
|
||||||
|
BYTE Channel = Value >> 6;
|
||||||
|
BYTE Mode = (Value >> 1) & 0x07;
|
||||||
|
|
||||||
|
/* Set the access mode and reset flip-flop */
|
||||||
|
// TODO: Support latch command!
|
||||||
|
PitChannels[Channel].AccessMode = (Value >> 4) & 3;
|
||||||
|
PitChannels[Channel].FlipFlop = FALSE;
|
||||||
|
|
||||||
|
switch (Mode)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
case 4:
|
||||||
|
{
|
||||||
|
PitChannels[Channel].RateGenerator = FALSE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
case 3:
|
||||||
|
{
|
||||||
|
PitChannels[Channel].RateGenerator = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VOID PitWriteData(BYTE Channel, BYTE Value)
|
||||||
|
{
|
||||||
|
/* Use the flip-flop for access mode 3 */
|
||||||
|
if (PitChannels[Channel].AccessMode == 3)
|
||||||
|
{
|
||||||
|
PitChannels[Channel].AccessMode = PitChannels[Channel].FlipFlop ? 1 : 2;
|
||||||
|
PitChannels[Channel].FlipFlop = !PitChannels[Channel].FlipFlop;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (PitChannels[Channel].AccessMode)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
{
|
||||||
|
/* Low byte */
|
||||||
|
PitChannels[Channel].ReloadValue &= 0xFF00;
|
||||||
|
PitChannels[Channel].ReloadValue |= Value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
{
|
||||||
|
/* High byte */
|
||||||
|
PitChannels[Channel].ReloadValue &= 0x00FF;
|
||||||
|
PitChannels[Channel].ReloadValue |= Value << 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VOID PitInitialize()
|
||||||
|
{
|
||||||
|
HANDLE ThreadHandle;
|
||||||
|
|
||||||
|
/* Set up channel 0 */
|
||||||
|
PitChannels[0].ReloadValue = 0;
|
||||||
|
PitChannels[0].RateGenerator = TRUE;
|
||||||
|
PitChannels[0].Pulsed = FALSE;
|
||||||
|
PitChannels[0].AccessMode = 3;
|
||||||
|
PitChannels[0].FlipFlop = FALSE;
|
||||||
|
|
||||||
|
/* Create the PIT timer thread */
|
||||||
|
ThreadHandle = CreateThread(NULL, 0, PitThread, NULL, 0, NULL);
|
||||||
|
|
||||||
|
/* We don't need the handle */
|
||||||
|
CloseHandle(ThreadHandle);
|
||||||
|
}
|
||||||
|
|
|
@ -208,11 +208,15 @@ VOID DosInt20h(WORD CodeSegment);
|
||||||
VOID DosInt21h(WORD CodeSegment);
|
VOID DosInt21h(WORD CodeSegment);
|
||||||
VOID DosBreakInterrupt();
|
VOID DosBreakInterrupt();
|
||||||
VOID BiosVideoService();
|
VOID BiosVideoService();
|
||||||
|
VOID BiosHandleIrq(BYTE IrqNumber);
|
||||||
BYTE PicReadCommand(BYTE Port);
|
BYTE PicReadCommand(BYTE Port);
|
||||||
VOID PicWriteCommand(BYTE Port, BYTE Value);
|
VOID PicWriteCommand(BYTE Port, BYTE Value);
|
||||||
BYTE PicReadData(BYTE Port);
|
BYTE PicReadData(BYTE Port);
|
||||||
VOID PicWriteData(BYTE Port, BYTE Value);
|
VOID PicWriteData(BYTE Port, BYTE Value);
|
||||||
VOID PicInterruptRequest(BYTE Number);
|
VOID PicInterruptRequest(BYTE Number);
|
||||||
|
VOID PitInitialize();
|
||||||
|
VOID PitWriteCommand(BYTE Value);
|
||||||
|
VOID PitWriteData(BYTE Channel, BYTE Value);
|
||||||
VOID EmulatorSetStack(WORD Segment, WORD Offset);
|
VOID EmulatorSetStack(WORD Segment, WORD Offset);
|
||||||
VOID EmulatorExecute(WORD Segment, WORD Offset);
|
VOID EmulatorExecute(WORD Segment, WORD Offset);
|
||||||
VOID EmulatorInterrupt(BYTE Number);
|
VOID EmulatorInterrupt(BYTE Number);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue