--- /dev/null
+#ifndef UAE_QEMU_H
+#define UAE_QEMU_H
+
+/* This file is intended to be included by external libraries as well,
+ * so don't pull in too much UAE-specific stuff. */
+
+#include "uae/api.h"
+
+/* The qemu-uae major version must match this */
+#define QEMU_UAE_VERSION_MAJOR 3
+
+/* The qemu-uae minor version must be at least this */
+#define QEMU_UAE_VERSION_MINOR 3
+
+UAE_DECLARE_IMPORT_FUNCTION(
+ void, qemu_uae_version, int *major, int *minor, int *revision)
+UAE_DECLARE_IMPORT_FUNCTION(
+ void, qemu_uae_init, void)
+UAE_DECLARE_IMPORT_FUNCTION(
+ void, qemu_uae_start, void)
+UAE_DECLARE_IMPORT_FUNCTION(
+ int, qemu_uae_lock, int)
+
+UAE_DECLARE_IMPORT_FUNCTION(
+ void, qemu_uae_slirp_init, void)
+UAE_DECLARE_IMPORT_FUNCTION(
+ void, qemu_uae_slirp_input, const uint8_t *pkt, int pkt_len)
+
+UAE_DECLARE_EXPORT_FUNCTION(
+ void, uae_slirp_output, const uint8_t *pkt, int pkt_len)
+
+UAE_DECLARE_IMPORT_FUNCTION(
+ bool, qemu_uae_ppc_init, const char* model, uint32_t hid1)
+UAE_DECLARE_IMPORT_FUNCTION(
+ bool, qemu_uae_ppc_in_cpu_thread, void)
+UAE_DECLARE_IMPORT_FUNCTION(
+ void, qemu_uae_ppc_external_interrupt, bool)
+
+#define QEMU_UAE_LOCK_TRYLOCK 1
+#define QEMU_UAE_LOCK_TRYLOCK_CANCEL 2
+#define QEMU_UAE_LOCK_ACQUIRE 3
+#define QEMU_UAE_LOCK_RELEASE 4
+
+#ifdef UAE
+
+#include "uae/dlopen.h"
+
+UAE_DLHANDLE uae_qemu_uae_init(void);
+
+#endif /* UAE */
+
+#if 0
+#ifdef UAE
+typedef void (QEMUCALL *qemu_uae_version_function)(int *major, int *minor,
+ int *revision);
+extern qemu_uae_version_function qemu_uae_version;
+#else
+void qemu_uae_version(int *major, int *minor, int *revision);
+#endif
+
+#ifdef UAE
+typedef void (QEMUCALL *qemu_uae_init_function)(void);
+extern qemu_uae_init_function qemu_uae_init;
+#else
+void qemu_uae_init(void);
+#endif
+#endif
+
+#endif /* UAE_QEMU_H */
#include "uae/ppc.h"
-/* The qemu-uae major version must match this */
-#define QEMU_UAE_VERSION_MAJOR 2
-
-/* The qemu-uae minor version must be at least this */
-#define QEMU_UAE_VERSION_MINOR 0
+#include "uae/qemu.h"
#define SPINLOCK_DEBUG 0
#define PPC_ACCESS_LOG 0
ppc_cpu_check_state_function check_state;
ppc_cpu_set_state_function set_state;
ppc_cpu_reset_function reset;
+ qemu_uae_ppc_in_cpu_thread_function in_cpu_thread;
+ qemu_uae_ppc_external_interrupt_function external_interrupt;
+ qemu_uae_lock_function lock;
+
} impl;
static void load_dummy_implementation(void)
impl.init = (ppc_cpu_init_function) uae_dlsym(handle, "ppc_cpu_init");
//impl.free = (ppc_cpu_free_function) uae_dlsym(handle, "ppc_cpu_free");
//impl.stop = (ppc_cpu_stop_function) uae_dlsym(handle, "ppc_cpu_stop");
- impl.atomic_raise_ext_exception = (ppc_cpu_atomic_raise_ext_exception_function) uae_dlsym(handle, "ppc_cpu_atomic_raise_ext_exception");
- impl.atomic_cancel_ext_exception = (ppc_cpu_atomic_cancel_ext_exception_function) uae_dlsym(handle, "ppc_cpu_atomic_cancel_ext_exception");
+ impl.external_interrupt = (qemu_uae_ppc_external_interrupt_function) uae_dlsym(handle, "qemu_uae_ppc_external_interrupt");
impl.map_memory = (ppc_cpu_map_memory_function) uae_dlsym(handle, "ppc_cpu_map_memory");
//impl.set_pc = (ppc_cpu_set_pc_function) uae_dlsym(handle, "ppc_cpu_set_pc");
impl.run_continuous = (ppc_cpu_run_continuous_function) uae_dlsym(handle, "ppc_cpu_run_continuous");
impl.check_state = (ppc_cpu_check_state_function) uae_dlsym(handle, "ppc_cpu_check_state");
impl.set_state = (ppc_cpu_set_state_function) uae_dlsym(handle, "ppc_cpu_set_state");
impl.reset = (ppc_cpu_reset_function) uae_dlsym(handle, "ppc_cpu_reset");
+ impl.in_cpu_thread = (qemu_uae_ppc_in_cpu_thread_function) uae_dlsym(handle, "qemu_uae_ppc_in_cpu_thread");
+ impl.lock = (qemu_uae_lock_function) uae_dlsym(handle, "qemu_uae_lock");
/* Check major version (=) and minor version (>=) */
return ppc_implementation == PPC_IMPLEMENTATION_PEARPC;
}
+enum PPCLockMethod {
+ PPC_RELEASE_SPINLOCK,
+ PPC_KEEP_SPINLOCK,
+};
+
+enum PPCLockStatus {
+ PPC_NO_LOCK_NEEDED,
+ PPC_LOCKED,
+ PPC_LOCKED_WITHOUT_SPINLOCK,
+};
+
+static PPCLockStatus get_ppc_lock(PPCLockMethod method)
+{
+ if (impl.in_cpu_thread()) {
+ return PPC_NO_LOCK_NEEDED;
+ } else if (method == PPC_RELEASE_SPINLOCK) {
+
+ uae_ppc_spinlock_release();
+ impl.lock(QEMU_UAE_LOCK_ACQUIRE);
+ return PPC_LOCKED_WITHOUT_SPINLOCK;
+
+ } else if (method == PPC_KEEP_SPINLOCK) {
+
+ bool trylock_called = false;
+ while (true) {
+ if (ppc_spinlock_waiting) {
+ /* PPC CPU is waiting for the spinlock and the UAE side
+ * owns the spinlock - no additional locking needed */
+ if (trylock_called) {
+ impl.lock(QEMU_UAE_LOCK_TRYLOCK_CANCEL);
+ }
+ return PPC_NO_LOCK_NEEDED;
+ }
+ int error = impl.lock(QEMU_UAE_LOCK_TRYLOCK);
+ if (error == 0) {
+ /* Lock succeeded */
+ return PPC_LOCKED;
+ }
+ trylock_called = true;
+ }
+ } else {
+ write_log("?\n");
+ return PPC_NO_LOCK_NEEDED;
+ }
+}
+
+static void release_ppc_lock(PPCLockStatus status)
+{
+ if (status == PPC_NO_LOCK_NEEDED) {
+ return;
+ } else if (status == PPC_LOCKED_WITHOUT_SPINLOCK) {
+ impl.lock(QEMU_UAE_LOCK_RELEASE);
+ uae_ppc_spinlock_get();
+ } else if (status == PPC_LOCKED) {
+ impl.lock(QEMU_UAE_LOCK_RELEASE);
+ }
+}
+
static void initialize(void)
{
static bool initialized = false;
regions[i].alias = r->alias;
regions[i].memory = r->memory;
}
+
+ if (impl.in_cpu_thread() == false) {
+ uae_ppc_spinlock_release();
+ }
impl.map_memory(regions, map.num_regions);
+ if (impl.in_cpu_thread() == false) {
+ uae_ppc_spinlock_get();
+ }
+
for (int i = 0; i < map.num_regions; i++) {
free((void*)regions[i].name);
}
return;
}
if (using_qemu()) {
- impl.set_state(state);
- if (unlock)
+ if (impl.in_cpu_thread() == false) {
uae_ppc_spinlock_release();
- while (!impl.check_state(state)) {
- sleep_millis(1);
}
- if (unlock)
- uae_ppc_spinlock_get();
+ impl.set_state(state);
+ if (impl.in_cpu_thread() == false) {
+ uae_ppc_spinlock_get();
+ }
}
}
r.name = ua(name);
r.alias = remove ? 0xffffffff : 0;
r.memory = addr;
+
+ if (impl.in_cpu_thread() == false) {
+ /* map_memory will acquire the qemu global lock, so we must ensure
+ * the PPC CPU can finish any I/O requests and release the lock. */
+ uae_ppc_spinlock_release();
+ }
impl.map_memory(&r, -1);
+ if (impl.in_cpu_thread() == false) {
+ uae_ppc_spinlock_get();
+ }
free((void*)r.name);
}
void uae_ppc_interrupt(bool active)
{
//TRACE(_T("uae_ppc_interrupt\n"));
- if (active) {
- impl.atomic_raise_ext_exception();
- uae_ppc_wakeup();
- } else {
- impl.atomic_cancel_ext_exception();
- }
+ if (using_pearpc()) {
+ if (active) {
+ impl.atomic_raise_ext_exception();
+ uae_ppc_wakeup();
+ } else {
+ impl.atomic_cancel_ext_exception();
+ }
+ return;
+ }
+
+ PPCLockStatus status = get_ppc_lock(PPC_KEEP_SPINLOCK);
+ impl.external_interrupt(active);
+ release_ppc_lock(status);
}
// sleep until interrupt (or PPC stopped)