[NTVDM]
authorAleksandar Andrejevic <aandrejevic@reactos.org>
Fri, 17 Apr 2015 00:20:39 +0000 (00:20 +0000)
committerAleksandar Andrejevic <aandrejevic@reactos.org>
Fri, 17 Apr 2015 00:20:39 +0000 (00:20 +0000)
- 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

17 files changed:
reactos/subsystems/mvdm/ntvdm/bios/bios32/moubios32.c
reactos/subsystems/mvdm/ntvdm/clock.c
reactos/subsystems/mvdm/ntvdm/clock.h
reactos/subsystems/mvdm/ntvdm/cpu/cpu.h
reactos/subsystems/mvdm/ntvdm/dos/dem.c
reactos/subsystems/mvdm/ntvdm/dos/mouse32.c
reactos/subsystems/mvdm/ntvdm/dos/mouse32.h
reactos/subsystems/mvdm/ntvdm/hardware/cmos.c
reactos/subsystems/mvdm/ntvdm/hardware/cmos.h
reactos/subsystems/mvdm/ntvdm/hardware/mouse.c
reactos/subsystems/mvdm/ntvdm/hardware/mouse.h
reactos/subsystems/mvdm/ntvdm/hardware/pit.c
reactos/subsystems/mvdm/ntvdm/hardware/pit.h
reactos/subsystems/mvdm/ntvdm/hardware/ps2.c
reactos/subsystems/mvdm/ntvdm/hardware/ps2.h
reactos/subsystems/mvdm/ntvdm/hardware/video/vga.c
reactos/subsystems/mvdm/ntvdm/hardware/video/vga.h

index e4e119f..f116762 100644 (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"
 // 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;
 }
 
index 305ccc2..7909321 100644 (file)
 #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
 
  */
 // #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 DWORD StartTickCount;
 
-static LARGE_INTEGER LastTimerTick, LastRtcTick, Counter;
-static LONGLONG TimerTicks;
-static DWORD StartTickCount, CurrentTickCount;
-static DWORD LastClockUpdate;
-static DWORD LastVerticalRefresh;
+#ifdef IPS_DISPLAY
+static ULONGLONG Cycles = 0ULL;
+#endif
 
-static DWORD LastIrq1Tick = 0, LastIrq12Tick = 0;
+/* PRIVATE FUNCTIONS **********************************************************/
 
 #ifdef IPS_DISPLAY
-    static DWORD LastCyclePrintout;
-    static ULONGLONG Cycles = 0;
+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 number of ticks */
-    CurrentTickCount = GetTickCount();
+        {
+            /* Get the current performance counter value */
+            /// DWORD_PTR oldmask = SetThreadAffinityMask(GetCurrentThread(), 0);
+            NtQueryPerformanceCounter(&Counter, NULL);
+            /// SetThreadAffinityMask(GetCurrentThread(), oldmask);
+        }
+
+        /* Continue CPU emulation */
+        for (i = 0; VdmRunning && CpuRunning && (i < STEPS_PER_CYCLE); i++)
+        {
+            CpuStep();
 
-#ifdef WORKING_TIMER
-    if ((PitResolution <= 1000) && (RtcFrequency <= 1000))
-    {
-        /* Calculate the approximate performance counter value instead */
-        Counter.QuadPart = StartPerfCount.QuadPart
-                           + (CurrentTickCount - StartTickCount)
-                           * (Frequency.QuadPart / 1000);
-    }
-    else
+#ifdef IPS_DISPLAY
+            ++Cycles;
 #endif
-    {
-        /* Get the current performance counter value */
-        /// DWORD_PTR oldmask = SetThreadAffinityMask(GetCurrentThread(), 0);
-        NtQueryPerformanceCounter(&Counter, NULL);
-        /// SetThreadAffinityMask(GetCurrentThread(), oldmask);
+        }
+
+        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;
+        }
     }
+}
 
-    /* Get the number of PIT ticks that have passed */
-    TimerTicks = ((Counter.QuadPart - LastTimerTick.QuadPart)
-                 * PIT_BASE_FREQUENCY) / Frequency.QuadPart;
+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;
+}
 
-    /* Update the PIT */
-    if (TimerTicks > 0)
-    {
-        PitClock(TimerTicks);
-        LastTimerTick = Counter;
-    }
+VOID EnableHardwareTimer(PHARDWARE_TIMER Timer)
+{
+    /* Increment the count */
+    Timer->EnableCount++;
 
-    /* Check for RTC update */
-    if ((CurrentTickCount - LastClockUpdate) >= 1000)
+    /* Check if the count is above 0 but the timer isn't enabled */
+    if ((Timer->EnableCount > 0) && !(Timer->Flags & HARDWARE_TIMER_ENABLED))
     {
-        RtcTimeUpdate();
-        LastClockUpdate = CurrentTickCount;
+        if (Timer->Flags & HARDWARE_TIMER_PRECISE)
+        {
+            NtQueryPerformanceCounter(&Timer->LastTick, NULL);
+        }
+        else
+        {
+            Timer->LastTick.LowPart = GetTickCount();
+        }
+
+        Timer->Flags |= HARDWARE_TIMER_ENABLED;
+        InsertTailList(&Timers, &Timer->Link);
     }
+}
 
-    /* Check for RTC periodic tick */
-    if ((Counter.QuadPart - LastRtcTick.QuadPart)
-        >= (Frequency.QuadPart / (LONGLONG)RtcFrequency))
-    {
-        RtcPeriodicTick();
-        LastRtcTick = Counter;
-    }
+VOID DisableHardwareTimer(PHARDWARE_TIMER Timer)
+{
+    /* Decrement the count */
+    Timer->EnableCount--;
 
-    /* Check for vertical retrace */
-    if ((CurrentTickCount - LastVerticalRefresh) >= 15)
+    /* Check if the count is 0 or less but the timer is enabled */
+    if ((Timer->EnableCount <= 0) && (Timer->Flags & HARDWARE_TIMER_ENABLED))
     {
-        VgaRefreshDisplay();
-        LastVerticalRefresh = CurrentTickCount;
+        /* Disable the timer */
+        Timer->Flags &= ~HARDWARE_TIMER_ENABLED;
+        RemoveEntryList(&Timer->Link);
     }
+}
 
-    if ((CurrentTickCount - LastIrq1Tick) >= IRQ1_CYCLES)
+VOID SetHardwareTimerDelay(PHARDWARE_TIMER Timer, ULONGLONG NewDelay)
+{
+    if (Timer->Flags & HARDWARE_TIMER_PRECISE)
     {
-        GenerateIrq1();
-        LastIrq1Tick = CurrentTickCount;
+        /* Convert the delay from nanoseconds to performance counter ticks */
+        Timer->Delay = (NewDelay * Frequency.QuadPart + 500000000ULL) / 1000000000ULL;
     }
-
-    if ((CurrentTickCount - LastIrq12Tick) >= IRQ12_CYCLES)
+    else
     {
-        GenerateIrq12();
-        LastIrq12Tick = CurrentTickCount;
+        Timer->Delay = NewDelay;
     }
+}
 
-    /* Horizontal retrace occurs as fast as possible */
-    VgaHorizontalRetrace();
+VOID DestroyHardwareTimer(PHARDWARE_TIMER Timer)
+{
+    if (Timer->Flags & HARDWARE_TIMER_ENABLED) RemoveEntryList(&Timer->Link);
+    RtlFreeHeap(RtlGetProcessHeap(), 0, Timer);
+}
 
-    /* Continue CPU emulation */
-    for (i = 0; VdmRunning && CpuRunning && (i < STEPS_PER_CYCLE); i++)
-    {
-        CpuStep();
+BOOLEAN ClockInitialize(VOID)
+{
 #ifdef IPS_DISPLAY
-        ++Cycles;
+    PHARDWARE_TIMER IpsTimer;
 #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
-
-    }
-}
+    InitializeListHead(&Timers);
 
-BOOLEAN ClockInitialize(VOID)
-{
     /* 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;
 }
index 53927d2..957a095 100644 (file)
 #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);
 
index d60d30d..b0e7fe2 100644 (file)
@@ -54,6 +54,8 @@ enum
     EMULATOR_EXCEPTION_PAGE_FAULT
 };
 #endif
+
+extern BOOLEAN CpuRunning;
 extern FAST486_STATE EmulatorContext;
 
 /* FUNCTIONS ******************************************************************/
index e65c2ef..9deee83 100644 (file)
@@ -423,7 +423,7 @@ static VOID WINAPI DosStart(LPWORD Stack)
     RegisterBop(BOP_START_DOS, NULL);
 
     /* Load the mouse driver */
-    // DosMouseInitialize();
+    DosMouseInitialize();
 
 #ifndef STANDALONE
 
index 212e5ee..0cbaec7 100644 (file)
 #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();
 }
index 899dfc8..aadfbee 100644 (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);
 
index 7ae7cf3..25632e1 100644 (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);
+}
+
+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);
+    }
 }
 
-VOID CmosWriteAddress(BYTE Value)
+/* 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)
-{
-    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);
-    }
-}
+/* PUBLIC FUNCTIONS ***********************************************************/
 
-/* Should be called every second */
-VOID RtcTimeUpdate(VOID)
+BOOLEAN IsNmiEnabled(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);
index f84d27c..b7369c6 100644 (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);
index faa176a..1cdae0a 100644 (file)
 
 #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;
+    PS2QueuePush(PS2Port, Packet->Flags);
+    PS2QueuePush(PS2Port, Packet->HorzCounter);
+    PS2QueuePush(PS2Port, Packet->VertCounter);
+    if (MouseId >= 3) PS2QueuePush(PS2Port, Packet->Extra);
 }
 
-/*static*/ VOID MouseScroll(LONG Direction)
+static VOID WINAPI MouseCommand(LPVOID Param, BYTE Command)
 {
-    ScrollCounter += Direction;
-}
+    /* Check if we were waiting for a data byte */
+    if (DataByteWait)
+    {
+        PS2QueuePush(PS2Port, MOUSE_ACK);
 
-/*static*/ COORD MouseGetPosition(VOID)
-{
-    return Position;
-}
+        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;
+        }
+    }
 
-static VOID WINAPI MouseCommand(LPVOID Param, BYTE Command)
-{
     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);
 
-BOOLEAN MouseInit(BYTE PS2Connector)
-{
-    HWND hWnd;
-    HDC hDC;
+    /* Update the counters */
+    HorzCounter += NewPosition.X - Position.X;
+    VertCounter += NewPosition.Y - Position.Y;
 
-    /* Get the console window */
-    hWnd = GetConsoleWindow();
-    if (hWnd == NULL) return FALSE;
+    /* Update the position */
+    Position = NewPosition;
 
-    /* Get the console window's device context */
-    hDC = GetWindowDC(hWnd);
-    if (hDC == NULL) return FALSE;
+    /* Update the button state */
+    ButtonState = MouseEvent->dwButtonState;
 
-    /* Get the parameters */
-    WidthMm      = (ULONG)GetDeviceCaps(hDC, HORZSIZE);
-    HeightMm     = (ULONG)GetDeviceCaps(hDC, VERTSIZE);
-    WidthPixels  = (ULONG)GetDeviceCaps(hDC, HORZRES);
-    HeightPixels = (ULONG)GetDeviceCaps(hDC, VERTRES);
+    if (MouseEvent->dwEventFlags & MOUSE_WHEELED)
+    {
+        ScrollCounter += (SHORT)HIWORD(MouseEvent->dwButtonState);
+    }
 
-    /* Release the device context */
-    ReleaseDC(hWnd, hDC);
+    EventsOccurred = TRUE;
+    ReleaseMutex(MouseMutex);
+}
 
+BOOLEAN MouseInit(BYTE PS2Connector)
+{
     /* 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;
 }
index 66fc2d0..b91146e 100644 (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_
index 037e062..07d6a75 100644 (file)
 #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 */
index 8d32938..b06fe24 100644 (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_
index b7e6c13..055e21c 100644 (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);
 }
index 95fb02a..d62387c 100644 (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);
index ea104e9..4cf83a8 100644 (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 */
index 298caf0..51e2d57 100644 (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);