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