Moved a number of DOS definitions out of the global headers and into
[wine] / dlls / winedos / timer.c
1 /*
2  * 8253/8254 Programmable Interval Timer (PIT) emulation
3  *
4  * Copyright 2003 Jukka Heinonen
5  *
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.
10  *
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.
15  *
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
19  */
20
21 #include "config.h"
22
23 #include "dosexe.h"
24 #include "wine/debug.h"
25 #include "wingdi.h"
26 #include "winuser.h"
27
28 WINE_DEFAULT_DEBUG_CHANNEL(int);
29
30 /*
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: Make sure that there are only limited number
39  *        of pending timer IRQ events queued. This makes sure that
40  *        timer handling does not eat all available memory even
41  *        if IRQ handling stops for some reason (suspended process?). 
42  *        This is easy to do by using DOSRELAY parameter.
43  * FIXME: Use timeSetEvent, NtSetEvent or timer thread for more precise
44  *        timing.
45  * FIXME: Move Win16 timer emulation code here.
46  */
47
48 /* The PC clocks ticks at 1193180 Hz. */
49 #define TIMER_FREQ 1193180
50
51 /* Unique system timer identifier. */
52 static UINT_PTR TIMER_id = 0;
53
54 /* Time when timer IRQ was last queued. */
55 static DWORD TIMER_stamp = 0;
56
57 /* Timer ticks between timer IRQs. */
58 static UINT TIMER_ticks = 0;
59
60
61 /***********************************************************************
62  *              TIMER_TimerProc
63  */
64 static void CALLBACK TIMER_TimerProc( HWND     hwnd,
65                                       UINT     uMsg,
66                                       UINT_PTR idEvent,
67                                       DWORD    dwTime )
68 {
69     TIMER_stamp = dwTime;
70     DOSVM_QueueEvent( 0, DOS_PRIORITY_REALTIME, NULL, NULL );
71 }
72
73
74 /***********************************************************************
75  *              TIMER_DoSetTimer
76  */
77 static void WINAPI TIMER_DoSetTimer( ULONG_PTR arg )
78 {
79     INT millis = MulDiv( arg, 1000, TIMER_FREQ );
80
81     /* sanity check - too fast timer */
82     if (millis < 1)
83         millis = 1;
84
85     TRACE_(int)( "setting timer tick delay to %d ms\n", millis );
86
87     if (TIMER_id)
88         KillTimer( NULL, TIMER_id );
89
90     TIMER_id = SetTimer( NULL, 0, millis, TIMER_TimerProc );
91     TIMER_stamp = GetTickCount();
92     TIMER_ticks = arg;
93 }
94
95
96 /***********************************************************************
97  *              DOSVM_GetTimer
98  */
99 UINT WINAPI DOSVM_GetTimer( void )
100 {
101     if (!DOSVM_IsWin16())
102     {
103         DWORD millis = GetTickCount() - TIMER_stamp;
104         INT   ticks = MulDiv( millis, TIMER_FREQ, 1000 );
105
106         /* sanity check - tick wrap or suspended process or update race */
107         if (ticks < 0 || ticks >= TIMER_ticks)
108             ticks = 0;
109
110         return ticks;
111     }
112
113     return 0;
114 }
115
116
117 /***********************************************************************
118  *              DOSVM_SetTimer
119  */
120 void WINAPI DOSVM_SetTimer( UINT ticks )
121 {
122     if (!DOSVM_IsWin16())
123         MZ_RunInThread( TIMER_DoSetTimer, ticks );
124 }
125
126
127 /***********************************************************************
128  *              DOSVM_Int08Handler
129  *
130  * DOS interrupt 08h handler (IRQ0 - TIMER).
131  */
132 void WINAPI DOSVM_Int08Handler( CONTEXT86 *context )
133 {
134     BIOSDATA *bios_data      = DOSVM_BiosData();
135     CONTEXT86 nested_context = *context;
136     FARPROC16 int1c_proc     = DOSVM_GetRMHandler( 0x1c );
137     
138     nested_context.SegCs = SELECTOROF(int1c_proc);
139     nested_context.Eip   = OFFSETOF(int1c_proc);
140
141     /*
142      * Update BIOS ticks since midnight.
143      *
144      * FIXME: What to do when number of ticks exceeds ticks per day?
145      */
146     bios_data->Ticks++;
147
148     /*
149      * If IRQ is called from protected mode, convert
150      * context into VM86 context. Stack is invalidated so
151      * that DPMI_CallRMProc allocates a new stack.
152      */
153     if (!ISV86(&nested_context))
154     {
155         nested_context.EFlags |= V86_FLAG;
156         nested_context.SegSs = 0;
157     }
158
159     /*
160      * Call interrupt 0x1c.
161      */
162     DPMI_CallRMProc( &nested_context, NULL, 0, TRUE );
163
164     DOSVM_AcknowledgeIRQ( context );
165 }