- Completely rewrite the timing system. Replace hardcoded callbacks
with dynamic hardware timers.
- Finish implementing the PS/2 mouse.
- Fix the DOS mouse driver.
Inspired by a patch by Stefano Toncich (Tonix) (see CORE-9166).
CORE-9166 #comment A different fix was committed in r67219.


svn path=/trunk/; revision=67219
This commit is contained in:
Aleksandar Andrejevic 2015-04-17 00:20:39 +00:00
parent 33d8a4b74b
commit e4a1abcc2a
17 changed files with 886 additions and 455 deletions

View file

@ -17,6 +17,7 @@
#include "io.h"
#include "hardware/mouse.h"
#include "hardware/ps2.h"
// HACK: For the PS/2 bypass and MOUSE.COM driver direct call
#include "dos/mouse32.h"
@ -28,13 +29,6 @@
// Mouse IRQ 12
static VOID WINAPI BiosMouseIrq(LPWORD Stack)
{
// HACK!! Call directly the MOUSE.COM driver instead of going
// through the regular interfaces!!
extern COORD DosNewPosition;
extern WORD DosButtonState;
DosMouseUpdatePosition(&DosNewPosition);
DosMouseUpdateButtons(DosButtonState);
PicIRQComplete(Stack);
}
@ -118,6 +112,7 @@ BOOLEAN MouseBios32Initialize(VOID)
{
/* Set up the HW vector interrupts */
EnableHwIRQ(12, BiosMouseIrq);
return TRUE;
}

View file

@ -14,12 +14,13 @@
#include "emulator.h"
#include "cpu/cpu.h"
// #include "clock.h"
#include "clock.h"
#include "hardware/cmos.h"
#include "hardware/ps2.h"
#include "hardware/pit.h"
#include "hardware/video/vga.h"
#include "hardware/mouse.h"
/* Extra PSDK/NDK Headers */
#include <ndk/kefuncs.h>
@ -28,8 +29,7 @@
/*
* Activate IPS_DISPLAY if you want to display the
* number of instructions per second, as well as
* the computed number of ticks for the PIT.
* number of instructions per second.
*/
// #define IPS_DISPLAY
@ -38,27 +38,27 @@
*/
// #define WORKING_TIMER
/* Processor speed */
#define STEPS_PER_CYCLE 256
#define IRQ1_CYCLES 16
#define IRQ12_CYCLES 16
/* VARIABLES ******************************************************************/
static LIST_ENTRY Timers;
static LARGE_INTEGER StartPerfCount, Frequency;
static LARGE_INTEGER LastTimerTick, LastRtcTick, Counter;
static LONGLONG TimerTicks;
static DWORD StartTickCount, CurrentTickCount;
static DWORD LastClockUpdate;
static DWORD LastVerticalRefresh;
static DWORD LastIrq1Tick = 0, LastIrq12Tick = 0;
static DWORD StartTickCount;
#ifdef IPS_DISPLAY
static DWORD LastCyclePrintout;
static ULONGLONG Cycles = 0;
static ULONGLONG Cycles = 0ULL;
#endif
/* PRIVATE FUNCTIONS **********************************************************/
#ifdef IPS_DISPLAY
static VOID FASTCALL IpsDisplayCallback(ULONGLONG ElapsedTime)
{
DPRINT1("NTVDM: %I64u Instructions Per Second\n", Cycles / ElapsedTime);
Cycles = 0ULL;
}
#endif
/* PUBLIC FUNCTIONS ***********************************************************/
@ -67,112 +67,159 @@ VOID ClockUpdate(VOID)
{
extern BOOLEAN CpuRunning;
UINT i;
// LARGE_INTEGER Counter;
#ifdef WORKING_TIMER
DWORD PitResolution;
#endif
DWORD RtcFrequency;
PLIST_ENTRY Entry;
LARGE_INTEGER Counter;
while (VdmRunning && CpuRunning)
{
/* Get the current number of ticks */
DWORD CurrentTickCount = GetTickCount();
#ifdef WORKING_TIMER
PitResolution = PitGetResolution();
if ((PitResolution <= 1000) && (RtcFrequency <= 1000))
{
/* Calculate the approximate performance counter value instead */
Counter.QuadPart = StartPerfCount.QuadPart
+ ((CurrentTickCount - StartTickCount)
* Frequency.QuadPart) / 1000;
}
else
#endif
RtcFrequency = RtcGetTicksPerSecond();
{
/* Get the current performance counter value */
/// DWORD_PTR oldmask = SetThreadAffinityMask(GetCurrentThread(), 0);
NtQueryPerformanceCounter(&Counter, NULL);
/// SetThreadAffinityMask(GetCurrentThread(), oldmask);
}
/* Get the current number of ticks */
CurrentTickCount = GetTickCount();
/* Continue CPU emulation */
for (i = 0; VdmRunning && CpuRunning && (i < STEPS_PER_CYCLE); i++)
{
CpuStep();
#ifdef WORKING_TIMER
if ((PitResolution <= 1000) && (RtcFrequency <= 1000))
#ifdef IPS_DISPLAY
++Cycles;
#endif
}
for (Entry = Timers.Flink; Entry != &Timers; Entry = Entry->Flink)
{
ULONGLONG Ticks = (ULONGLONG)-1;
PHARDWARE_TIMER Timer = CONTAINING_RECORD(Entry, HARDWARE_TIMER, Link);
ASSERT((Timer->EnableCount > 0) && (Timer->Flags & HARDWARE_TIMER_ENABLED));
if (Timer->Delay)
{
if (Timer->Flags & HARDWARE_TIMER_PRECISE)
{
/* Use the performance counter for precise timers */
if (Counter.QuadPart <= Timer->LastTick.QuadPart) continue;
Ticks = (Counter.QuadPart - Timer->LastTick.QuadPart) / Timer->Delay;
}
else
{
/* Use the regular tick count for normal timers */
if (CurrentTickCount <= Timer->LastTick.LowPart) continue;
Ticks = (CurrentTickCount - Timer->LastTick.LowPart) / (ULONG)Timer->Delay;
}
if (Ticks == 0) continue;
}
Timer->Callback(Ticks);
if (Timer->Flags & HARDWARE_TIMER_ONESHOT)
{
/* Disable this timer */
DisableHardwareTimer(Timer);
}
/* Update the time of the last timer tick */
Timer->LastTick.QuadPart += Ticks * Timer->Delay;
}
}
}
PHARDWARE_TIMER CreateHardwareTimer(ULONG Flags, ULONG Delay, PHARDWARE_TIMER_PROC Callback)
{
PHARDWARE_TIMER Timer;
Timer = (PHARDWARE_TIMER)RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(HARDWARE_TIMER));
if (Timer == NULL) return NULL;
Timer->Flags = Flags & ~HARDWARE_TIMER_ENABLED;
Timer->EnableCount = 0;
Timer->Callback = Callback;
SetHardwareTimerDelay(Timer, (ULONGLONG)Delay);
if (Flags & HARDWARE_TIMER_ENABLED) EnableHardwareTimer(Timer);
return Timer;
}
VOID EnableHardwareTimer(PHARDWARE_TIMER Timer)
{
/* Increment the count */
Timer->EnableCount++;
/* Check if the count is above 0 but the timer isn't enabled */
if ((Timer->EnableCount > 0) && !(Timer->Flags & HARDWARE_TIMER_ENABLED))
{
/* Calculate the approximate performance counter value instead */
Counter.QuadPart = StartPerfCount.QuadPart
+ (CurrentTickCount - StartTickCount)
* (Frequency.QuadPart / 1000);
if (Timer->Flags & HARDWARE_TIMER_PRECISE)
{
NtQueryPerformanceCounter(&Timer->LastTick, NULL);
}
else
{
Timer->LastTick.LowPart = GetTickCount();
}
Timer->Flags |= HARDWARE_TIMER_ENABLED;
InsertTailList(&Timers, &Timer->Link);
}
}
VOID DisableHardwareTimer(PHARDWARE_TIMER Timer)
{
/* Decrement the count */
Timer->EnableCount--;
/* Check if the count is 0 or less but the timer is enabled */
if ((Timer->EnableCount <= 0) && (Timer->Flags & HARDWARE_TIMER_ENABLED))
{
/* Disable the timer */
Timer->Flags &= ~HARDWARE_TIMER_ENABLED;
RemoveEntryList(&Timer->Link);
}
}
VOID SetHardwareTimerDelay(PHARDWARE_TIMER Timer, ULONGLONG NewDelay)
{
if (Timer->Flags & HARDWARE_TIMER_PRECISE)
{
/* Convert the delay from nanoseconds to performance counter ticks */
Timer->Delay = (NewDelay * Frequency.QuadPart + 500000000ULL) / 1000000000ULL;
}
else
#endif
{
/* Get the current performance counter value */
/// DWORD_PTR oldmask = SetThreadAffinityMask(GetCurrentThread(), 0);
NtQueryPerformanceCounter(&Counter, NULL);
/// SetThreadAffinityMask(GetCurrentThread(), oldmask);
Timer->Delay = NewDelay;
}
}
/* Get the number of PIT ticks that have passed */
TimerTicks = ((Counter.QuadPart - LastTimerTick.QuadPart)
* PIT_BASE_FREQUENCY) / Frequency.QuadPart;
/* Update the PIT */
if (TimerTicks > 0)
{
PitClock(TimerTicks);
LastTimerTick = Counter;
}
/* Check for RTC update */
if ((CurrentTickCount - LastClockUpdate) >= 1000)
{
RtcTimeUpdate();
LastClockUpdate = CurrentTickCount;
}
/* Check for RTC periodic tick */
if ((Counter.QuadPart - LastRtcTick.QuadPart)
>= (Frequency.QuadPart / (LONGLONG)RtcFrequency))
{
RtcPeriodicTick();
LastRtcTick = Counter;
}
/* Check for vertical retrace */
if ((CurrentTickCount - LastVerticalRefresh) >= 15)
{
VgaRefreshDisplay();
LastVerticalRefresh = CurrentTickCount;
}
if ((CurrentTickCount - LastIrq1Tick) >= IRQ1_CYCLES)
{
GenerateIrq1();
LastIrq1Tick = CurrentTickCount;
}
if ((CurrentTickCount - LastIrq12Tick) >= IRQ12_CYCLES)
{
GenerateIrq12();
LastIrq12Tick = CurrentTickCount;
}
/* Horizontal retrace occurs as fast as possible */
VgaHorizontalRetrace();
/* Continue CPU emulation */
for (i = 0; VdmRunning && CpuRunning && (i < STEPS_PER_CYCLE); i++)
{
CpuStep();
#ifdef IPS_DISPLAY
++Cycles;
#endif
}
#ifdef IPS_DISPLAY
if ((CurrentTickCount - LastCyclePrintout) >= 1000)
{
DPRINT1("NTVDM: %I64u Instructions Per Second; TimerTicks = %I64d\n", Cycles * 1000 / (CurrentTickCount - LastCyclePrintout), TimerTicks);
LastCyclePrintout = CurrentTickCount;
Cycles = 0;
}
#endif
}
VOID DestroyHardwareTimer(PHARDWARE_TIMER Timer)
{
if (Timer->Flags & HARDWARE_TIMER_ENABLED) RemoveEntryList(&Timer->Link);
RtlFreeHeap(RtlGetProcessHeap(), 0, Timer);
}
BOOLEAN ClockInitialize(VOID)
{
#ifdef IPS_DISPLAY
PHARDWARE_TIMER IpsTimer;
#endif
InitializeListHead(&Timers);
/* Initialize the performance counter (needed for hardware timers) */
/* Find the starting performance */
NtQueryPerformanceCounter(&StartPerfCount, &Frequency);
@ -185,15 +232,16 @@ BOOLEAN ClockInitialize(VOID)
/* Find the starting tick count */
StartTickCount = GetTickCount();
/* Set the different last counts to the starting count */
LastClockUpdate = LastVerticalRefresh =
#ifdef IPS_DISPLAY
LastCyclePrintout =
#endif
StartTickCount;
/* Set the last timer ticks to the current time */
LastTimerTick = LastRtcTick = StartPerfCount;
IpsTimer = CreateHardwareTimer(HARDWARE_TIMER_ENABLED, 1000, IpsDisplayCallback);
if (IpsTimer == NULL)
{
wprintf(L"FATAL: Cannot create IPS display timer.\n");
return FALSE;
}
#endif
return TRUE;
}

View file

@ -10,8 +10,39 @@
#ifndef _CLOCK_H_
#define _CLOCK_H_
#include <ndk/rtlfuncs.h>
/* DEFINITIONS ****************************************************************/
#define HARDWARE_TIMER_ENABLED (1 << 0)
#define HARDWARE_TIMER_ONESHOT (1 << 1)
#define HARDWARE_TIMER_PRECISE (1 << 2)
typedef VOID (FASTCALL *PHARDWARE_TIMER_PROC)(ULONGLONG ElapsedTime);
typedef struct _HARDWARE_TIMER
{
LIST_ENTRY Link;
ULONG Flags;
LONG EnableCount;
ULONGLONG Delay;
PHARDWARE_TIMER_PROC Callback;
LARGE_INTEGER LastTick;
} HARDWARE_TIMER, *PHARDWARE_TIMER;
/* FUNCTIONS ******************************************************************/
PHARDWARE_TIMER CreateHardwareTimer
(
ULONG Flags,
ULONG Delay, /* milliseconds for normal timers, nanoseconds for precise timers */
PHARDWARE_TIMER_PROC Callback
);
VOID EnableHardwareTimer(PHARDWARE_TIMER Timer);
VOID DisableHardwareTimer(PHARDWARE_TIMER Timer);
VOID SetHardwareTimerDelay(PHARDWARE_TIMER Timer, ULONGLONG NewDelay);
VOID DestroyHardwareTimer(PHARDWARE_TIMER Timer);
VOID ClockUpdate(VOID);
BOOLEAN ClockInitialize(VOID);

View file

@ -54,6 +54,8 @@ enum
EMULATOR_EXCEPTION_PAGE_FAULT
};
#endif
extern BOOLEAN CpuRunning;
extern FAST486_STATE EmulatorContext;
/* FUNCTIONS ******************************************************************/

View file

@ -423,7 +423,7 @@ static VOID WINAPI DosStart(LPWORD Stack)
RegisterBop(BOP_START_DOS, NULL);
/* Load the mouse driver */
// DosMouseInitialize();
DosMouseInitialize();
#ifndef STANDALONE

View file

@ -13,36 +13,45 @@
#include "emulator.h"
#include "cpu/cpu.h"
#include "int32.h"
#include "hardware/mouse.h"
#include "hardware/ps2.h"
#include "hardware/pic.h"
#include "hardware/video/vga.h"
#include "mouse32.h"
#include "bios/bios.h"
#include "bios/bios32/bios32p.h"
#include "io.h"
#include "dos32krnl/dos.h"
/* PRIVATE VARIABLES **********************************************************/
static BOOLEAN DriverEnabled = TRUE;
static MOUSE_DRIVER_STATE DriverState;
#define MICKEYS_PER_CELL_HORIZ 8
#define MICKEYS_PER_CELL_VERT 16
/**/
COORD DosNewPosition;
WORD DosButtonState;
/**/
static MOUSE_PACKET Packet;
static INT ByteCounter = 0;
static BOOLEAN DriverEnabled = FALSE;
static MOUSE_DRIVER_STATE DriverState;
static DWORD OldIrqHandler;
/* PRIVATE FUNCTIONS **********************************************************/
extern VOID WINAPI BiosMouseIrq(LPWORD Stack);
static VOID PaintMouseCursor(VOID)
{
if (Bda->VideoMode <= 3)
{
WORD Character;
WORD CellX = DriverState.Position.X / 8;
WORD CellY = DriverState.Position.Y / 8;
DWORD VideoAddress = TO_LINEAR(TEXT_VIDEO_SEG, Bda->VideoPage * Bda->VideoPageSize);
EmulatorReadMemory(&EmulatorContext,
VideoAddress
+ (DriverState.Position.Y * Bda->ScreenColumns
+ DriverState.Position.X) * sizeof(WORD),
+ (CellY * Bda->ScreenColumns + CellX) * sizeof(WORD),
(LPVOID)&Character,
sizeof(WORD));
@ -52,8 +61,7 @@ static VOID PaintMouseCursor(VOID)
EmulatorWriteMemory(&EmulatorContext,
VideoAddress
+ (DriverState.Position.Y * Bda->ScreenColumns
+ DriverState.Position.X) * sizeof(WORD),
+ (CellY * Bda->ScreenColumns + CellX) * sizeof(WORD),
(LPVOID)&Character,
sizeof(WORD));
}
@ -68,12 +76,13 @@ static VOID EraseMouseCursor(VOID)
{
if (Bda->VideoMode <= 3)
{
WORD CellX = DriverState.Position.X / 8;
WORD CellY = DriverState.Position.Y / 8;
DWORD VideoAddress = TO_LINEAR(TEXT_VIDEO_SEG, Bda->VideoPage * Bda->VideoPageSize);
EmulatorWriteMemory(&EmulatorContext,
VideoAddress
+ (DriverState.Position.Y * Bda->ScreenColumns
+ DriverState.Position.X) * sizeof(WORD),
+ (CellY * Bda->ScreenColumns + CellX) * sizeof(WORD),
(LPVOID)&DriverState.Character,
sizeof(WORD));
}
@ -84,10 +93,45 @@ static VOID EraseMouseCursor(VOID)
}
}
static VOID ToMouseCoordinates(PCOORD Position)
{
COORD Resolution = VgaGetDisplayResolution();
WORD Width = DriverState.MaxX - DriverState.MinX + 1;
WORD Height = DriverState.MaxY - DriverState.MinY + 1;
if (!VgaGetDoubleVisionState(NULL, NULL))
{
Resolution.X *= 8;
Resolution.Y *= 8;
}
Position->X = DriverState.MinX + ((Position->X * Width) / Resolution.X);
Position->Y = DriverState.MinY + ((Position->Y * Height) / Resolution.Y);
}
static VOID FromMouseCoordinates(PCOORD Position)
{
COORD Resolution = VgaGetDisplayResolution();
WORD Width = DriverState.MaxX - DriverState.MinX + 1;
WORD Height = DriverState.MaxY - DriverState.MinY + 1;
if (!VgaGetDoubleVisionState(NULL, NULL))
{
Resolution.X *= 8;
Resolution.Y *= 8;
}
Position->X = (((Position->X - DriverState.MinX) * Resolution.X) / Width);
Position->Y = (((Position->Y - DriverState.MinY) * Resolution.Y) / Height);
}
static VOID CallMouseUserHandlers(USHORT CallMask)
{
USHORT i;
USHORT AX, BX, CX, DX, SI, DI;
COORD Position = DriverState.Position;
ToMouseCoordinates(&Position);
/* Call handler 0 */
if ((DriverState.Handler0.CallMask & CallMask) != 0 &&
@ -108,10 +152,10 @@ static VOID CallMouseUserHandlers(USHORT CallMask)
setAX(CallMask);
setBX(DriverState.ButtonState);
setCX(DriverState.Position.X);
setDX(DriverState.Position.Y);
setSI(DriverState.MickeysPerCellHoriz);
setDI(DriverState.MickeysPerCellVert);
setCX(Position.X);
setDX(Position.Y);
setSI(MICKEYS_PER_CELL_HORIZ);
setDI(MICKEYS_PER_CELL_VERT);
DPRINT("Calling Handler0 %04X:%04X with CallMask 0x%04X\n",
HIWORD(DriverState.Handler0.Callback),
@ -150,10 +194,10 @@ static VOID CallMouseUserHandlers(USHORT CallMask)
setAX(CallMask);
setBX(DriverState.ButtonState);
setCX(DriverState.Position.X);
setDX(DriverState.Position.Y);
setSI(DriverState.MickeysPerCellHoriz);
setDI(DriverState.MickeysPerCellVert);
setCX(Position.X);
setDX(Position.Y);
setSI(MICKEYS_PER_CELL_HORIZ);
setDI(MICKEYS_PER_CELL_VERT);
DPRINT1("Calling Handler[%d] %04X:%04X with CallMask 0x%04X\n",
i,
@ -174,6 +218,127 @@ static VOID CallMouseUserHandlers(USHORT CallMask)
}
}
static inline VOID DosUpdatePosition(PCOORD NewPosition)
{
COORD Resolution = VgaGetDisplayResolution();
/* Check for text mode */
if (!VgaGetDoubleVisionState(NULL, NULL))
{
Resolution.X *= 8;
Resolution.Y *= 8;
}
if (DriverState.ShowCount > 0) EraseMouseCursor();
DriverState.Position = *NewPosition;
/* Apply the clipping rectangle */
if (DriverState.Position.X < DriverState.MinX) DriverState.Position.X = DriverState.MinX;
if (DriverState.Position.X > DriverState.MaxX) DriverState.Position.X = DriverState.MaxX;
if (DriverState.Position.Y < DriverState.MinY) DriverState.Position.Y = DriverState.MinY;
if (DriverState.Position.Y > DriverState.MaxY) DriverState.Position.Y = DriverState.MaxY;
if (DriverState.ShowCount > 0) PaintMouseCursor();
/* Call the mouse handlers */
CallMouseUserHandlers(0x0001); // We use MS MOUSE v1.0+ format
}
static inline VOID DosUpdateButtons(BYTE ButtonState)
{
USHORT i;
USHORT CallMask = 0x0000; // We use MS MOUSE v1.0+ format
for (i = 0; i < NUM_MOUSE_BUTTONS; i++)
{
BOOLEAN OldState = (DriverState.ButtonState >> i) & 1;
BOOLEAN NewState = (ButtonState >> i) & 1;
if (NewState > OldState)
{
/* Mouse press */
DriverState.PressCount[i]++;
DriverState.LastPress[i] = DriverState.Position;
CallMask |= (1 << (2 * i + 1));
}
else if (NewState < OldState)
{
/* Mouse release */
DriverState.ReleaseCount[i]++;
DriverState.LastRelease[i] = DriverState.Position;
CallMask |= (1 << (2 * i + 2));
}
}
DriverState.ButtonState = ButtonState;
/* Call the mouse handlers */
CallMouseUserHandlers(CallMask);
}
static VOID WINAPI DosMouseIrq(LPWORD Stack)
{
COORD Position;
BYTE ButtonState;
switch (ByteCounter)
{
case 0:
{
Packet.Flags = IOReadB(PS2_DATA_PORT);
break;
}
case 1:
{
Packet.HorzCounter = IOReadB(PS2_DATA_PORT);
break;
}
case 2:
{
Packet.VertCounter = IOReadB(PS2_DATA_PORT);
break;
}
default:
{
/* Shouldn't happen */
ASSERT(FALSE);
}
}
if (++ByteCounter == 3)
{
SHORT DeltaX = Packet.HorzCounter;
SHORT DeltaY = Packet.VertCounter;
/* Adjust the sign */
if (Packet.Flags & MOUSE_X_SIGN) DeltaX = -DeltaX;
if (Packet.Flags & MOUSE_Y_SIGN) DeltaY = -DeltaY;
DriverState.HorizCount += DeltaX;
DriverState.VertCount += DeltaY;
ByteCounter = 0;
}
/*
* Get the absolute position directly from the mouse, this is the only
* way to perfectly synchronize the host and guest mouse pointer.
*/
MouseGetDataFast(&Position, &ButtonState);
/* Call the update subroutines */
DosUpdatePosition(&Position);
DosUpdateButtons(ButtonState);
/* Complete the IRQ */
PicIRQComplete(Stack);
}
static VOID WINAPI DosMouseService(LPWORD Stack)
{
switch (getAX())
@ -183,10 +348,15 @@ static VOID WINAPI DosMouseService(LPWORD Stack)
{
SHORT i;
DriverEnabled = TRUE;
DriverState.ShowCount = 0;
DriverState.ButtonState = 0;
/* Initialize the default clipping range */
DriverState.MinX = 0;
DriverState.MaxX = MOUSE_MAX_HORIZ - 1;
DriverState.MinY = 0;
DriverState.MaxY = MOUSE_MAX_VERT - 1;
/* Set the default text cursor */
DriverState.TextCursor.ScreenMask = 0xFFFF; /* Display everything */
DriverState.TextCursor.CursorMask = 0xFF00; /* ... but with inverted attributes */
@ -237,10 +407,6 @@ static VOID WINAPI DosMouseService(LPWORD Stack)
DriverState.PressCount[i] = DriverState.ReleaseCount[i] = 0;
}
/* Initialize the resolution */
DriverState.MickeysPerCellHoriz = 8;
DriverState.MickeysPerCellVert = 16;
/* Return mouse information */
setAX(0xFFFF); // Hardware & driver installed
setBX(NUM_MOUSE_BUTTONS);
@ -252,7 +418,7 @@ static VOID WINAPI DosMouseService(LPWORD Stack)
case 0x01:
{
DriverState.ShowCount++;
if (DriverState.ShowCount > 0) PaintMouseCursor();
if (DriverState.ShowCount == 1) PaintMouseCursor();
break;
}
@ -261,7 +427,7 @@ static VOID WINAPI DosMouseService(LPWORD Stack)
case 0x02:
{
DriverState.ShowCount--;
if (DriverState.ShowCount <= 0) EraseMouseCursor();
if (DriverState.ShowCount == 0) EraseMouseCursor();
break;
}
@ -269,23 +435,22 @@ static VOID WINAPI DosMouseService(LPWORD Stack)
/* Return Position And Button Status */
case 0x03:
{
COORD Position = DriverState.Position;
ToMouseCoordinates(&Position);
setBX(DriverState.ButtonState);
setCX(DriverState.Position.X);
setDX(DriverState.Position.Y);
setCX(Position.X);
setDX(Position.Y);
break;
}
/* Position Mouse Cursor */
case 0x04:
{
POINT Point;
Point.x = getCX();
Point.y = getDX();
ClientToScreen(GetConsoleWindow(), &Point);
SetCursorPos(Point.x, Point.y);
COORD Position = { getCX(), getDX() };
FromMouseCoordinates(&Position);
DriverState.Position = Position;
break;
}
@ -293,11 +458,13 @@ static VOID WINAPI DosMouseService(LPWORD Stack)
case 0x05:
{
WORD Button = getBX();
COORD LastPress = DriverState.LastPress[Button];
ToMouseCoordinates(&LastPress);
setAX(DriverState.ButtonState);
setBX(DriverState.PressCount[Button]);
setCX(DriverState.LastPress[Button].X);
setDX(DriverState.LastPress[Button].Y);
setCX(LastPress.X);
setDX(LastPress.Y);
/* Reset the counter */
DriverState.PressCount[Button] = 0;
@ -309,11 +476,13 @@ static VOID WINAPI DosMouseService(LPWORD Stack)
case 0x06:
{
WORD Button = getBX();
COORD LastRelease = DriverState.LastRelease[Button];
ToMouseCoordinates(&LastRelease);
setAX(DriverState.ButtonState);
setBX(DriverState.ReleaseCount[Button]);
setCX(DriverState.LastRelease[Button].X);
setDX(DriverState.LastRelease[Button].Y);
setCX(LastRelease.X);
setDX(LastRelease.Y);
/* Reset the counter */
DriverState.ReleaseCount[Button] = 0;
@ -322,6 +491,24 @@ static VOID WINAPI DosMouseService(LPWORD Stack)
}
/* Define Horizontal Cursor Range */
case 0x07:
{
DPRINT("Setting mouse horizontal range: %u - %u\n", getCX(), getDX());
DriverState.MinX = getCX();
DriverState.MaxX = getDX();
break;
}
/* Define Vertical Cursor Range */
case 0x08:
{
DPRINT("Setting mouse vertical range: %u - %u\n", getCX(), getDX());
DriverState.MinY = getCX();
DriverState.MaxY = getDX();
break;
}
/* Define Graphics Cursor */
case 0x09:
{
@ -395,8 +582,7 @@ static VOID WINAPI DosMouseService(LPWORD Stack)
/* Define Mickey/Pixel Ratio */
case 0x0F:
{
DriverState.MickeysPerCellHoriz = getCX();
DriverState.MickeysPerCellVert = getDX();
/* This call should be completely ignored */
break;
}
@ -588,14 +774,14 @@ static VOID WINAPI DosMouseService(LPWORD Stack)
setES(0x0000);
setBX(0x0000);
DriverEnabled = FALSE;
DosMouseDisable();
break;
}
/* Enable Mouse Driver */
case 0x20:
{
DriverEnabled = TRUE;
DosMouseEnable();
break;
}
@ -608,6 +794,38 @@ static VOID WINAPI DosMouseService(LPWORD Stack)
/* PUBLIC FUNCTIONS ***********************************************************/
VOID DosMouseEnable(VOID)
{
if (!DriverEnabled)
{
DriverEnabled = TRUE;
/* Get the old IRQ handler */
OldIrqHandler = ((PDWORD)BaseAddress)[MOUSE_IRQ_INT];
/* Set the IRQ handler */
RegisterDosInt32(MOUSE_IRQ_INT, DosMouseIrq);
/* Enable packet reporting */
IOWriteB(PS2_CONTROL_PORT, 0xD4);
IOWriteB(PS2_DATA_PORT, 0xF4);
/* Read the mouse ACK reply */
PS2PortQueueRead(1);
}
}
VOID DosMouseDisable(VOID)
{
if (DriverEnabled)
{
/* Restore the old IRQ handler */
((PDWORD)BaseAddress)[MOUSE_IRQ_INT] = OldIrqHandler;
DriverEnabled = FALSE;
}
}
VOID DosMouseUpdatePosition(PCOORD NewPosition)
{
SHORT DeltaX = NewPosition->X - DriverState.Position.X;
@ -615,8 +833,8 @@ VOID DosMouseUpdatePosition(PCOORD NewPosition)
if (!DriverEnabled) return;
DriverState.HorizCount += (DeltaX * (SHORT)DriverState.MickeysPerCellHoriz) / 8;
DriverState.VertCount += (DeltaY * (SHORT)DriverState.MickeysPerCellVert ) / 8;
DriverState.HorizCount += (DeltaX * MICKEYS_PER_CELL_HORIZ) / 8;
DriverState.VertCount += (DeltaY * MICKEYS_PER_CELL_VERT) / 8;
if (DriverState.ShowCount > 0) EraseMouseCursor();
DriverState.Position = *NewPosition;
@ -671,10 +889,12 @@ BOOLEAN DosMouseInitialize(VOID)
/* Initialize the interrupt handler */
RegisterDosInt32(DOS_MOUSE_INTERRUPT, DosMouseService);
DosMouseEnable();
return TRUE;
}
VOID DosMouseCleanup(VOID)
{
if (DriverState.ShowCount > 0) EraseMouseCursor();
DosMouseDisable();
}

View file

@ -16,6 +16,9 @@
/* DEFINES ********************************************************************/
#define DOS_MOUSE_INTERRUPT 0x33
#define MOUSE_IRQ_INT 0x74
#define MOUSE_MAX_HORIZ 640
#define MOUSE_MAX_VERT 200
enum
{
@ -47,8 +50,7 @@ typedef struct _MOUSE_DRIVER_STATE
COORD LastRelease[NUM_MOUSE_BUTTONS];
SHORT HorizCount;
SHORT VertCount;
WORD MickeysPerCellHoriz;
WORD MickeysPerCellVert;
WORD MinX, MaxX, MinY, MaxY;
/*
* User Subroutine Handlers called on mouse events
@ -72,9 +74,8 @@ typedef struct _MOUSE_DRIVER_STATE
/* FUNCTIONS ******************************************************************/
VOID DosMouseUpdatePosition(PCOORD NewPosition);
VOID DosMouseUpdateButtons(WORD ButtonStatus);
VOID DosMouseEnable(VOID);
VOID DosMouseDisable(VOID);
BOOLEAN DosMouseInitialize(VOID);
VOID DosMouseCleanup(VOID);

View file

@ -15,6 +15,7 @@
#include "io.h"
#include "pic.h"
#include "clock.h"
/* PRIVATE VARIABLES **********************************************************/
@ -24,14 +25,82 @@ static CMOS_MEMORY CmosMemory;
static BOOLEAN NmiEnabled = TRUE;
static CMOS_REGISTERS SelectedRegister = CMOS_REG_STATUS_D;
/* PUBLIC FUNCTIONS ***********************************************************/
static PHARDWARE_TIMER ClockTimer;
static PHARDWARE_TIMER PeriodicTimer;
BOOLEAN IsNmiEnabled(VOID)
/* PRIVATE FUNCTIONS **********************************************************/
static VOID RtcUpdatePeriodicTimer(VOID)
{
return NmiEnabled;
BYTE RateSelect = CmosMemory.StatusRegA & 0x0F;
if (RateSelect == 0)
{
/* No periodic interrupt */
DisableHardwareTimer(PeriodicTimer);
return;
}
/* 1 and 2 act like 8 and 9 */
if (RateSelect <= 2) RateSelect += 7;
SetHardwareTimerDelay(PeriodicTimer, 1000000000ULL / (ULONGLONG)(1 << (16 - RateSelect)));
EnableHardwareTimer(PeriodicTimer);
}
VOID CmosWriteAddress(BYTE Value)
static VOID FASTCALL RtcPeriodicTick(ULONGLONG ElapsedTime)
{
UNREFERENCED_PARAMETER(ElapsedTime);
/* Set PF */
CmosMemory.StatusRegC |= CMOS_STC_PF;
/* Check if there should be an interrupt on a periodic timer tick */
if (CmosMemory.StatusRegB & CMOS_STB_INT_PERIODIC)
{
CmosMemory.StatusRegC |= CMOS_STC_IRQF;
/* Interrupt! */
PicInterruptRequest(RTC_IRQ_NUMBER);
}
}
/* Should be called every second */
static VOID FASTCALL RtcTimeUpdate(ULONGLONG ElapsedTime)
{
SYSTEMTIME CurrentTime;
UNREFERENCED_PARAMETER(ElapsedTime);
/* Get the current time */
GetLocalTime(&CurrentTime);
/* Set UF */
CmosMemory.StatusRegC |= CMOS_STC_UF;
/* Check if the time matches the alarm time */
if ((CurrentTime.wHour == CmosMemory.AlarmHour ) &&
(CurrentTime.wMinute == CmosMemory.AlarmMinute) &&
(CurrentTime.wSecond == CmosMemory.AlarmSecond))
{
/* Set the alarm flag */
CmosMemory.StatusRegC |= CMOS_STC_AF;
/* Set IRQF if there should be an interrupt */
if (CmosMemory.StatusRegB & CMOS_STB_INT_ON_ALARM) CmosMemory.StatusRegC |= CMOS_STC_IRQF;
}
/* Check if there should be an interrupt on update */
if (CmosMemory.StatusRegB & CMOS_STB_INT_ON_UPDATE) CmosMemory.StatusRegC |= CMOS_STC_IRQF;
if (CmosMemory.StatusRegC & CMOS_STC_IRQF)
{
/* Interrupt! */
PicInterruptRequest(RTC_IRQ_NUMBER);
}
}
static VOID CmosWriteAddress(BYTE Value)
{
/* Update the NMI enabled flag */
NmiEnabled = !(Value & CMOS_DISABLE_NMI);
@ -51,7 +120,7 @@ VOID CmosWriteAddress(BYTE Value)
}
}
BYTE CmosReadData(VOID)
static BYTE CmosReadData(VOID)
{
BYTE Value;
SYSTEMTIME CurrentTime;
@ -181,7 +250,7 @@ BYTE CmosReadData(VOID)
return Value;
}
VOID CmosWriteData(BYTE Value)
static VOID CmosWriteData(BYTE Value)
{
BOOLEAN ChangeTime = FALSE;
SYSTEMTIME CurrentTime;
@ -296,6 +365,7 @@ VOID CmosWriteData(BYTE Value)
case CMOS_REG_STATUS_A:
{
CmosMemory.StatusRegA = Value & 0x7F; // Bit 7 is read-only
RtcUpdatePeriodicTimer();
break;
}
@ -342,13 +412,13 @@ VOID CmosWriteData(BYTE Value)
SelectedRegister = CMOS_REG_STATUS_D;
}
BYTE WINAPI CmosReadPort(USHORT Port)
static BYTE WINAPI CmosReadPort(USHORT Port)
{
ASSERT(Port == CMOS_DATA_PORT);
return CmosReadData();
}
VOID WINAPI CmosWritePort(USHORT Port, BYTE Data)
static VOID WINAPI CmosWritePort(USHORT Port, BYTE Data)
{
if (Port == CMOS_ADDRESS_PORT)
CmosWriteAddress(Data);
@ -356,68 +426,12 @@ VOID WINAPI CmosWritePort(USHORT Port, BYTE Data)
CmosWriteData(Data);
}
DWORD RtcGetTicksPerSecond(VOID)
/* PUBLIC FUNCTIONS ***********************************************************/
BOOLEAN IsNmiEnabled(VOID)
{
BYTE RateSelect = CmosMemory.StatusRegB & 0x0F;
if (RateSelect == 0)
{
/* No periodic interrupt */
return 0;
}
/* 1 and 2 act like 8 and 9 */
if (RateSelect <= 2) RateSelect += 7;
return 1 << (16 - RateSelect);
}
VOID RtcPeriodicTick(VOID)
{
/* Set PF */
CmosMemory.StatusRegC |= CMOS_STC_PF;
/* Check if there should be an interrupt on a periodic timer tick */
if (CmosMemory.StatusRegB & CMOS_STB_INT_PERIODIC)
{
CmosMemory.StatusRegC |= CMOS_STC_IRQF;
/* Interrupt! */
PicInterruptRequest(RTC_IRQ_NUMBER);
}
}
/* Should be called every second */
VOID RtcTimeUpdate(VOID)
{
SYSTEMTIME CurrentTime;
/* Get the current time */
GetLocalTime(&CurrentTime);
/* Set UF */
CmosMemory.StatusRegC |= CMOS_STC_UF;
/* Check if the time matches the alarm time */
if ((CurrentTime.wHour == CmosMemory.AlarmHour ) &&
(CurrentTime.wMinute == CmosMemory.AlarmMinute) &&
(CurrentTime.wSecond == CmosMemory.AlarmSecond))
{
/* Set the alarm flag */
CmosMemory.StatusRegC |= CMOS_STC_AF;
/* Set IRQF if there should be an interrupt */
if (CmosMemory.StatusRegB & CMOS_STB_INT_ON_ALARM) CmosMemory.StatusRegC |= CMOS_STC_IRQF;
}
/* Check if there should be an interrupt on update */
if (CmosMemory.StatusRegB & CMOS_STB_INT_ON_UPDATE) CmosMemory.StatusRegC |= CMOS_STC_IRQF;
if (CmosMemory.StatusRegC & CMOS_STC_IRQF)
{
/* Interrupt! */
PicInterruptRequest(RTC_IRQ_NUMBER);
}
return NmiEnabled;
}
VOID CmosInitialize(VOID)
@ -486,6 +500,11 @@ VOID CmosInitialize(VOID)
/* Register the I/O Ports */
RegisterIoPort(CMOS_ADDRESS_PORT, NULL , CmosWritePort);
RegisterIoPort(CMOS_DATA_PORT , CmosReadPort, CmosWritePort);
ClockTimer = CreateHardwareTimer(HARDWARE_TIMER_ENABLED, 1000, RtcTimeUpdate);
PeriodicTimer = CreateHardwareTimer(HARDWARE_TIMER_ENABLED | HARDWARE_TIMER_PRECISE,
1000000, /* 1,000,000 ns = 1 ms */
RtcPeriodicTick);
}
VOID CmosCleanup(VOID)
@ -494,6 +513,9 @@ VOID CmosCleanup(VOID)
if (hCmosRam == INVALID_HANDLE_VALUE) return;
DestroyHardwareTimer(PeriodicTimer);
DestroyHardwareTimer(ClockTimer);
/* Flush the CMOS memory back to the RAM file and close it */
SetFilePointer(hCmosRam, 0, NULL, FILE_BEGIN);
WriteFile(hCmosRam, &CmosMemory, CmosSize, &CmosSize, NULL);

View file

@ -128,8 +128,6 @@ C_ASSERT(sizeof(CMOS_MEMORY) == 0x40);
BOOLEAN IsNmiEnabled(VOID);
DWORD RtcGetTicksPerSecond(VOID);
VOID RtcPeriodicTick(VOID);
VOID RtcTimeUpdate(VOID);
VOID CmosInitialize(VOID);
VOID CmosCleanup(VOID);

View file

@ -12,31 +12,43 @@
#include "mouse.h"
#include "ps2.h"
// #include "pic.h"
#include "clock.h"
#include "video/vga.h"
/* PRIVATE VARIABLES **********************************************************/
static const BYTE ScrollMagic[3] = { 200, 100, 80 };
static const BYTE ExtraButtonMagic[3] = { 200, 200, 80 };
static HANDLE MouseMutex;
static PHARDWARE_TIMER StreamTimer;
static MOUSE_PACKET LastPacket;
static MOUSE_MODE Mode, PreviousMode;
static COORD Position;
static ULONG WidthMm, HeightMm, WidthPixels, HeightPixels;
static ULONG SampleRate;
static ULONG Resolution;
static BOOLEAN Scaling;
static BYTE Resolution; /* Completely ignored */
static BOOLEAN Scaling; /* Completely ignored */
static BOOLEAN Reporting;
static BYTE MouseId;
static ULONG ButtonState;
static SHORT HorzCounter;
static SHORT VertCounter;
static CHAR ScrollCounter;
static BOOLEAN EventsOccurred = FALSE;
static BYTE DataByteWait = 0;
static BYTE ScrollMagicCounter = 0, ExtraButtonMagicCounter = 0;
static BYTE PS2Port = 1;
/* PUBLIC VARIABLES ***********************************************************/
UINT MouseCycles = 10;
/* PRIVATE FUNCTIONS **********************************************************/
static VOID MouseResetConfig(VOID)
{
/* Reset the configuration to defaults */
SampleRate = 100;
MouseCycles = 10;
Resolution = 4;
Scaling = FALSE;
Reporting = FALSE;
@ -45,7 +57,7 @@ static VOID MouseResetConfig(VOID)
static VOID MouseResetCounters(VOID)
{
/* Reset all flags and counters */
ButtonState = HorzCounter = VertCounter = ScrollCounter = 0;
HorzCounter = VertCounter = ScrollCounter = 0;
}
static VOID MouseReset(VOID)
@ -57,42 +69,50 @@ static VOID MouseReset(VOID)
/* Enter streaming mode and the reset the mouse ID */
Mode = MOUSE_STREAMING_MODE;
MouseId = 0;
ScrollMagicCounter = ExtraButtonMagicCounter = 0;
/* Send the Basic Assurance Test success code and the device ID */
PS2QueuePush(PS2Port, MOUSE_BAT_SUCCESS);
PS2QueuePush(PS2Port, MouseId);
}
#if 0
static VOID MouseGetPacket(PMOUSE_PACKET Packet)
{
/* Clear the packet */
RtlZeroMemory(Packet, sizeof(*Packet));
/* Acquire the mutex */
WaitForSingleObject(MouseMutex, INFINITE);
Packet->Flags |= MOUSE_ALWAYS_SET;
/* Check for horizontal overflows */
if ((HorzCounter < MOUSE_MIN) || (HorzCounter > MOUSE_MAX))
/* Set the sign flags */
if (HorzCounter < 0)
{
if (HorzCounter > MOUSE_MAX) HorzCounter = MOUSE_MAX;
if (HorzCounter < MOUSE_MIN) HorzCounter = MOUSE_MIN;
Packet->Flags |= MOUSE_X_SIGN;
HorzCounter = -HorzCounter;
}
if (VertCounter < 0)
{
Packet->Flags |= MOUSE_Y_SIGN;
VertCounter = -VertCounter;
}
/* Check for horizontal overflows */
if (HorzCounter > MOUSE_MAX)
{
HorzCounter = MOUSE_MAX;
Packet->Flags |= MOUSE_X_OVERFLOW;
}
/* Check for vertical overflows */
if ((VertCounter < MOUSE_MIN) || (VertCounter > MOUSE_MAX))
if (VertCounter > MOUSE_MAX)
{
if (VertCounter > MOUSE_MIN) VertCounter = MOUSE_MIN;
if (VertCounter < MOUSE_MIN) VertCounter = MOUSE_MIN;
VertCounter = MOUSE_MAX;
Packet->Flags |= MOUSE_Y_OVERFLOW;
}
/* Set the sign flags */
if (HorzCounter & MOUSE_SIGN_BIT) Packet->Flags |= MOUSE_X_SIGN;
if (HorzCounter & MOUSE_SIGN_BIT) Packet->Flags |= MOUSE_Y_SIGN;
/* Set the button flags */
if (ButtonState & FROM_LEFT_1ST_BUTTON_PRESSED) Packet->Flags |= MOUSE_LEFT_BUTTON;
if (ButtonState & FROM_LEFT_2ND_BUTTON_PRESSED) Packet->Flags |= MOUSE_MIDDLE_BUTTON;
@ -116,36 +136,94 @@ static VOID MouseGetPacket(PMOUSE_PACKET Packet)
/* Reset the counters */
MouseResetCounters();
}
#endif
/*static*/ VOID MouseUpdatePosition(PCOORD NewPosition)
{
/* Update the counters */
HorzCounter += ((NewPosition->X - Position.X) * WidthMm * Resolution) / WidthPixels;
VertCounter += ((NewPosition->Y - Position.Y) * HeightMm * Resolution) / HeightPixels;
/* Update the position */
Position = *NewPosition;
/* Release the mutex */
ReleaseMutex(MouseMutex);
}
/*static*/ VOID MouseUpdateButtons(ULONG NewButtonState)
static VOID MouseDispatchPacket(PMOUSE_PACKET Packet)
{
ButtonState = NewButtonState;
}
/*static*/ VOID MouseScroll(LONG Direction)
{
ScrollCounter += Direction;
}
/*static*/ COORD MouseGetPosition(VOID)
{
return Position;
PS2QueuePush(PS2Port, Packet->Flags);
PS2QueuePush(PS2Port, Packet->HorzCounter);
PS2QueuePush(PS2Port, Packet->VertCounter);
if (MouseId >= 3) PS2QueuePush(PS2Port, Packet->Extra);
}
static VOID WINAPI MouseCommand(LPVOID Param, BYTE Command)
{
/* Check if we were waiting for a data byte */
if (DataByteWait)
{
PS2QueuePush(PS2Port, MOUSE_ACK);
switch (DataByteWait)
{
/* Set Resolution */
case 0xE8:
{
Resolution = Command;
break;
}
/* Set Sample Rate */
case 0xF3:
{
/* Check for the scroll wheel enabling sequence */
if (MouseId == 0)
{
if (Command == ScrollMagic[ScrollMagicCounter])
{
ScrollMagicCounter++;
if (ScrollMagicCounter == 3) MouseId = 3;
}
else
{
ScrollMagicCounter = 0;
}
}
/* Check for the 5-button enabling sequence */
if (MouseId == 3)
{
if (Command == ExtraButtonMagic[ExtraButtonMagicCounter])
{
ExtraButtonMagicCounter++;
if (ExtraButtonMagicCounter == 3) MouseId = 4;
}
else
{
ExtraButtonMagicCounter = 0;
}
}
MouseCycles = 1000 / (UINT)Command;
break;
}
default:
{
/* Shouldn't happen */
ASSERT(FALSE);
}
}
DataByteWait = 0;
}
/* Check if we're in wrap mode */
if (Mode == MOUSE_WRAP_MODE)
{
/*
* In this mode, we just echo whatever byte we get,
* except for the 0xEC and 0xFF commands.
*/
if (Command != 0xEC && Command != 0xFF)
{
PS2QueuePush(PS2Port, Command);
return;
}
}
switch (Command)
{
/* Set 1:1 Scaling */
@ -166,17 +244,27 @@ static VOID WINAPI MouseCommand(LPVOID Param, BYTE Command)
/* Set Resolution */
case 0xE8:
/* Set Sample Rate */
case 0xF3:
{
// TODO: NOT IMPLEMENTED
UNIMPLEMENTED;
PS2QueuePush(PS2Port, MOUSE_ACK);
DataByteWait = Command;
break;
}
/* Read Status */
case 0xE9:
{
// TODO: NOT IMPLEMENTED
UNIMPLEMENTED;
BYTE Status = ButtonState & 7;
PS2QueuePush(PS2Port, MOUSE_ACK);
if (Scaling) Status |= 1 << 4;
if (Reporting) Status |= 1 << 5;
if (Mode == MOUSE_REMOTE_MODE) Status |= 1 << 6;
PS2QueuePush(PS2Port, Status);
PS2QueuePush(PS2Port, Resolution);
PS2QueuePush(PS2Port, (BYTE)(1000 / MouseCycles));
break;
}
@ -193,8 +281,9 @@ static VOID WINAPI MouseCommand(LPVOID Param, BYTE Command)
/* Read Packet */
case 0xEB:
{
// TODO: NOT IMPLEMENTED
UNIMPLEMENTED;
PS2QueuePush(PS2Port, MOUSE_ACK);
MouseGetPacket(&LastPacket);
MouseDispatchPacket(&LastPacket);
break;
}
@ -247,14 +336,6 @@ static VOID WINAPI MouseCommand(LPVOID Param, BYTE Command)
break;
}
/* Set Sample Rate */
case 0xF3:
{
// TODO: NOT IMPLEMENTED
UNIMPLEMENTED;
break;
}
/* Enable Reporting */
case 0xF4:
{
@ -283,8 +364,8 @@ static VOID WINAPI MouseCommand(LPVOID Param, BYTE Command)
/* Resend */
case 0xFE:
{
// TODO: NOT IMPLEMENTED
UNIMPLEMENTED;
PS2QueuePush(PS2Port, MOUSE_ACK);
MouseDispatchPacket(&LastPacket);
break;
}
@ -303,50 +384,77 @@ static VOID WINAPI MouseCommand(LPVOID Param, BYTE Command)
}
}
static VOID FASTCALL MouseStreamingCallback(ULONGLONG ElapsedTime)
{
UNREFERENCED_PARAMETER(ElapsedTime);
/* Check if we're not in streaming mode, not reporting, or there's nothing to report */
if (Mode != MOUSE_STREAMING_MODE || !Reporting || !EventsOccurred) return;
MouseGetPacket(&LastPacket);
MouseDispatchPacket(&LastPacket);
EventsOccurred = FALSE;
}
/* PUBLIC FUNCTIONS ***********************************************************/
VOID MouseGetDataFast(PCOORD CurrentPosition, PBYTE CurrentButtonState)
{
WaitForSingleObject(MouseMutex, INFINITE);
*CurrentPosition = Position;
*CurrentButtonState = LOBYTE(ButtonState);
ReleaseMutex(MouseMutex);
}
VOID MouseEventHandler(PMOUSE_EVENT_RECORD MouseEvent)
{
extern COORD DosNewPosition;
extern WORD DosButtonState;
COORD NewPosition = MouseEvent->dwMousePosition;
BOOLEAN DoubleWidth = FALSE, DoubleHeight = FALSE;
// FIXME: Sync our private data
MouseUpdatePosition(&MouseEvent->dwMousePosition);
MouseUpdateButtons(MouseEvent->dwButtonState);
if (!VgaGetDoubleVisionState(&DoubleWidth, &DoubleHeight))
{
/* Text mode */
NewPosition.X *= 8;
NewPosition.Y *= 8;
}
// HACK: Bypass PS/2 and instead, notify the MOUSE.COM driver directly
DosNewPosition = MouseEvent->dwMousePosition;
DosButtonState = LOWORD(MouseEvent->dwButtonState);
/* Adjust for double vision */
if (DoubleWidth) NewPosition.X /= 2;
if (DoubleHeight) NewPosition.Y /= 2;
// PS2QueuePush(PS2Port, Data);
WaitForSingleObject(MouseMutex, INFINITE);
/* Update the counters */
HorzCounter += NewPosition.X - Position.X;
VertCounter += NewPosition.Y - Position.Y;
/* Update the position */
Position = NewPosition;
/* Update the button state */
ButtonState = MouseEvent->dwButtonState;
if (MouseEvent->dwEventFlags & MOUSE_WHEELED)
{
ScrollCounter += (SHORT)HIWORD(MouseEvent->dwButtonState);
}
EventsOccurred = TRUE;
ReleaseMutex(MouseMutex);
}
BOOLEAN MouseInit(BYTE PS2Connector)
{
HWND hWnd;
HDC hDC;
/* Get the console window */
hWnd = GetConsoleWindow();
if (hWnd == NULL) return FALSE;
/* Get the console window's device context */
hDC = GetWindowDC(hWnd);
if (hDC == NULL) return FALSE;
/* Get the parameters */
WidthMm = (ULONG)GetDeviceCaps(hDC, HORZSIZE);
HeightMm = (ULONG)GetDeviceCaps(hDC, VERTSIZE);
WidthPixels = (ULONG)GetDeviceCaps(hDC, HORZRES);
HeightPixels = (ULONG)GetDeviceCaps(hDC, VERTRES);
/* Release the device context */
ReleaseDC(hWnd, hDC);
/* Finish to plug the mouse to the specified PS/2 port */
PS2Port = PS2Connector;
PS2SetDeviceCmdProc(PS2Port, NULL, MouseCommand);
MouseMutex = CreateMutex(NULL, FALSE, NULL);
if (MouseMutex == NULL) return FALSE;
StreamTimer = CreateHardwareTimer(HARDWARE_TIMER_ENABLED, 10, MouseStreamingCallback);
MouseReset();
return TRUE;
}

View file

@ -16,9 +16,7 @@
/* DEFINES ********************************************************************/
/* Mouse packet constants */
#define MOUSE_MIN -256
#define MOUSE_MAX 255
#define MOUSE_SIGN_BIT (1 << 8)
/* Mouse packet flags */
#define MOUSE_LEFT_BUTTON (1 << 0)
@ -69,6 +67,7 @@ typedef struct _MOUSE_PACKET
/* FUNCTIONS ******************************************************************/
VOID MouseEventHandler(PMOUSE_EVENT_RECORD MouseEvent);
VOID MouseGetDataFast(PCOORD CurrentPosition, PBYTE CurrentButtonState);
BOOLEAN MouseInit(BYTE PS2Connector);
#endif // _MOUSE_H_

View file

@ -16,10 +16,12 @@
#include "io.h"
#include "pit.h"
#include "pic.h"
#include "clock.h"
/* PRIVATE VARIABLES **********************************************************/
static PIT_CHANNEL PitChannels[PIT_CHANNELS];
static PHARDWARE_TIMER MasterClock;
/* PRIVATE FUNCTIONS **********************************************************/
@ -451,6 +453,17 @@ static VOID PitDecrementCount(PPIT_CHANNEL Channel, DWORD Count)
}
}
static VOID FASTCALL PitClock(ULONGLONG Count)
{
UCHAR i;
for (i = 0; i < PIT_CHANNELS; i++)
{
// if (!PitChannels[i].Counting) continue;
PitDecrementCount(&PitChannels[i], Count);
}
}
/* PUBLIC FUNCTIONS ***********************************************************/
VOID PitSetOutFunction(BYTE Channel, LPVOID Param, PIT_OUT_FUNCTION OutFunction)
@ -480,38 +493,6 @@ WORD PitGetReloadValue(BYTE Channel)
return PitChannels[Channel].ReloadValue;
}
DWORD PitGetResolution(VOID)
{
UCHAR i;
DWORD MinReloadValue = 65536;
for (i = 0; i < PIT_CHANNELS; i++)
{
DWORD ReloadValue = PitChannels[i].ReloadValue;
/* 0 means 65536 */
if (ReloadValue == 0) ReloadValue = 65536;
if (ReloadValue < MinReloadValue) MinReloadValue = ReloadValue;
}
/* Return the frequency resolution */
return PIT_BASE_FREQUENCY / MinReloadValue;
}
VOID PitClock(DWORD Count)
{
UCHAR i;
if (Count == 0) return;
for (i = 0; i < PIT_CHANNELS; i++)
{
// if (!PitChannels[i].Counting) continue;
PitDecrementCount(&PitChannels[i], Count);
}
}
VOID PitInitialize(VOID)
{
/* Set up the timers to their default value */
@ -527,6 +508,11 @@ VOID PitInitialize(VOID)
RegisterIoPort(PIT_DATA_PORT(0), PitReadPort, PitWritePort);
RegisterIoPort(PIT_DATA_PORT(1), PitReadPort, PitWritePort);
RegisterIoPort(PIT_DATA_PORT(2), PitReadPort, PitWritePort);
/* Register the hardware timer */
MasterClock = CreateHardwareTimer(HARDWARE_TIMER_ENABLED | HARDWARE_TIMER_PRECISE,
1000000000ULL / PIT_BASE_FREQUENCY,
PitClock);
}
/* EOF */

View file

@ -79,9 +79,6 @@ VOID PitSetOutFunction(BYTE Channel, LPVOID Param, PIT_OUT_FUNCTION OutFunction)
VOID PitSetGate(BYTE Channel, BOOLEAN State);
WORD PitGetReloadValue(BYTE Channel);
DWORD PitGetResolution(VOID);
VOID PitClock(DWORD Count);
VOID PitInitialize(VOID);
#endif // _PIT_H_

View file

@ -15,6 +15,7 @@
#include "io.h"
#include "ps2.h"
#include "pic.h"
#include "clock.h"
/* PRIVATE VARIABLES **********************************************************/
@ -49,6 +50,8 @@ static BYTE StatusRegister = 0x00;
// static BYTE InputBuffer = 0x00; // PS/2 Input Buffer
static BYTE OutputBuffer = 0x00; // PS/2 Output Buffer
static PHARDWARE_TIMER IrqTimer = NULL;
/* PRIVATE FUNCTIONS **********************************************************/
static VOID PS2SendCommand(PPS2_PORT Port, BYTE Command)
@ -262,7 +265,30 @@ static VOID WINAPI PS2WritePort(USHORT Port, BYTE Data)
}
}
static BOOLEAN PS2PortQueueRead(BYTE PS2Port)
static VOID FASTCALL GeneratePS2Irq(ULONGLONG ElapsedTime)
{
UNREFERENCED_PARAMETER(ElapsedTime);
/* Generate an IRQ 1 if there is data ready in the output queue */
if (PS2PortQueueRead(0))
{
/* Generate an interrupt if interrupts for the first PS/2 port are enabled */
if (ControllerConfig & 0x01) PicInterruptRequest(1);
return;
}
/* Generate an IRQ 12 if there is data ready in the output queue */
if (PS2PortQueueRead(1))
{
/* Generate an interrupt if interrupts for the second PS/2 port are enabled */
if (ControllerConfig & 0x02) PicInterruptRequest(12);
return;
}
}
/* PUBLIC FUNCTIONS ***********************************************************/
BOOLEAN PS2PortQueueRead(BYTE PS2Port)
{
BOOLEAN Result = TRUE;
PPS2_PORT Port;
@ -316,8 +342,6 @@ Done:
return Result;
}
/* PUBLIC FUNCTIONS ***********************************************************/
VOID PS2SetDeviceCmdProc(BYTE PS2Port, LPVOID Param, PS2_DEVICE_CMDPROC DeviceCommand)
{
if (PS2Port >= PS2_PORTS) return;
@ -354,43 +378,14 @@ BOOLEAN PS2QueuePush(BYTE PS2Port, BYTE Data)
/* The queue is not empty anymore */
Port->QueueEmpty = FALSE;
/*
// Get the data
OutputBuffer = Port->Queue[Port->QueueStart];
StatusRegister |= (1 << 0); // There is something to read
// FIXME: Sometimes StatusRegister |= (1 << 5); for the second PS/2 port
if (PS2Port == 0)
PicInterruptRequest(1);
else if (PS2Port == 1)
PicInterruptRequest(12);
*/
/* Schedule the IRQ */
EnableHardwareTimer(IrqTimer);
Done:
ReleaseMutex(Port->QueueMutex);
return Result;
}
VOID GenerateIrq1(VOID)
{
/* Generate an interrupt if interrupts for the first PS/2 port are enabled */
if (ControllerConfig & 0x01)
{
/* Generate an IRQ 1 if there is data ready in the output queue */
if (PS2PortQueueRead(0)) PicInterruptRequest(1);
}
}
VOID GenerateIrq12(VOID)
{
/* Generate an interrupt if interrupts for the second PS/2 port are enabled */
if (ControllerConfig & 0x02)
{
/* Generate an IRQ 12 if there is data ready in the output queue */
if (PS2PortQueueRead(1)) PicInterruptRequest(12);
}
}
BOOLEAN PS2Initialize(VOID)
{
/* Initialize the PS/2 ports */
@ -410,11 +405,15 @@ BOOLEAN PS2Initialize(VOID)
RegisterIoPort(PS2_CONTROL_PORT, PS2ReadPort, PS2WritePort);
RegisterIoPort(PS2_DATA_PORT , PS2ReadPort, PS2WritePort);
IrqTimer = CreateHardwareTimer(HARDWARE_TIMER_ONESHOT, 20, GeneratePS2Irq);
return TRUE;
}
VOID PS2Cleanup(VOID)
{
DestroyHardwareTimer(IrqTimer);
CloseHandle(Ports[1].QueueMutex);
CloseHandle(Ports[0].QueueMutex);
}

View file

@ -26,9 +26,7 @@ typedef VOID (WINAPI *PS2_DEVICE_CMDPROC)(LPVOID Param, BYTE Command);
VOID PS2SetDeviceCmdProc(BYTE PS2Port, LPVOID Param, PS2_DEVICE_CMDPROC DeviceCommand);
BOOLEAN PS2QueuePush(BYTE PS2Port, BYTE Data);
VOID GenerateIrq1(VOID);
VOID GenerateIrq12(VOID);
BOOLEAN PS2PortQueueRead(BYTE PS2Port);
BOOLEAN PS2Initialize(VOID);
VOID PS2Cleanup(VOID);

View file

@ -16,6 +16,7 @@
#include "memory.h"
#include "io.h"
#include "clock.h"
/* PRIVATE VARIABLES **********************************************************/
@ -245,6 +246,9 @@ static HANDLE ConsoleMutex = NULL;
static BOOLEAN DoubleWidth = FALSE;
static BOOLEAN DoubleHeight = FALSE;
static PHARDWARE_TIMER VSyncTimer;
static PHARDWARE_TIMER HSyncTimer;
/*
* VGA Hardware
*/
@ -1754,60 +1758,12 @@ static VOID WINAPI VgaWritePort(USHORT Port, BYTE Data)
}
}
/* PUBLIC FUNCTIONS ***********************************************************/
COORD VgaGetDisplayResolution(VOID)
{
COORD Resolution;
BYTE MaximumScanLine = 1 + (VgaCrtcRegisters[VGA_CRTC_MAX_SCAN_LINE_REG] & 0x1F);
/* The low 8 bits are in the display registers */
Resolution.X = VgaCrtcRegisters[VGA_CRTC_END_HORZ_DISP_REG];
Resolution.Y = VgaCrtcRegisters[VGA_CRTC_VERT_DISP_END_REG];
/* Set the top bits from the overflow register */
if (VgaCrtcRegisters[VGA_CRTC_OVERFLOW_REG] & VGA_CRTC_OVERFLOW_VDE8)
{
Resolution.Y |= 1 << 8;
}
if (VgaCrtcRegisters[VGA_CRTC_OVERFLOW_REG] & VGA_CRTC_OVERFLOW_VDE9)
{
Resolution.Y |= 1 << 9;
}
/* Increase the values by 1 */
Resolution.X++;
Resolution.Y++;
if (VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA)
{
/* Multiply the horizontal resolution by the 9/8 dot mode */
Resolution.X *= (VgaSeqRegisters[VGA_SEQ_CLOCK_REG] & VGA_SEQ_CLOCK_98DM)
? 8 : 9;
/* The horizontal resolution is halved in 8-bit mode */
if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT) Resolution.X /= 2;
}
if (VgaCrtcRegisters[VGA_CRTC_MAX_SCAN_LINE_REG] & VGA_CRTC_MAXSCANLINE_DOUBLE)
{
/* Halve the vertical resolution */
Resolution.Y >>= 1;
}
else
{
/* Divide the vertical resolution by the maximum scan line (== font size in text mode) */
Resolution.Y /= MaximumScanLine;
}
/* Return the resolution */
return Resolution;
}
VOID VgaRefreshDisplay(VOID)
static VOID FASTCALL VgaVerticalRetrace(ULONGLONG ElapsedTime)
{
HANDLE ConsoleBufferHandle = NULL;
UNREFERENCED_PARAMETER(ElapsedTime);
/* Set the vertical retrace flag */
InVerticalRetrace = TRUE;
@ -1876,12 +1832,77 @@ VOID VgaRefreshDisplay(VOID)
NeedsUpdate = FALSE;
}
VOID VgaHorizontalRetrace(VOID)
static VOID FASTCALL VgaHorizontalRetrace(ULONGLONG ElapsedTime)
{
UNREFERENCED_PARAMETER(ElapsedTime);
/* Set the flag */
InHorizontalRetrace = TRUE;
}
/* PUBLIC FUNCTIONS ***********************************************************/
COORD VgaGetDisplayResolution(VOID)
{
COORD Resolution;
BYTE MaximumScanLine = 1 + (VgaCrtcRegisters[VGA_CRTC_MAX_SCAN_LINE_REG] & 0x1F);
/* The low 8 bits are in the display registers */
Resolution.X = VgaCrtcRegisters[VGA_CRTC_END_HORZ_DISP_REG];
Resolution.Y = VgaCrtcRegisters[VGA_CRTC_VERT_DISP_END_REG];
/* Set the top bits from the overflow register */
if (VgaCrtcRegisters[VGA_CRTC_OVERFLOW_REG] & VGA_CRTC_OVERFLOW_VDE8)
{
Resolution.Y |= 1 << 8;
}
if (VgaCrtcRegisters[VGA_CRTC_OVERFLOW_REG] & VGA_CRTC_OVERFLOW_VDE9)
{
Resolution.Y |= 1 << 9;
}
/* Increase the values by 1 */
Resolution.X++;
Resolution.Y++;
if (VgaGcRegisters[VGA_GC_MISC_REG] & VGA_GC_MISC_NOALPHA)
{
/* Multiply the horizontal resolution by the 9/8 dot mode */
Resolution.X *= (VgaSeqRegisters[VGA_SEQ_CLOCK_REG] & VGA_SEQ_CLOCK_98DM)
? 8 : 9;
/* The horizontal resolution is halved in 8-bit mode */
if (VgaAcRegisters[VGA_AC_CONTROL_REG] & VGA_AC_CONTROL_8BIT) Resolution.X /= 2;
}
if (VgaCrtcRegisters[VGA_CRTC_MAX_SCAN_LINE_REG] & VGA_CRTC_MAXSCANLINE_DOUBLE)
{
/* Halve the vertical resolution */
Resolution.Y >>= 1;
}
else
{
/* Divide the vertical resolution by the maximum scan line (== font size in text mode) */
Resolution.Y /= MaximumScanLine;
}
/* Return the resolution */
return Resolution;
}
BOOLEAN VgaGetDoubleVisionState(PBOOLEAN Vertical, PBOOLEAN Horizontal)
{
if (GraphicsConsoleBuffer == NULL) return FALSE;
if (Vertical) *Vertical = DoubleWidth;
if (Horizontal) *Horizontal = DoubleHeight;
return TRUE;
}
VOID VgaRefreshDisplay(VOID)
{
VgaVerticalRetrace(0);
}
VOID NTAPI VgaReadMemory(ULONG Address, PVOID Buffer, ULONG Size)
{
DWORD i;
@ -2106,12 +2127,18 @@ BOOLEAN VgaInitialize(HANDLE TextHandle)
RegisterIoPort(0x3D8, VgaReadPort, VgaWritePort); // CGA_MODE_CTRL_REG
RegisterIoPort(0x3D9, VgaReadPort, VgaWritePort); // CGA_PAL_CTRL_REG
HSyncTimer = CreateHardwareTimer(HARDWARE_TIMER_ENABLED, 0, VgaHorizontalRetrace);
VSyncTimer = CreateHardwareTimer(HARDWARE_TIMER_ENABLED, 16, VgaVerticalRetrace);
/* Return success */
return TRUE;
}
VOID VgaCleanup(VOID)
{
DestroyHardwareTimer(VSyncTimer);
DestroyHardwareTimer(HSyncTimer);
if (ScreenMode == GRAPHICS_MODE)
{
/* Leave the current graphics mode */

View file

@ -260,9 +260,9 @@ VOID VgaDetachFromConsole(BOOL ChangeMode);
COORD VgaGetDisplayResolution(VOID);
VOID VgaRefreshDisplay(VOID);
VOID VgaHorizontalRetrace(VOID);
VOID VgaWriteFont(UINT FontNumber, CONST UCHAR *FontData, UINT Height);
VOID VgaClearMemory(VOID);
BOOLEAN VgaGetDoubleVisionState(PBOOLEAN Vertical, PBOOLEAN Horizontal);
BOOLEAN VgaInitialize(HANDLE TextHandle);
VOID VgaCleanup(VOID);