1 /*
2 * C64_WIN32.i - Put the pieces together, WIN32 specific stuff
3 *
4 * Frodo (C) 1994-1997,2002 Christian Bauer
5 * WIN32 code by J. Richard Sladkey <jrs@world.std.com>
6 */
7
8 #include <process.h>
9 #include "main.h"
10
11 #define FRAME_INTERVAL (1000/SCREEN_FREQ) // in milliseconds
12 #ifdef FRODO_SC
13 #define SPEEDOMETER_INTERVAL 4000 // in milliseconds
14 #else
15 #define SPEEDOMETER_INTERVAL 1000 // in milliseconds
16 #endif
17 #define JOYSTICK_SENSITIVITY 40 // % of live range
18 #define JOYSTICK_MIN 0x0000 // min value of range
19 #define JOYSTICK_MAX 0xffff // max value of range
20 #define JOYSTICK_RANGE (JOYSTICK_MAX - JOYSTICK_MIN)
21
22 static BOOL high_resolution_timer = FALSE;
23
24 /*
25 * Constructor, system-dependent things
26 */
27
c64_ctor1()28 void C64::c64_ctor1()
29 {
30 Debug("C64::c64_ctor1\n");
31
32 // Initialize joystick variables.
33 joy_state = 0xff;
34
35 // No need to check for state change.
36 state_change = FALSE;
37
38 // Start the synchronization timer.
39 timer_semaphore = NULL;
40 timer_id = NULL;
41 StartTimer();
42 }
43
c64_ctor2()44 void C64::c64_ctor2()
45 {
46 Debug("C64::c64_ctor2\n");
47 }
48
49
50 /*
51 * Destructor, system-dependent things
52 */
53
c64_dtor()54 void C64::c64_dtor()
55 {
56 Debug("C64::c64_dtor\n");
57
58 StopTimer();
59 }
60
61
62 /*
63 * Start emulation
64 */
65
Run()66 void C64::Run()
67 {
68 // Reset chips
69 TheCPU->Reset();
70 TheSID->Reset();
71 TheCIA1->Reset();
72 TheCIA2->Reset();
73 TheCPU1541->Reset();
74
75 // Patch kernal IEC routines
76 orig_kernal_1d84 = Kernal[0x1d84];
77 orig_kernal_1d85 = Kernal[0x1d85];
78 patch_kernal(ThePrefs.FastReset, ThePrefs.Emul1541Proc);
79
80 // Start the CPU thread
81 thread_func();
82 }
83
84
85 /*
86 * Stop emulation
87 */
88
Quit()89 void C64::Quit()
90 {
91 // Ask the thread to quit itself if it is running
92 quit_thyself = TRUE;
93 state_change = TRUE;
94 }
95
96
97 /*
98 * Pause emulation
99 */
100
Pause()101 void C64::Pause()
102 {
103 StopTimer();
104 TheSID->PauseSound();
105 have_a_break = TRUE;
106 state_change = TRUE;
107 }
108
109
110 /*
111 * Resume emulation
112 */
113
Resume()114 void C64::Resume()
115 {
116 StartTimer();
117 TheSID->ResumeSound();
118 have_a_break = FALSE;
119 }
120
121
122 /*
123 * Vertical blank: Poll keyboard and joysticks, update window
124 */
125
VBlank(bool draw_frame)126 void C64::VBlank(bool draw_frame)
127 {
128 //Debug("C64::VBlank\n");
129
130 // Poll the keyboard.
131 TheDisplay->PollKeyboard(TheCIA1->KeyMatrix, TheCIA1->RevMatrix, &joykey);
132 // Poll the joysticks.
133 TheCIA1->Joystick1 = poll_joystick(0);
134 TheCIA1->Joystick2 = poll_joystick(1);
135
136 if (ThePrefs.JoystickSwap) {
137 uint8 tmp = TheCIA1->Joystick1;
138 TheCIA1->Joystick1 = TheCIA1->Joystick2;
139 TheCIA1->Joystick2 = tmp;
140 }
141
142 // Joystick keyboard emulation.
143 if (TheDisplay->NumLock())
144 TheCIA1->Joystick1 &= joykey;
145 else
146 TheCIA1->Joystick2 &= joykey;
147
148 // Count TOD clocks.
149 TheCIA1->CountTOD();
150 TheCIA2->CountTOD();
151
152 #if 1
153 // Output a frag.
154 TheSID->VBlank();
155 #endif
156
157 if (have_a_break)
158 return;
159
160 // Update the window if needed.
161 frame++;
162 if (draw_frame) {
163
164 // Synchronize to the timer if limiting the speed.
165 if (ThePrefs.LimitSpeed) {
166 if (skipped_frames == 0) {
167 // There is a tiny race condtion here that
168 // could cause a full extra delay cycle.
169 WaitForSingleObject(timer_semaphore, INFINITE);
170 }
171 else {
172 Debug("*** Skipped a frame! ***\n");
173 skipped_frames = 0;
174 }
175 }
176
177 // Perform the actual screen update exactly at the
178 // beginning of an interval for the smoothest video.
179 TheDisplay->Update();
180
181 // Compute the speed index and show it in the speedometer.
182 DWORD now = timeGetTime();
183 int elapsed_time = now - ref_time;
184 if (now - ref_time >= SPEEDOMETER_INTERVAL) {
185 double speed_index = double(frame * FRAME_INTERVAL * 100 + elapsed_time/2) / elapsed_time;
186 TheDisplay->Speedometer((int)speed_index);
187 ref_time = now;
188 frame = 0;
189 }
190
191 // Make sure our timer is set correctly.
192 CheckTimerChange();
193 }
194 }
195
196
CheckTimerChange()197 void C64::CheckTimerChange()
198 {
199 // Make sure the timer interval matches the preferences.
200 if (!ThePrefs.LimitSpeed && timer_every == 0)
201 return;
202 if (ThePrefs.LimitSpeed && ThePrefs.SkipFrames == timer_every)
203 return;
204 StopTimer();
205 StartTimer();
206 }
207
208 /*
209 * Open/close joystick drivers given old and new state of
210 * joystick preferences
211 */
212
213 BOOL joystick_open[2];
214
open_close_joysticks(bool oldjoy1,bool oldjoy2,bool newjoy1,bool newjoy2)215 void C64::open_close_joysticks(bool oldjoy1, bool oldjoy2, bool newjoy1, bool newjoy2)
216 {
217 if (oldjoy1 != newjoy1) {
218 joystick_open[0] = FALSE;
219 if (newjoy1) {
220 JOYINFO joyinfo;
221 if (joyGetPos(0, &joyinfo) == JOYERR_NOERROR)
222 joystick_open[0] = TRUE;
223 }
224 }
225
226 if (oldjoy2 != newjoy2) {
227 joystick_open[1] = FALSE;
228 if (newjoy1) {
229 JOYINFO joyinfo;
230 if (joyGetPos(1, &joyinfo) == JOYERR_NOERROR)
231 joystick_open[1] = TRUE;
232 }
233 }
234
235 // XXX: Should have our own new prefs!
236 state_change = TRUE;
237 }
238
239
240 /*
241 * Poll joystick port, return CIA mask
242 */
243
poll_joystick(int port)244 uint8 C64::poll_joystick(int port)
245 {
246 uint8 j = 0xff;
247
248 if (joystick_open[port]) {
249 JOYINFO joyinfo;
250 if (joyGetPos(port, &joyinfo) == JOYERR_NOERROR) {
251 int x = joyinfo.wXpos;
252 int y = joyinfo.wYpos;
253 int buttons = joyinfo.wButtons;
254 int s1 = JOYSTICK_SENSITIVITY;
255 int s2 = 100 - JOYSTICK_SENSITIVITY;
256 if (x < JOYSTICK_MIN + s1*JOYSTICK_RANGE/100)
257 j &= 0xfb; // Left
258 else if (x > JOYSTICK_MIN + s2*JOYSTICK_RANGE/100)
259 j &= 0xf7; // Right
260 if (y < JOYSTICK_MIN + s1*JOYSTICK_RANGE/100)
261 j &= 0xfe; // Up
262 else if (y > JOYSTICK_MIN + s2*JOYSTICK_RANGE/100)
263 j &= 0xfd; // Down
264 if (buttons & 1)
265 j &= 0xef; // Button
266 if (buttons & 2) {
267 Pause();
268 while (joyGetPos(port, &joyinfo) == JOYERR_NOERROR && (joyinfo.wButtons & 2))
269 Sleep(100);
270 Resume();
271 }
272 }
273 }
274
275 return j;
276 }
277
StartTimer()278 void C64::StartTimer()
279 {
280 ref_time = timeGetTime();
281 skipped_frames = 0;
282 frame = 0;
283
284 if (!ThePrefs.LimitSpeed) {
285 timer_every = 0;
286 StopTimer();
287 return;
288 }
289 timer_every = ThePrefs.SkipFrames;
290
291 if (!timer_semaphore) {
292 timer_semaphore = CreateSemaphore(NULL, 0, 1, NULL);
293 if (!timer_semaphore)
294 Debug("CreateSemaphore failed\n");
295 }
296
297 if (!timer_id) {
298
299 // Turn on high-resolution times and delays.
300 int resolution = FRAME_INTERVAL;
301 if (high_resolution_timer) {
302 timeBeginPeriod(1);
303 resolution = 0;
304 }
305
306 timer_id = timeSetEvent(timer_every*FRAME_INTERVAL, resolution, StaticTimeProc, (DWORD) this, TIME_PERIODIC);
307 if (!timer_id)
308 Debug("timeSetEvent failed\n");
309 }
310 }
311
StopTimer()312 void C64::StopTimer()
313 {
314 if (timer_semaphore) {
315 CloseHandle(timer_semaphore);
316 timer_semaphore = NULL;
317 }
318 if (timer_id) {
319 timeKillEvent(timer_id);
320 timer_id = NULL;
321
322 // Turn off high-resolution delays.
323 if (high_resolution_timer)
324 timeEndPeriod(1);
325 }
326
327 }
328
StaticTimeProc(UINT uID,UINT uMsg,DWORD dwUser,DWORD dw1,DWORD dw2)329 void CALLBACK C64::StaticTimeProc(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
330 {
331 C64* TheC64 = (C64 *) dwUser;
332 TheC64->TimeProc(uID);
333 }
334
TimeProc(UINT id)335 void C64::TimeProc(UINT id)
336 {
337 if (id != timer_id) {
338 Debug("TimeProc called for wrong timer id!\n");
339 timeKillEvent(id);
340 return;
341 }
342
343 if (!ReleaseSemaphore(timer_semaphore, 1, NULL))
344 skipped_frames++;
345 }
346
347
348 /*
349 * The emulation's main loop
350 */
351
thread_func()352 void C64::thread_func()
353 {
354 Debug("C64::thread_func\n");
355
356 thread_running = TRUE;
357
358 while (!quit_thyself) {
359
360 if (have_a_break)
361 TheDisplay->WaitUntilActive();
362
363 #ifdef FRODO_SC
364 if (ThePrefs.Emul1541Proc)
365 EmulateCyclesWith1541();
366 else
367 EmulateCyclesWithout1541();
368 state_change = FALSE;
369 #else
370 // The order of calls is important here
371 int cycles = TheVIC->EmulateLine();
372 TheSID->EmulateLine();
373 #if !PRECISE_CIA_CYCLES
374 TheCIA1->EmulateLine(ThePrefs.CIACycles);
375 TheCIA2->EmulateLine(ThePrefs.CIACycles);
376 #endif
377 if (ThePrefs.Emul1541Proc) {
378 int cycles_1541 = ThePrefs.FloppyCycles;
379 TheCPU1541->CountVIATimers(cycles_1541);
380
381 if (!TheCPU1541->Idle) {
382 // 1541 processor active, alternately execute
383 // 6502 and 6510 instructions until both have
384 // used up their cycles
385 while (cycles >= 0 || cycles_1541 >= 0)
386 if (cycles > cycles_1541)
387 cycles -= TheCPU->EmulateLine(1);
388 else
389 cycles_1541 -= TheCPU1541->EmulateLine(1);
390 } else
391 TheCPU->EmulateLine(cycles);
392 } else
393 // 1541 processor disabled, only emulate 6510
394 TheCPU->EmulateLine(cycles);
395 #endif
396 }
397
398 thread_running = FALSE;
399
400 }
401
402 #ifdef FRODO_SC
403
EmulateCyclesWith1541()404 void C64::EmulateCyclesWith1541()
405 {
406 thread_running = TRUE;
407 while (!state_change) {
408 // The order of calls is important here
409 if (TheVIC->EmulateCycle())
410 TheSID->EmulateLine();
411 #ifndef BATCH_CIA_CYCLES
412 TheCIA1->EmulateCycle();
413 TheCIA2->EmulateCycle();
414 #endif
415 TheCPU->EmulateCycle();
416 TheCPU1541->CountVIATimers(1);
417 if (!TheCPU1541->Idle)
418 TheCPU1541->EmulateCycle();
419 CycleCounter++;
420 }
421 }
422
EmulateCyclesWithout1541()423 void C64::EmulateCyclesWithout1541()
424 {
425 thread_running = TRUE;
426 while (!state_change) {
427 // The order of calls is important here
428 if (TheVIC->EmulateCycle())
429 TheSID->EmulateLine();
430 #ifndef BATCH_CIA_CYCLES
431 TheCIA1->EmulateCycle();
432 TheCIA2->EmulateCycle();
433 #endif
434 TheCPU->EmulateCycle();
435 CycleCounter++;
436 }
437 }
438
439 #endif
440