2 * 8253/8254 Programmable Interval Timer (PIT) emulation
4 * Copyright 2003 Jukka Heinonen
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #include "wine/debug.h"
28 WINE_DEFAULT_DEBUG_CHANNEL(int);
31 * FIXME: Use QueryPerformanceCounter for
32 * more precise GetTimer implementation.
33 * FIXME: Use QueryPerformanceCounter (or GetTimer implementation)
34 * in timer tick routine to compensate for lost ticks.
35 * This should also make it possible to
36 * emulate really fast timers.
37 * FIXME: Support special timer modes in addition to periodic mode.
38 * FIXME: Use timeSetEvent, NtSetEvent or timer thread for more precise
40 * FIXME: Move Win16 timer emulation code here.
43 /* The PC clocks ticks at 1193180 Hz. */
44 #define TIMER_FREQ 1193180
46 /* How many timer IRQs can be pending at any time. */
47 #define TIMER_MAX_PENDING 20
49 /* Unique system timer identifier. */
50 static UINT_PTR TIMER_id = 0;
52 /* Time when timer IRQ was last queued. */
53 static DWORD TIMER_stamp = 0;
55 /* Timer ticks between timer IRQs. */
56 static UINT TIMER_ticks = 0;
58 /* Number of pending timer IRQs. */
59 static LONG TIMER_pending = 0;
62 /***********************************************************************
65 * Decrement the number of pending IRQs after IRQ handler has been
66 * called. This function will be called even if application has its
67 * own IRQ handler that does not jump to builtin IRQ handler.
69 static void TIMER_Relay( CONTEXT86 *context, void *data )
71 InterlockedDecrement( &TIMER_pending );
75 /***********************************************************************
78 static void CALLBACK TIMER_TimerProc( HWND hwnd,
83 LONG pending = InterlockedIncrement( &TIMER_pending );
85 if (pending >= TIMER_MAX_PENDING)
87 DWORD delta = (dwTime >= TIMER_stamp) ?
88 (dwTime - TIMER_stamp) : (0xffffffff - (TIMER_stamp - dwTime));
92 ERR( "DOS timer has been stuck for 60 seconds...\n" );
96 InterlockedDecrement( &TIMER_pending );
100 TIMER_stamp = dwTime;
101 DOSVM_QueueEvent( 0, DOS_PRIORITY_REALTIME, TIMER_Relay, NULL );
106 /***********************************************************************
109 static void WINAPI TIMER_DoSetTimer( ULONG_PTR arg )
111 INT millis = MulDiv( arg, 1000, TIMER_FREQ );
113 /* sanity check - too fast timer */
117 TRACE_(int)( "setting timer tick delay to %d ms\n", millis );
120 KillTimer( NULL, TIMER_id );
122 TIMER_id = SetTimer( NULL, 0, millis, TIMER_TimerProc );
123 TIMER_stamp = GetTickCount();
128 /***********************************************************************
131 UINT WINAPI DOSVM_GetTimer( void )
133 if (!DOSVM_IsWin16())
135 DWORD millis = GetTickCount() - TIMER_stamp;
136 INT ticks = MulDiv( millis, TIMER_FREQ, 1000 );
138 /* sanity check - tick wrap or suspended process or update race */
139 if (ticks < 0 || ticks >= TIMER_ticks)
149 /***********************************************************************
152 void WINAPI DOSVM_SetTimer( UINT ticks )
154 /* PIT interprets zero as a maximum length delay. */
158 if (!DOSVM_IsWin16())
159 MZ_RunInThread( TIMER_DoSetTimer, ticks );
163 /***********************************************************************
166 * DOS interrupt 08h handler (IRQ0 - TIMER).
168 void WINAPI DOSVM_Int08Handler( CONTEXT86 *context )
170 BIOSDATA *bios_data = DOSVM_BiosData();
171 CONTEXT86 nested_context = *context;
172 FARPROC16 int1c_proc = DOSVM_GetRMHandler( 0x1c );
174 nested_context.SegCs = SELECTOROF(int1c_proc);
175 nested_context.Eip = OFFSETOF(int1c_proc);
178 * Update BIOS ticks since midnight.
180 * FIXME: What to do when number of ticks exceeds ticks per day?
185 * If IRQ is called from protected mode, convert
186 * context into VM86 context. Stack is invalidated so
187 * that DPMI_CallRMProc allocates a new stack.
189 if (!ISV86(&nested_context))
191 nested_context.EFlags |= V86_FLAG;
192 nested_context.SegSs = 0;
196 * Call interrupt 0x1c.
198 DPMI_CallRMProc( &nested_context, NULL, 0, TRUE );
200 DOSVM_AcknowledgeIRQ( context );