1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
/*
* 8253/8254 Programmable Interval Timer (PIT) emulation
*
* Copyright 2003 Jukka Heinonen
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "config.h"
#include "dosexe.h"
#include "wine/debug.h"
#include "wingdi.h"
#include "winuser.h"
WINE_DEFAULT_DEBUG_CHANNEL(int);
/*
* FIXME: Use QueryPerformanceCounter for
* more precise GetTimer implementation.
* FIXME: Use QueryPerformanceCounter (or GetTimer implementation)
* in timer tick routine to compensate for lost ticks.
* This should also make it possible to
* emulate really fast timers.
* FIXME: Support special timer modes in addition to periodic mode.
* FIXME: Use timeSetEvent, NtSetEvent or timer thread for more precise
* timing.
* FIXME: Move Win16 timer emulation code here.
*/
/* The PC clocks ticks at 1193180 Hz. */
#define TIMER_FREQ 1193180
/* How many timer IRQs can be pending at any time. */
#define TIMER_MAX_PENDING 20
/* Unique system timer identifier. */
static UINT_PTR TIMER_id = 0;
/* Time when timer IRQ was last queued. */
static DWORD TIMER_stamp = 0;
/* Timer ticks between timer IRQs. */
static UINT TIMER_ticks = 0;
/* Number of pending timer IRQs. */
static LONG TIMER_pending = 0;
/***********************************************************************
* TIMER_Relay
*
* Decrement the number of pending IRQs after IRQ handler has been
* called. This function will be called even if application has its
* own IRQ handler that does not jump to builtin IRQ handler.
*/
static void TIMER_Relay( CONTEXT86 *context, void *data )
{
InterlockedDecrement( &TIMER_pending );
}
/***********************************************************************
* TIMER_TimerProc
*/
static void CALLBACK TIMER_TimerProc( HWND hwnd,
UINT uMsg,
UINT_PTR idEvent,
DWORD dwTime )
{
LONG pending = InterlockedIncrement( &TIMER_pending );
if (pending >= TIMER_MAX_PENDING)
{
DWORD delta = (dwTime >= TIMER_stamp) ?
(dwTime - TIMER_stamp) : (0xffffffff - (TIMER_stamp - dwTime));
if (delta >= 60000)
{
ERR( "DOS timer has been stuck for 60 seconds...\n" );
TIMER_stamp = dwTime;
}
InterlockedDecrement( &TIMER_pending );
}
else
{
TIMER_stamp = dwTime;
DOSVM_QueueEvent( 0, DOS_PRIORITY_REALTIME, TIMER_Relay, NULL );
}
}
/***********************************************************************
* TIMER_DoSetTimer
*/
static void WINAPI TIMER_DoSetTimer( ULONG_PTR arg )
{
INT millis = MulDiv( arg, 1000, TIMER_FREQ );
/* sanity check - too fast timer */
if (millis < 1)
millis = 1;
TRACE_(int)( "setting timer tick delay to %d ms\n", millis );
if (TIMER_id)
KillTimer( NULL, TIMER_id );
TIMER_id = SetTimer( NULL, 0, millis, TIMER_TimerProc );
TIMER_stamp = GetTickCount();
TIMER_ticks = arg;
}
/***********************************************************************
* DOSVM_GetTimer
*/
UINT WINAPI DOSVM_GetTimer( void )
{
if (!DOSVM_IsWin16())
{
DWORD millis = GetTickCount() - TIMER_stamp;
INT ticks = MulDiv( millis, TIMER_FREQ, 1000 );
/* sanity check - tick wrap or suspended process or update race */
if (ticks < 0 || ticks >= TIMER_ticks)
ticks = 0;
return ticks;
}
return 0;
}
/***********************************************************************
* DOSVM_SetTimer
*/
void WINAPI DOSVM_SetTimer( UINT ticks )
{
/* PIT interprets zero as a maximum length delay. */
if (ticks == 0)
ticks = 0x10000;
if (!DOSVM_IsWin16())
MZ_RunInThread( TIMER_DoSetTimer, ticks );
}
/***********************************************************************
* DOSVM_Int08Handler
*
* DOS interrupt 08h handler (IRQ0 - TIMER).
*/
void WINAPI DOSVM_Int08Handler( CONTEXT86 *context )
{
BIOSDATA *bios_data = DOSVM_BiosData();
CONTEXT86 nested_context = *context;
FARPROC16 int1c_proc = DOSVM_GetRMHandler( 0x1c );
nested_context.SegCs = SELECTOROF(int1c_proc);
nested_context.Eip = OFFSETOF(int1c_proc);
/*
* Update BIOS ticks since midnight.
*
* FIXME: What to do when number of ticks exceeds ticks per day?
*/
bios_data->Ticks++;
/*
* If IRQ is called from protected mode, convert
* context into VM86 context. Stack is invalidated so
* that DPMI_CallRMProc allocates a new stack.
*/
if (!ISV86(&nested_context))
{
nested_context.EFlags |= V86_FLAG;
nested_context.SegSs = 0;
}
/*
* Call interrupt 0x1c.
*/
DPMI_CallRMProc( &nested_context, NULL, 0, TRUE );
DOSVM_AcknowledgeIRQ( context );
}