1 
2 #include "sysconfig.h"
3 #include "sysdeps.h"
4 
5 #ifdef WITH_PPC
6 
7 #include "options.h"
8 #include "threaddep/thread.h"
9 #include "machdep/rpt.h"
10 #include "memory.h"
11 #include "cpuboard.h"
12 #include "debug.h"
13 #include "custom.h"
14 #include "uae.h"
15 #include "gui.h"
16 #include "autoconf.h"
17 #include "uae/dlopen.h"
18 #include "uae/log.h"
19 #include "uae/ppc.h"
20 #include "uae/qemu.h"
21 
22 #define SPINLOCK_DEBUG 0
23 #define PPC_ACCESS_LOG 0
24 
25 #define PPC_DEBUG_ADDR_FROM 0x000000
26 #define PPC_DEBUG_ADDR_TO   0xffffff
27 
28 #ifdef WITH_PEARPC_CPU
29 #include "pearpc/cpu/cpu.h"
30 #include "pearpc/io/io.h"
31 #include "pearpc/cpu/cpu_generic/ppc_cpu.h"
32 #endif
33 
34 #define TRACE(format, ...) write_log(_T("PPC: ") format, ## __VA_ARGS__)
35 
36 #ifdef WINUAE
37 #define WIN32_SPINLOCK
38 #endif
39 
40 #if SPINLOCK_DEBUG
41 static volatile int spinlock_cnt;
42 #endif
43 
44 static volatile bool ppc_spinlock_waiting;
45 
46 #ifdef WIN32_SPINLOCK
47 #define CRITICAL_SECTION_SPIN_COUNT 5000
48 static CRITICAL_SECTION ppc_cs1, ppc_cs2;
49 static bool ppc_cs_initialized;
50 #else
51 #include <glib.h>
52 static GMutex mutex, mutex2;
53 #endif
54 
uae_ppc_spinlock_get(void)55 void uae_ppc_spinlock_get(void)
56 {
57 #ifdef WIN32_SPINLOCK
58 	EnterCriticalSection(&ppc_cs2);
59 	ppc_spinlock_waiting = true;
60 	EnterCriticalSection(&ppc_cs1);
61 	ppc_spinlock_waiting = false;
62 	LeaveCriticalSection(&ppc_cs2);
63 #else
64 	g_mutex_lock(&mutex2);
65 	ppc_spinlock_waiting = true;
66 	g_mutex_lock(&mutex);
67 	ppc_spinlock_waiting = false;
68 	g_mutex_unlock(&mutex2);
69 #endif
70 #if SPINLOCK_DEBUG
71 	if (spinlock_cnt != 0)
72 		write_log(_T("uae_ppc_spinlock_get %d!\n"), spinlock_cnt);
73 	spinlock_cnt++;
74 #endif
75 }
76 
uae_ppc_spinlock_release(void)77 void uae_ppc_spinlock_release(void)
78 {
79 #if SPINLOCK_DEBUG
80 	if (spinlock_cnt != 1)
81 		write_log(_T("uae_ppc_spinlock_release %d!\n"), spinlock_cnt);
82 	spinlock_cnt--;
83 #endif
84 #ifdef WIN32_SPINLOCK
85 	LeaveCriticalSection(&ppc_cs1);
86 #else
87 	g_mutex_unlock(&mutex);
88 #endif
89 }
90 
uae_ppc_spinlock_create(void)91 static void uae_ppc_spinlock_create(void)
92 {
93 #ifdef WIN32_SPINLOCK
94 #if SPINLOCK_DEBUG
95 	write_log(_T("uae_ppc_spinlock_create\n"));
96 #endif
97 	if (ppc_cs_initialized) {
98 		DeleteCriticalSection(&ppc_cs1);
99 		DeleteCriticalSection(&ppc_cs2);
100 	}
101 	InitializeCriticalSectionAndSpinCount(&ppc_cs1, CRITICAL_SECTION_SPIN_COUNT);
102 	InitializeCriticalSectionAndSpinCount(&ppc_cs2, CRITICAL_SECTION_SPIN_COUNT);
103 #if SPINLOCK_DEBUG
104 	spinlock_cnt = 0;
105 #endif
106 	ppc_cs_initialized = true;
107 #endif
108 }
109 
110 volatile int ppc_state;
111 static volatile bool ppc_thread_running;
112 int ppc_cycle_count;
113 static volatile bool ppc_access;
114 static volatile int ppc_cpu_lock_state;
115 static bool ppc_init_done;
116 static int ppc_implementation;
117 static bool ppc_paused;
118 
119 #define CSPPC_PVR 0x00090204
120 #define BLIZZPPC_PVR 0x00070101
121 
122 #define KB * 1024
123 #define MB * (1024 * 1024)
124 
125 /* Dummy PPC implementation */
126 
dummy_ppc_cpu_close(void)127 static void PPCCALL dummy_ppc_cpu_close(void) { }
dummy_ppc_cpu_stop(void)128 static void PPCCALL dummy_ppc_cpu_stop(void) { }
dummy_ppc_cpu_atomic_raise_ext_exception(void)129 static void PPCCALL dummy_ppc_cpu_atomic_raise_ext_exception(void) { }
dummy_ppc_cpu_atomic_cancel_ext_exception(void)130 static void PPCCALL dummy_ppc_cpu_atomic_cancel_ext_exception(void) { }
dummy_ppc_cpu_map_memory(PPCMemoryRegion * regions,int count)131 static void PPCCALL dummy_ppc_cpu_map_memory(PPCMemoryRegion *regions, int count) { }
dummy_ppc_cpu_set_pc(int cpu,uint32_t value)132 static void PPCCALL dummy_ppc_cpu_set_pc(int cpu, uint32_t value) { }
dummy_ppc_cpu_run_continuous(void)133 static void PPCCALL dummy_ppc_cpu_run_continuous(void) { }
dummy_ppc_cpu_run_single(int count)134 static void PPCCALL dummy_ppc_cpu_run_single(int count) { }
135 //static uint64_t PPCCALL dummy_ppc_cpu_get_dec(void) { return 0; }
136 //static void PPCCALL dummy_ppc_cpu_do_dec(int value) { }
137 
dummy_ppc_cpu_pause(int pause)138 static void PPCCALL dummy_ppc_cpu_pause(int pause)
139 {
140     UAE_LOG_STUB("pause=%d\n", pause);
141 }
142 
143 /* Functions typedefs for PPC implementation */
144 
145 typedef bool (PPCCALL *ppc_cpu_init_function)(const char *model, uint32_t hid1);
146 typedef bool (PPCCALL *ppc_cpu_init_pvr_function)(uint32_t pvr);
147 typedef void (PPCCALL *ppc_cpu_close_function)(void);
148 typedef void (PPCCALL *ppc_cpu_stop_function)(void);
149 typedef void (PPCCALL *ppc_cpu_atomic_raise_ext_exception_function)(void);
150 typedef void (PPCCALL *ppc_cpu_atomic_cancel_ext_exception_function)(void);
151 typedef void (PPCCALL *ppc_cpu_map_memory_function)(PPCMemoryRegion *regions, int count);
152 typedef void (PPCCALL *ppc_cpu_set_pc_function)(int cpu, uint32_t value);
153 typedef void (PPCCALL *ppc_cpu_run_continuous_function)(void);
154 typedef void (PPCCALL *ppc_cpu_run_single_function)(int count);
155 typedef uint64_t (PPCCALL *ppc_cpu_get_dec_function)(void);
156 typedef void (PPCCALL *ppc_cpu_do_dec_function)(int value);
157 typedef void (PPCCALL *ppc_cpu_pause_function)(int pause);
158 typedef bool (PPCCALL *ppc_cpu_check_state_function)(int state);
159 typedef void (PPCCALL *ppc_cpu_set_state_function)(int state);
160 typedef void (PPCCALL *ppc_cpu_reset_function)(void);
161 
162 /* Function pointers to active PPC implementation */
163 
164 static struct impl {
165 	/* Common */
166 	ppc_cpu_atomic_raise_ext_exception_function atomic_raise_ext_exception;
167 	ppc_cpu_atomic_cancel_ext_exception_function atomic_cancel_ext_exception;
168 
169 	/* PearPC */
170 	ppc_cpu_run_continuous_function run_continuous;
171 	ppc_cpu_init_pvr_function init_pvr;
172 	ppc_cpu_pause_function pause;
173 	ppc_cpu_close_function close;
174 	ppc_cpu_stop_function stop;
175 	ppc_cpu_set_pc_function set_pc;
176 	ppc_cpu_run_single_function run_single;
177 	ppc_cpu_get_dec_function get_dec;
178 	ppc_cpu_do_dec_function do_dec;
179 
180 	/* QEMU */
181 	ppc_cpu_init_function init;
182 	ppc_cpu_map_memory_function map_memory;
183 	ppc_cpu_check_state_function check_state;
184 	ppc_cpu_set_state_function set_state;
185 	ppc_cpu_reset_function reset;
186 	qemu_uae_ppc_in_cpu_thread_function in_cpu_thread;
187 	qemu_uae_ppc_external_interrupt_function external_interrupt;
188 	qemu_uae_lock_function lock;
189 
190 } impl;
191 
load_dummy_implementation(void)192 static void load_dummy_implementation(void)
193 {
194 	write_log(_T("PPC: Loading dummy implementation\n"));
195 	memset(&impl, 0, sizeof(impl));
196 	impl.close = dummy_ppc_cpu_close;
197 	impl.stop = dummy_ppc_cpu_stop;
198 	impl.atomic_raise_ext_exception = dummy_ppc_cpu_atomic_raise_ext_exception;
199 	impl.atomic_cancel_ext_exception = dummy_ppc_cpu_atomic_cancel_ext_exception;
200 	impl.map_memory = dummy_ppc_cpu_map_memory;
201 	impl.set_pc = dummy_ppc_cpu_set_pc;
202 	impl.run_continuous = dummy_ppc_cpu_run_continuous;
203 	impl.run_single = dummy_ppc_cpu_run_single;
204 	impl.pause = dummy_ppc_cpu_pause;
205 }
206 
uae_patch_library_ppc(UAE_DLHANDLE handle)207 static void uae_patch_library_ppc(UAE_DLHANDLE handle)
208 {
209 	void *ptr;
210 
211 	ptr = uae_dlsym(handle, "uae_ppc_io_mem_read");
212 	if (ptr) *((uae_ppc_io_mem_read_function *) ptr) = &uae_ppc_io_mem_read;
213 	else write_log(_T("WARNING: uae_ppc_io_mem_read not set\n"));
214 
215 	ptr = uae_dlsym(handle, "uae_ppc_io_mem_write");
216 	if (ptr) *((uae_ppc_io_mem_write_function *) ptr) = &uae_ppc_io_mem_write;
217 	else write_log(_T("WARNING: uae_ppc_io_mem_write not set\n"));
218 
219 	ptr = uae_dlsym(handle, "uae_ppc_io_mem_read64");
220 	if (ptr) *((uae_ppc_io_mem_read64_function *) ptr) = &uae_ppc_io_mem_read64;
221 	else write_log(_T("WARNING: uae_ppc_io_mem_read64 not set\n"));
222 
223 	ptr = uae_dlsym(handle, "uae_ppc_io_mem_write64");
224 	if (ptr) *((uae_ppc_io_mem_write64_function *) ptr) = &uae_ppc_io_mem_write64;
225 	else write_log(_T("WARNING: uae_ppc_io_mem_write64 not set\n"));
226 }
227 
load_qemu_implementation(void)228 static bool load_qemu_implementation(void)
229 {
230 #ifdef WITH_QEMU_CPU
231 	write_log(_T("PPC: Loading QEmu implementation\n"));
232 	memset(&impl, 0, sizeof(impl));
233 
234 	UAE_DLHANDLE handle = uae_qemu_uae_init();
235 	if (!handle) {
236 #ifdef FSUAE
237 		gui_message(_T("PPC: Error loading qemu-uae plugin\n"));
238 #else
239 		notify_user (NUMSG_NO_PPC);
240 #endif
241 		return false;
242 	}
243 	write_log(_T("PPC: Loaded qemu-uae library at %p\n"), handle);
244 
245 	/* Retrieve function pointers from library */
246 
247 	impl.init = (ppc_cpu_init_function) uae_dlsym(handle, "ppc_cpu_init");
248 	//impl.free = (ppc_cpu_free_function) uae_dlsym(handle, "ppc_cpu_free");
249 	//impl.stop = (ppc_cpu_stop_function) uae_dlsym(handle, "ppc_cpu_stop");
250 	impl.external_interrupt = (qemu_uae_ppc_external_interrupt_function) uae_dlsym(handle, "qemu_uae_ppc_external_interrupt");
251 	impl.map_memory = (ppc_cpu_map_memory_function) uae_dlsym(handle, "ppc_cpu_map_memory");
252 	impl.run_continuous = (ppc_cpu_run_continuous_function) uae_dlsym(handle, "ppc_cpu_run_continuous");
253 	impl.check_state = (ppc_cpu_check_state_function) uae_dlsym(handle, "ppc_cpu_check_state");
254 	impl.set_state = (ppc_cpu_set_state_function) uae_dlsym(handle, "ppc_cpu_set_state");
255 	impl.reset = (ppc_cpu_reset_function) uae_dlsym(handle, "ppc_cpu_reset");
256 	impl.in_cpu_thread = (qemu_uae_ppc_in_cpu_thread_function) uae_dlsym(handle, "qemu_uae_ppc_in_cpu_thread");
257 	impl.lock = (qemu_uae_lock_function) uae_dlsym(handle, "qemu_uae_lock");
258 
259 	// FIXME: not needed, handled internally by uae_dlopen_plugin
260 	// uae_dlopen_patch_common(handle);
261 
262 	uae_patch_library_ppc(handle);
263 	return true;
264 #else
265 	return false;
266 #endif
267 }
268 
load_pearpc_implementation(void)269 static bool load_pearpc_implementation(void)
270 {
271 #ifdef WITH_PEARPC_CPU
272 	write_log(_T("PPC: Loading PearPC implementation\n"));
273 	memset(&impl, 0, sizeof(impl));
274 
275 	impl.init_pvr = ppc_cpu_init;
276 	impl.close = ppc_cpu_close;
277 	impl.stop = ppc_cpu_stop;
278 	impl.atomic_raise_ext_exception = ppc_cpu_atomic_raise_ext_exception;
279 	impl.atomic_cancel_ext_exception = ppc_cpu_atomic_cancel_ext_exception;
280 	impl.set_pc = ppc_cpu_set_pc;
281 	impl.run_continuous = ppc_cpu_run_continuous;
282 	impl.run_single = ppc_cpu_run_single;
283 	impl.get_dec = ppc_cpu_get_dec;
284 	impl.do_dec = ppc_cpu_do_dec;
285 	return true;
286 #else
287 	return false;
288 #endif
289 }
290 
load_ppc_implementation(void)291 static void load_ppc_implementation(void)
292 {
293 	int impl = currprefs.ppc_implementation;
294 	if (impl == PPC_IMPLEMENTATION_AUTO || impl == PPC_IMPLEMENTATION_QEMU) {
295 		if (load_qemu_implementation()) {
296 			ppc_implementation = PPC_IMPLEMENTATION_QEMU;
297 			return;
298 		}
299 	}
300 	if (impl == PPC_IMPLEMENTATION_AUTO || impl == PPC_IMPLEMENTATION_PEARPC) {
301 		if (load_pearpc_implementation()) {
302 			ppc_implementation = PPC_IMPLEMENTATION_PEARPC;
303 			return;
304 		}
305 	}
306 	load_dummy_implementation();
307 	ppc_implementation = PPC_IMPLEMENTATION_DUMMY;
308 }
309 
using_qemu(void)310 static bool using_qemu(void)
311 {
312 	return ppc_implementation == PPC_IMPLEMENTATION_QEMU;
313 }
314 
using_pearpc(void)315 static bool using_pearpc(void)
316 {
317 	return ppc_implementation == PPC_IMPLEMENTATION_PEARPC;
318 }
319 
320 enum PPCLockMethod {
321 	PPC_RELEASE_SPINLOCK,
322 	PPC_KEEP_SPINLOCK,
323 };
324 
325 enum PPCLockStatus {
326 	PPC_NO_LOCK_NEEDED,
327 	PPC_LOCKED,
328 	PPC_LOCKED_WITHOUT_SPINLOCK,
329 };
330 
get_ppc_lock(PPCLockMethod method)331 static PPCLockStatus get_ppc_lock(PPCLockMethod method)
332 {
333 	if (impl.in_cpu_thread()) {
334 		return PPC_NO_LOCK_NEEDED;
335 	} else if (method == PPC_RELEASE_SPINLOCK) {
336 
337 		uae_ppc_spinlock_release();
338 		impl.lock(QEMU_UAE_LOCK_ACQUIRE);
339 		return PPC_LOCKED_WITHOUT_SPINLOCK;
340 
341 	} else if (method == PPC_KEEP_SPINLOCK) {
342 
343 		bool trylock_called = false;
344 		while (true) {
345 			if (ppc_spinlock_waiting) {
346 				/* PPC CPU is waiting for the spinlock and the UAE side
347 				 * owns the spinlock - no additional locking needed */
348 				if (trylock_called) {
349 					impl.lock(QEMU_UAE_LOCK_TRYLOCK_CANCEL);
350 				}
351 				return PPC_NO_LOCK_NEEDED;
352 			}
353 			int error = impl.lock(QEMU_UAE_LOCK_TRYLOCK);
354 			if (error == 0) {
355 				/* Lock succeeded */
356 				return PPC_LOCKED;
357 			}
358 			trylock_called = true;
359 		}
360 	} else {
361 #ifdef FSUAE
362 		//uae_abort("invalid ppc loc method");
363 		write_log("invalid ppc loc method");
364 		abort();
365 #else
366 		write_log("?\n");
367 		return PPC_NO_LOCK_NEEDED;
368 #endif
369 	}
370 }
371 
release_ppc_lock(PPCLockStatus status)372 static void release_ppc_lock(PPCLockStatus status)
373 {
374 	if (status == PPC_NO_LOCK_NEEDED) {
375 		return;
376 	} else if (status == PPC_LOCKED_WITHOUT_SPINLOCK) {
377 		impl.lock(QEMU_UAE_LOCK_RELEASE);
378 		uae_ppc_spinlock_get();
379 	} else if (status == PPC_LOCKED) {
380 		impl.lock(QEMU_UAE_LOCK_RELEASE);
381 	}
382 }
383 
initialize(void)384 static void initialize(void)
385 {
386 	static bool initialized = false;
387 	if (initialized) {
388 		return;
389 	}
390 
391 	initialized = true;
392 
393 	load_ppc_implementation();
394 
395 	uae_ppc_spinlock_create();
396 	/* Grab the lock for the first time. This lock will be released
397 	 * by the UAE emulation thread when the PPC CPU can do I/O. */
398 	uae_ppc_spinlock_get();
399 }
400 
map_banks(void)401 static void map_banks(void)
402 {
403 	if (impl.map_memory == NULL) {
404 		return;
405 	}
406 	/*
407 	 * Use NULL for memory to get callbacks for read/write. Use real
408 	 * memory address for direct access to RAM banks (looks like this
409 	 * is needed by JIT, or at least more work is needed on QEmu Side
410 	 * to allow all memory access to go via callbacks).
411 	 */
412 
413 	PPCMemoryRegion regions[UAE_MEMORY_REGIONS_MAX];
414 	UaeMemoryMap map;
415 	uae_memory_map(&map);
416 
417 	for (int i = 0; i < map.num_regions; i++) {
418 		UaeMemoryRegion *r = &map.regions[i];
419 		regions[i].start = r->start;
420 		regions[i].size = r->size;
421 		regions[i].name = ua(r->name);
422 		regions[i].alias = r->alias;
423 		regions[i].memory = r->memory;
424 	}
425 
426 	if (impl.in_cpu_thread && impl.in_cpu_thread() == false) {
427 		uae_ppc_spinlock_release();
428 	}
429 	impl.map_memory(regions, map.num_regions);
430 	if (impl.in_cpu_thread && impl.in_cpu_thread() == false) {
431 		uae_ppc_spinlock_get();
432 	}
433 
434 	for (int i = 0; i < map.num_regions; i++) {
435 		free(regions[i].name);
436 	}
437 }
438 
set_and_wait_for_state(int state,int unlock)439 static void set_and_wait_for_state(int state, int unlock)
440 {
441 	if (state == PPC_CPU_STATE_PAUSED) {
442 		if (ppc_paused)
443 			return;
444 		ppc_paused = true;
445 	} else if (state == PPC_CPU_STATE_RUNNING) {
446 		if (!ppc_paused)
447 			return;
448 		ppc_paused = false;
449 	} else {
450 		return;
451 	}
452 	if (using_qemu()) {
453 		if (impl.in_cpu_thread() == false) {
454 			uae_ppc_spinlock_release();
455 		}
456 		impl.set_state(state);
457 		if (impl.in_cpu_thread() == false) {
458 			uae_ppc_spinlock_get();
459 		}
460 	}
461 }
462 
uae_self_is_ppc(void)463 bool uae_self_is_ppc(void)
464 {
465 	if (ppc_state == PPC_STATE_INACTIVE)
466 		return false;
467 	return impl.in_cpu_thread();
468 }
469 
uae_ppc_wakeup_main(void)470 void uae_ppc_wakeup_main(void)
471 {
472 	if (uae_self_is_ppc()) {
473 		sleep_cpu_wakeup();
474 	}
475 }
476 
ppc_map_banks(uae_u32 start,uae_u32 size,const TCHAR * name,void * addr,bool remove)477 void ppc_map_banks(uae_u32 start, uae_u32 size, const TCHAR *name, void *addr, bool remove)
478 {
479 	if (ppc_state == PPC_STATE_INACTIVE || !impl.map_memory)
480 		return;
481 
482 	PPCMemoryRegion r;
483 	r.start = start;
484 	r.size = size;
485 	r.name = ua(name);
486 	r.alias = remove ? 0xffffffff : 0;
487 	r.memory = addr;
488 
489 	if (impl.in_cpu_thread() == false) {
490 		/* map_memory will acquire the qemu global lock, so we must ensure
491 		 * the PPC CPU can finish any I/O requests and release the lock. */
492 		uae_ppc_spinlock_release();
493 	}
494 	impl.map_memory(&r, -1);
495 	if (impl.in_cpu_thread() == false) {
496 		uae_ppc_spinlock_get();
497 	}
498 	free((void*)r.name);
499 }
500 
uae_ppc_get_model(const TCHAR ** model,uint32_t * hid1)501 void uae_ppc_get_model(const TCHAR **model, uint32_t *hid1)
502 {
503 	if (currprefs.ppc_model[0]) {
504 		/* Override PPC CPU model. See qemu/target-ppc/cpu-models.c for
505 		 * a list of valid CPU model identifiers */
506 		*model = currprefs.ppc_model;
507 	} else {
508 		/* Set default CPU model based on accelerator board */
509 		if (ISCPUBOARD(BOARD_BLIZZARD, BOARD_BLIZZARD_SUB_PPC)) {
510 			*model = _T("603ev");
511 		} else {
512 			*model = _T("604e");
513 		}
514 	}
515 	if (ISCPUBOARD(BOARD_BLIZZARD, BOARD_BLIZZARD_SUB_PPC)) {
516 		*hid1 = 0xc0000000; // 4x
517 	} else {
518 		*hid1 = 0xa0000000; // 4x
519 	}
520 }
521 
cpu_init(void)522 static void cpu_init(void)
523 {
524 	if (using_pearpc()) {
525 		const TCHAR *model;
526 		uint32_t hid1;
527 		uae_ppc_get_model(&model, &hid1);
528 		uint32_t pvr = 0;
529 		if (_tcsicmp(model, _T("603ev")) == 0) {
530 			pvr = BLIZZPPC_PVR;
531 		} else if (_tcsicmp(model, _T("604e")) == 0) {
532 			pvr = CSPPC_PVR;
533 		} else {
534 			pvr = CSPPC_PVR;
535 			write_log(_T("PPC: Unrecognized model \"%s\", using PVR 0x%08x\n"), model, pvr);
536 		}
537 		write_log(_T("PPC: Calling ppc_cpu_init with PVR 0x%08x\n"), pvr);
538 		impl.init_pvr(pvr);
539 		return;
540 	}
541 
542 	/* QEMU: CPU has already been initialized by qemu_uae_init */
543 }
544 
uae_ppc_cpu_reset(void)545 static void uae_ppc_cpu_reset(void)
546 {
547 	TRACE(_T("uae_ppc_cpu_reset\n"));
548 
549 	initialize();
550 
551 	static bool ppc_cpu_init_done;
552 	if (!ppc_cpu_init_done) {
553 		write_log(_T("PPC: Hard reset\n"));
554 		cpu_init();
555 		ppc_cpu_init_done = true;
556 	}
557 
558 	/* Map memory and I/O banks (for QEmu PPC implementation) */
559 	map_banks();
560 
561 	if (using_qemu()) {
562 		impl.reset();
563 	} else if (using_pearpc()) {
564 		write_log(_T("PPC: Init\n"));
565 		impl.set_pc(0, 0xfff00100);
566 		ppc_cycle_count = 2000;
567 	}
568 
569 	ppc_cpu_lock_state = 0;
570 	ppc_state = PPC_STATE_ACTIVE;
571 }
572 
ppc_thread(void * v)573 static void *ppc_thread(void *v)
574 {
575 	if (using_qemu()) {
576 		write_log(_T("PPC: Warning - ppc_thread started with QEMU impl\n"));
577 	} else {
578 		uae_ppc_cpu_reset();
579 		impl.run_continuous();
580 
581 		if (ppc_state == PPC_STATE_ACTIVE || ppc_state == PPC_STATE_SLEEP)
582 			ppc_state = PPC_STATE_STOP;
583 		write_log(_T("ppc_cpu_run() exited.\n"));
584 		ppc_thread_running = false;
585 	}
586 	return NULL;
587 }
588 
uae_ppc_execute_check(void)589 void uae_ppc_execute_check(void)
590 {
591 	if (ppc_spinlock_waiting) {
592 		uae_ppc_spinlock_release();
593 		uae_ppc_spinlock_get();
594 	}
595 }
596 
uae_ppc_execute_quick()597 void uae_ppc_execute_quick()
598 {
599 	uae_ppc_spinlock_release();
600 	sleep_millis_main(1);
601 	uae_ppc_spinlock_get();
602 }
603 
uae_ppc_emulate(void)604 void uae_ppc_emulate(void)
605 {
606 	if (using_pearpc()) {
607 		ppc_interrupt(intlev());
608 		if (ppc_state == PPC_STATE_ACTIVE || ppc_state == PPC_STATE_SLEEP)
609 			impl.run_single(10);
610 	}
611 }
612 
uae_ppc_direct_physical_memory_handle(uint32_t addr,uint8_t * & ptr)613 bool uae_ppc_direct_physical_memory_handle(uint32_t addr, uint8_t *&ptr)
614 {
615 	ptr = get_real_address(addr);
616 	if (!ptr)
617 		gui_message(_T("Executing PPC code at IO address %08x!"), addr);
618 	return true;
619 }
620 
spinlock_pre(uaecptr addr)621 STATIC_INLINE bool spinlock_pre(uaecptr addr)
622 {
623 	addrbank *ab = &get_mem_bank(addr);
624 	if ((ab->flags & ABFLAG_THREADSAFE) == 0) {
625 		sleep_cpu_wakeup();
626 		uae_ppc_spinlock_get();
627 		return true;
628 	}
629 	return false;
630 }
631 
spinlock_post(bool locked)632 STATIC_INLINE void spinlock_post(bool locked)
633 {
634 	if (locked)
635 		uae_ppc_spinlock_release();
636 }
637 
uae_ppc_io_mem_write(uint32_t addr,uint32_t data,int size)638 bool UAECALL uae_ppc_io_mem_write(uint32_t addr, uint32_t data, int size)
639 {
640 	bool locked = false;
641 
642 	while (ppc_thread_running && ppc_cpu_lock_state < 0 && ppc_state);
643 
644 #if PPC_ACCESS_LOG > 0 && PPC_ACCESS_LOG < 2
645 	if (!valid_address(addr, size)) {
646 		if (addr >= PPC_DEBUG_ADDR_FROM && addr < PPC_DEBUG_ADDR_TO)
647 			write_log(_T("PPC io write %08x = %08x %d\n"), addr, data, size);
648 	}
649 #endif
650 
651 	locked = spinlock_pre(addr);
652 	switch (size)
653 	{
654 	case 4:
655 		put_long(addr, data);
656 		break;
657 	case 2:
658 		put_word(addr, data);
659 		break;
660 	case 1:
661 		put_byte(addr, data);
662 		break;
663 	}
664 
665 	if (addr >= 0xdff000 && addr < 0xe00000) {
666 		int reg = addr & 0x1fe;
667 		switch (reg) {
668 			case 0x09c: // INTREQ
669 			case 0x09a: // INTENA
670 			if (data & 0x8000) {
671 				// possible interrupt change:
672 				// make sure M68K thread reacts to it ASAP.
673 				uae_int_requested |= 0x010000;
674 			}
675 			break;
676 		}
677 	}
678 
679 	spinlock_post(locked);
680 
681 #if PPC_ACCESS_LOG >= 2
682 	write_log(_T("PPC write %08x = %08x %d\n"), addr, data, size);
683 #endif
684 
685 	return true;
686 }
687 
uae_ppc_io_mem_read(uint32_t addr,uint32_t * data,int size)688 bool UAECALL uae_ppc_io_mem_read(uint32_t addr, uint32_t *data, int size)
689 {
690 	uint32_t v;
691 	bool locked = false;
692 
693 	while (ppc_thread_running && ppc_cpu_lock_state < 0 && ppc_state);
694 
695 	if (addr >= 0xdff000 && addr < 0xe00000) {
696 		int reg = addr & 0x1fe;
697 		// shortcuts for common registers
698 		switch (reg) {
699 			case 0x01c: // INTENAR
700 			*data = intena;
701 			return true;
702 			case 0x01e: // INTREQR
703 			*data = intreq;
704 			return true;
705 		}
706 	}
707 
708 	locked = spinlock_pre(addr);
709 	switch (size)
710 	{
711 	case 4:
712 		v = get_long(addr);
713 		break;
714 	case 2:
715 		v = get_word(addr);
716 		break;
717 	case 1:
718 		v = get_byte(addr);
719 		break;
720 	}
721 	*data = v;
722 	spinlock_post(locked);
723 
724 #if PPC_ACCESS_LOG > 0 && PPC_ACCESS_LOG < 2
725 	if (!valid_address(addr, size)) {
726 		if (addr >= PPC_DEBUG_ADDR_FROM && addr < PPC_DEBUG_ADDR_TO && addr != 0xdff006)
727 			write_log(_T("PPC io read %08x=%08x %d\n"), addr, v, size);
728 	}
729 #endif
730 #if PPC_ACCESS_LOG >= 2
731 	if (addr < 0xb00000 || addr > 0xc00000)
732 		write_log(_T("PPC read %08x=%08x %d\n"), addr, v, size);
733 #endif
734 	return true;
735 }
736 
uae_ppc_io_mem_write64(uint32_t addr,uint64_t data)737 bool UAECALL uae_ppc_io_mem_write64(uint32_t addr, uint64_t data)
738 {
739 	bool locked = false;
740 
741 	while (ppc_thread_running && ppc_cpu_lock_state < 0 && ppc_state);
742 
743 	locked = spinlock_pre(addr);
744 	put_long(addr + 0, data >> 32);
745 	put_long(addr + 4, data & 0xffffffff);
746 	spinlock_post(locked);
747 
748 #if PPC_ACCESS_LOG >= 2
749 	write_log(_T("PPC mem write64 %08x = %08llx\n"), addr, data);
750 #endif
751 
752 	return true;
753 }
754 
uae_ppc_io_mem_read64(uint32_t addr,uint64_t * data)755 bool UAECALL uae_ppc_io_mem_read64(uint32_t addr, uint64_t *data)
756 {
757 	bool locked = false;
758 	uint32_t v1, v2;
759 
760 	while (ppc_thread_running && ppc_cpu_lock_state < 0 && ppc_state);
761 
762 	locked = spinlock_pre(addr);
763 	v1 = get_long(addr + 0);
764 	v2 = get_long(addr + 4);
765 	*data = ((uint64_t)v1 << 32) | v2;
766 	spinlock_post(locked);
767 
768 #if PPC_ACCESS_LOG >= 2
769 	write_log(_T("PPC mem read64 %08x = %08llx\n"), addr, *data);
770 #endif
771 
772 	return true;
773 }
774 
uae_ppc_cpu_stop(void)775 void uae_ppc_cpu_stop(void)
776 {
777 	if (ppc_state == PPC_STATE_INACTIVE)
778 		return;
779 	TRACE(_T("uae_ppc_cpu_stop %d %d\n"), ppc_thread_running, ppc_state);
780 	if (using_qemu()) {
781 		write_log(_T("PPC: Stopping...\n"));
782 		set_and_wait_for_state(PPC_CPU_STATE_PAUSED, 1);
783 		write_log(_T("PPC: Stopped\n"));
784 	} else if (using_pearpc()) {
785 		if (ppc_thread_running && ppc_state) {
786 			write_log(_T("PPC: Stopping...\n"));
787 			impl.stop();
788 			while (ppc_state != PPC_STATE_STOP && ppc_state != PPC_STATE_CRASH) {
789 				uae_ppc_wakeup();
790 				uae_ppc_spinlock_release();
791 				uae_ppc_spinlock_get();
792 			}
793 			write_log(_T("PPC: Stopped\n"));
794 		}
795 	}
796 	ppc_state = PPC_STATE_INACTIVE;
797 }
798 
uae_ppc_cpu_reboot(void)799 void uae_ppc_cpu_reboot(void)
800 {
801 	TRACE(_T("uae_ppc_cpu_reboot\n"));
802 
803 	initialize();
804 
805 	if (!ppc_thread_running) {
806 		write_log(_T("Starting PPC thread.\n"));
807 		ppc_thread_running = true;
808 		if (using_qemu()) {
809 			uae_ppc_cpu_reset();
810 			//qemu_uae_ppc_start();
811 			impl.set_state(PPC_CPU_STATE_RUNNING);
812 			//set_and_wait_for_state(PPC_CPU_STATE_RUNNING, 0);
813 		} else {
814 			uae_start_thread(NULL, ppc_thread, NULL, NULL);
815 		}
816 	} else if (using_qemu()) {
817 		write_log(_T("PPC: Thread already running, resetting\n"));
818 		uae_ppc_cpu_reset();
819 		set_and_wait_for_state(PPC_CPU_STATE_RUNNING, 1);
820 	}
821 }
822 
uae_ppc_reset(bool hardreset)823 void uae_ppc_reset(bool hardreset)
824 {
825 	if (!currprefs.ppc_mode)
826 		return;
827 	TRACE(_T("uae_ppc_reset hardreset=%d\n"), hardreset);
828 	if (using_qemu()) {
829 	    set_and_wait_for_state(PPC_CPU_STATE_PAUSED, 1);
830 	} else if (using_pearpc()) {
831 		uae_ppc_cpu_stop();
832 		if (hardreset) {
833 			if (ppc_init_done)
834 				impl.close();
835 			ppc_init_done = false;
836 		}
837 	}
838 	ppc_state = PPC_STATE_INACTIVE;
839 }
840 
uae_ppc_free(void)841 void uae_ppc_free(void)
842 {
843 	bool wasactive = ppc_state != PPC_STATE_INACTIVE;
844 	uae_ppc_cpu_stop();
845 	if (wasactive && impl.map_memory)
846 		impl.map_memory(NULL, 0);
847 }
848 
uae_ppc_cpu_lock(void)849 void uae_ppc_cpu_lock(void)
850 {
851 	// when called, lock was already set by other CPU
852 	if (ppc_access) {
853 		// ppc accessing but m68k already locked
854 		ppc_cpu_lock_state = -1;
855 	} else {
856 		// m68k accessing but ppc already locked
857 		ppc_cpu_lock_state = 1;
858 	}
859 }
860 
uae_ppc_cpu_unlock(void)861 bool uae_ppc_cpu_unlock(void)
862 {
863 	if (!ppc_cpu_lock_state)
864 		return true;
865 	ppc_cpu_lock_state = 0;
866 	return false;
867 }
868 
uae_ppc_wakeup(void)869 void uae_ppc_wakeup(void)
870 {
871 	if (ppc_state == PPC_STATE_SLEEP)
872 		ppc_state = PPC_STATE_ACTIVE;
873 }
874 
uae_ppc_interrupt(bool active)875 void uae_ppc_interrupt(bool active)
876 {
877 	if (using_pearpc()) {
878 		if (active) {
879 			impl.atomic_raise_ext_exception();
880 			uae_ppc_wakeup();
881 		} else {
882 			impl.atomic_cancel_ext_exception();
883 		}
884 		return;
885 	}
886 
887 	PPCLockStatus status = get_ppc_lock(PPC_KEEP_SPINLOCK);
888 	impl.external_interrupt(active);
889 	release_ppc_lock(status);
890 }
891 
892 // sleep until interrupt (or PPC stopped)
uae_ppc_doze(void)893 void uae_ppc_doze(void)
894 {
895 	//TRACE(_T("uae_ppc_doze\n"));
896 	if (!ppc_thread_running)
897 		return;
898 	ppc_state = PPC_STATE_SLEEP;
899 	while (ppc_state == PPC_STATE_SLEEP) {
900 		sleep_millis(2);
901 	}
902 }
903 
uae_ppc_crash(void)904 void uae_ppc_crash(void)
905 {
906 	TRACE(_T("uae_ppc_crash\n"));
907 	ppc_state = PPC_STATE_CRASH;
908 	if (impl.stop) {
909 		impl.stop();
910 	}
911 }
912 
uae_ppc_hsync_handler(void)913 void uae_ppc_hsync_handler(void)
914 {
915 	if (ppc_state == PPC_STATE_INACTIVE)
916 		return;
917 	if (using_pearpc()) {
918 		if (ppc_state != PPC_STATE_SLEEP)
919 			return;
920 		if (impl.get_dec() == 0) {
921 			uae_ppc_wakeup();
922 		} else {
923 			impl.do_dec(ppc_cycle_count);
924 		}
925 	}
926 }
927 
uae_ppc_pause(int pause)928 void uae_ppc_pause(int pause)
929 {
930 	if (ppc_state == PPC_STATE_INACTIVE)
931 		return;
932 	// FIXME: assert(uae_is_emulation_thread())
933 	if (using_qemu()) {
934 		if (pause) {
935 			set_and_wait_for_state(PPC_CPU_STATE_PAUSED, 1);
936 		} else {
937 			set_and_wait_for_state(PPC_CPU_STATE_RUNNING, 1);
938 		}
939 	}
940 #if 0
941 	else if (impl.pause) {
942 		impl.pause(pause);
943 	}
944 #endif
945 }
946 
947 #ifdef FSUAE // NL
948 
949 UAE_EXTERN_C void fsuae_ppc_pause(int pause);
fsuae_ppc_pause(int pause)950 UAE_EXTERN_C void fsuae_ppc_pause(int pause)
951 {
952 	/* We cannot call uae_ppc_pause except from the UAE thread due
953 	 * to use of the spinlock */
954 	if (using_qemu()) {
955 		if (pause) {
956 			set_and_wait_for_state(PPC_CPU_STATE_PAUSED, 0);
957 		}
958 		else {
959 			set_and_wait_for_state(PPC_CPU_STATE_RUNNING, 0);
960 		}
961 	}
962 }
963 
964 #endif
965 
966 #else
967 
968 #include "uae/ppc.h"
969 
uae_self_is_ppc(void)970 bool uae_self_is_ppc(void)
971 {
972 	return false;
973 }
974 
975 #endif /* WITH_PPC */
976