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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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;
61 /* Number of milliseconds between IRQs. */
62 static DWORD TIMER_millis
= 0;
64 /***********************************************************************
67 * Decrement the number of pending IRQs after IRQ handler has been
68 * called. This function will be called even if application has its
69 * own IRQ handler that does not jump to builtin IRQ handler.
71 static void TIMER_Relay( CONTEXT86
*context
, void *data
)
73 InterlockedDecrement( &TIMER_pending
);
77 /***********************************************************************
80 static void CALLBACK
TIMER_TimerProc( HWND hwnd
,
85 LONG pending
= InterlockedIncrement( &TIMER_pending
);
86 DWORD delta
= (dwTime
>= TIMER_stamp
) ?
87 (dwTime
- TIMER_stamp
) : (0xffffffff - (TIMER_stamp
- dwTime
));
89 if (pending
>= TIMER_MAX_PENDING
)
94 ERR( "DOS timer has been stuck for 60 seconds...\n" );
98 InterlockedDecrement( &TIMER_pending
);
104 /* Calculate the number of valid timer interrupts we can generate */
105 DWORD count
= delta
/ TIMER_millis
;
107 /* Forward the timestamp with the time used */
108 TIMER_stamp
+= (count
* TIMER_millis
);
110 /* Generate interrupts */
113 DOSVM_QueueEvent( 0, DOS_PRIORITY_REALTIME
, TIMER_Relay
, NULL
);
119 /***********************************************************************
122 static void WINAPI
TIMER_DoSetTimer( ULONG_PTR arg
)
124 INT millis
= MulDiv( arg
, 1000, TIMER_FREQ
);
126 /* sanity check - too fast timer */
130 TRACE_(int)( "setting timer tick delay to %d ms\n", millis
);
133 KillTimer( NULL
, TIMER_id
);
135 TIMER_id
= SetTimer( NULL
, 0, millis
, TIMER_TimerProc
);
136 TIMER_stamp
= GetTickCount();
139 /* Remember number of milliseconds to wait */
140 TIMER_millis
= millis
;
144 /***********************************************************************
147 UINT WINAPI
DOSVM_GetTimer( void )
149 if (!DOSVM_IsWin16())
151 DWORD millis
= GetTickCount() - TIMER_stamp
;
152 INT ticks
= MulDiv( millis
, TIMER_FREQ
, 1000 );
154 /* sanity check - tick wrap or suspended process or update race */
155 if (ticks
< 0 || ticks
>= TIMER_ticks
)
165 /***********************************************************************
168 void WINAPI
DOSVM_SetTimer( UINT ticks
)
170 /* PIT interprets zero as a maximum length delay. */
174 if (!DOSVM_IsWin16())
175 MZ_RunInThread( TIMER_DoSetTimer
, ticks
);
179 /***********************************************************************
182 * DOS interrupt 08h handler (IRQ0 - TIMER).
184 void WINAPI
DOSVM_Int08Handler( CONTEXT86
*context
)
186 BIOSDATA
*bios_data
= DOSVM_BiosData();
187 CONTEXT86 nested_context
= *context
;
188 FARPROC16 int1c_proc
= DOSVM_GetRMHandler( 0x1c );
190 nested_context
.SegCs
= SELECTOROF(int1c_proc
);
191 nested_context
.Eip
= OFFSETOF(int1c_proc
);
194 * Update BIOS ticks since midnight.
196 * FIXME: What to do when number of ticks exceeds ticks per day?
201 * If IRQ is called from protected mode, convert
202 * context into VM86 context. Stack is invalidated so
203 * that DPMI_CallRMProc allocates a new stack.
205 if (!ISV86(&nested_context
))
207 nested_context
.EFlags
|= V86_FLAG
;
208 nested_context
.SegSs
= 0;
212 * Call interrupt 0x1c.
214 DPMI_CallRMProc( &nested_context
, NULL
, 0, TRUE
);
216 DOSVM_AcknowledgeIRQ( context
);