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