ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Windows/timer_windows.cpp
Revision: 1.6
Committed: 2006-05-01T06:12:50Z (18 years, 6 months ago) by gbeauche
Branch: MAIN
Changes since 1.5: +57 -0 lines
Log Message:
Implement better Windows suspend/resume routines so that we don't oversleep.
i.e. really wake up the thread on next TriggerInterrupt().

File Contents

# Content
1 /*
2 * timer_windows.cpp - Time Manager emulation, Windows specific stuff
3 *
4 * Basilisk II (C) 1997-2005 Christian Bauer
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program 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
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 #include "sysdeps.h"
22
23 #define WIN32_LEAN_AND_MEAN
24 #include <windows.h>
25
26 #include "main.h"
27 #include "macos_util.h"
28 #include "timer.h"
29
30 #define DEBUG 0
31 #include "debug.h"
32
33
34 // Helper time functions
35 #define MSECS2TICKS(MSECS) (((uint64)(MSECS) * frequency) / 1000)
36 #define USECS2TICKS(USECS) (((uint64)(USECS) * frequency) / 1000000)
37 #define TICKS2USECS(TICKS) (((uint64)(TICKS) * 1000000) / frequency)
38
39 // From main_windows.cpp
40 extern HANDLE emul_thread;
41
42 // Global variables
43 static uint32 frequency; // CPU frequency in Hz (< 4 GHz)
44 static tm_time_t mac_boot_ticks;
45 static tm_time_t mac_1904_ticks;
46 static tm_time_t mac_now_diff;
47
48
49 /*
50 * Initialize native Windows timers
51 */
52
53 void timer_init(void)
54 {
55 D(bug("SysTimerInit\n"));
56
57 LARGE_INTEGER tt;
58 if (!QueryPerformanceFrequency(&tt)) {
59 ErrorAlert("No high resolution timers available\n");
60 QuitEmulator();
61 }
62 frequency = tt.LowPart;
63 D(bug(" frequency %d\n", frequency));
64
65 // mac_boot_ticks is 1.18 us since Basilisk II was started
66 QueryPerformanceCounter(&tt);
67 mac_boot_ticks = tt.QuadPart;
68
69 // mac_1904_ticks is 1.18 us since Mac time started 1904
70 mac_1904_ticks = time(NULL) * frequency;
71 mac_now_diff = mac_1904_ticks - mac_boot_ticks;
72 }
73
74
75 /*
76 * Return microseconds since boot (64 bit)
77 */
78
79 void Microseconds(uint32 &hi, uint32 &lo)
80 {
81 D(bug("Microseconds\n"));
82 LARGE_INTEGER tt;
83 QueryPerformanceCounter(&tt);
84 tt.QuadPart = TICKS2USECS(tt.QuadPart - mac_boot_ticks);
85 hi = tt.HighPart;
86 lo = tt.LowPart;
87 }
88
89
90 /*
91 * Return local date/time in Mac format (seconds since 1.1.1904)
92 */
93
94 uint32 TimerDateTime(void)
95 {
96 return TimeToMacTime(time(NULL));
97 }
98
99
100 /*
101 * Get current time
102 */
103
104 void timer_current_time(tm_time_t &t)
105 {
106 LARGE_INTEGER tt;
107 QueryPerformanceCounter(&tt);
108 t = tt.QuadPart + mac_now_diff;
109 }
110
111
112 /*
113 * Add times
114 */
115
116 void timer_add_time(tm_time_t &res, tm_time_t a, tm_time_t b)
117 {
118 res = a + b;
119 }
120
121
122 /*
123 * Subtract times
124 */
125
126 void timer_sub_time(tm_time_t &res, tm_time_t a, tm_time_t b)
127 {
128 res = a - b;
129 }
130
131
132 /*
133 * Compare times (<0: a < b, =0: a = b, >0: a > b)
134 */
135
136 int timer_cmp_time(tm_time_t a, tm_time_t b)
137 {
138 tm_time_t r = a - b;
139 return r < 0 ? -1 : (r > 0 ? 1 : 0);
140 }
141
142
143 /*
144 * Convert Mac time value (>0: microseconds, <0: microseconds) to tm_time_t
145 */
146
147 void timer_mac2host_time(tm_time_t &res, int32 mactime)
148 {
149 if (mactime > 0) {
150 // Time in milliseconds
151 res = MSECS2TICKS(mactime);
152 } else {
153 // Time in negative microseconds
154 res = USECS2TICKS(-mactime);
155 }
156 }
157
158
159 /*
160 * Convert positive tm_time_t to Mac time value (>0: microseconds, <0: microseconds)
161 * A negative input value for hosttime results in a zero return value
162 * As long as the microseconds value fits in 32 bit, it must not be converted to milliseconds!
163 */
164
165 int32 timer_host2mac_time(tm_time_t hosttime)
166 {
167 if (hosttime < 0)
168 return 0;
169 else {
170 uint64 t = TICKS2USECS(hosttime);
171 if (t > 0x7fffffff)
172 return t / 1000; // Time in milliseconds
173 else
174 return -t; // Time in negative microseconds
175 }
176 }
177
178
179 /*
180 * Get current value of microsecond timer
181 */
182
183 uint64 GetTicks_usec(void)
184 {
185 LARGE_INTEGER tt;
186 QueryPerformanceCounter(&tt);
187 return TICKS2USECS(tt.QuadPart - mac_boot_ticks);
188 }
189
190
191 /*
192 * Delay by specified number of microseconds (<1 second)
193 */
194
195 void Delay_usec(uint32 usec)
196 {
197 // FIXME: fortunately, Delay_usec() is generally used with
198 // millisecond resolution anyway
199 Sleep(usec / 1000);
200 }
201
202
203 /*
204 * Suspend emulator thread, virtual CPU in idle mode
205 */
206
207 struct idle_sentinel {
208 idle_sentinel();
209 ~idle_sentinel();
210 };
211 static idle_sentinel idle_sentinel;
212
213 static int idle_sem_ok = -1;
214 static HANDLE idle_sem = NULL;
215
216 static HANDLE idle_lock = NULL;
217 #define LOCK_IDLE WaitForSingleObject(idle_lock, INFINITE)
218 #define UNLOCK_IDLE ReleaseMutex(idle_lock)
219
220 idle_sentinel::idle_sentinel()
221 {
222 LOCK_IDLE;
223 idle_sem_ok = 1;
224 if ((idle_sem = CreateSemaphore(0, 0, 1, NULL)) == NULL)
225 idle_sem_ok = 0;
226 if ((idle_lock = CreateMutex(NULL, FALSE, NULL)) == NULL)
227 idle_sem_ok = 0;
228 UNLOCK_IDLE;
229 }
230
231 idle_sentinel::~idle_sentinel()
232 {
233 if (idle_lock) {
234 ReleaseMutex(idle_lock);
235 CloseHandle(idle_lock);
236 }
237 if (idle_sem) {
238 ReleaseSemaphore(idle_sem, 1, NULL);
239 CloseHandle(idle_sem);
240 }
241 }
242
243 void idle_wait(void)
244 {
245 LOCK_IDLE;
246 if (idle_sem_ok > 0) {
247 idle_sem_ok++;
248 UNLOCK_IDLE;
249 WaitForSingleObject(idle_sem, INFINITE);
250 return;
251 }
252 UNLOCK_IDLE;
253
254 // Fallback: sleep 10 ms (this should not happen though)
255 Delay_usec(10000);
256 }
257
258
259 /*
260 * Resume execution of emulator thread, events just arrived
261 */
262
263 void idle_resume(void)
264 {
265 LOCK_IDLE;
266 if (idle_sem_ok > 1) {
267 idle_sem_ok--;
268 UNLOCK_IDLE;
269 ReleaseSemaphore(idle_sem, 1, NULL);
270 return;
271 }
272 UNLOCK_IDLE;
273 }