mirror of
https://github.com/open-goal/jak-project
synced 2026-05-25 07:23:19 -04:00
139 lines
3.3 KiB
C++
139 lines
3.3 KiB
C++
/*!
|
|
* @file vm.cpp
|
|
* Base "PS2 virtual machine" code.
|
|
* Simulates the existence of select PS2 components, for inspection & debugging.
|
|
* Not an emulator!
|
|
*/
|
|
|
|
#include "vm.h"
|
|
#include "dmac.h"
|
|
#include "common/log/log.h"
|
|
#include "game/kernel/kscheme.h"
|
|
#include <condition_variable>
|
|
#include <mutex>
|
|
|
|
namespace VM {
|
|
|
|
bool use = true; // enable VM by default, since we're debugging right now
|
|
|
|
namespace {
|
|
Status status;
|
|
std::condition_variable vm_init_cv;
|
|
std::condition_variable vm_dead_cv;
|
|
std::mutex status_mutex;
|
|
|
|
int components = 0;
|
|
} // namespace
|
|
|
|
static void vm_change_status(Status new_status) {
|
|
std::unique_lock<std::mutex> lk(status_mutex);
|
|
status = new_status;
|
|
}
|
|
|
|
void wait_vm_init() {
|
|
std::unique_lock<std::mutex> lk(status_mutex);
|
|
|
|
vm_init_cv.wait(lk, [&] { return status == Status::Inited; });
|
|
}
|
|
|
|
void wait_vm_dead() {
|
|
if (status != Status::Kill && status != Status::Dead) {
|
|
lg::warn("[VM] Dying without being killed! There are {} component(s) running", components);
|
|
}
|
|
|
|
std::unique_lock<std::mutex> lk(status_mutex);
|
|
|
|
vm_dead_cv.wait(lk, [&] { return components == 0; });
|
|
}
|
|
|
|
bool vm_want_exit() {
|
|
return status == Status::Kill || status == Status::Dead;
|
|
}
|
|
|
|
void vm_prepare() {
|
|
lg::debug("[VM] Preparing...");
|
|
vm_change_status(Status::Uninited);
|
|
lg::debug("[VM] Prepared");
|
|
}
|
|
|
|
void vm_init() {
|
|
if (status != Status::Uninited) {
|
|
lg::warn("[VM] unexpected status {}", status);
|
|
}
|
|
|
|
lg::debug("[VM] Inited");
|
|
vm_change_status(Status::Inited);
|
|
vm_init_cv.notify_all();
|
|
}
|
|
|
|
void vm_kill() {
|
|
lg::debug("[VM] Killing");
|
|
|
|
vm_change_status(Status::Kill);
|
|
|
|
// stall caller until VM is done dying
|
|
wait_vm_dead();
|
|
|
|
vm_change_status(Status::Dead);
|
|
}
|
|
|
|
void subscribe_component() {
|
|
if (status == Status::Dead) {
|
|
throw std::runtime_error("[VM] Cannot add new components when VM is dead!");
|
|
}
|
|
|
|
status_mutex.lock();
|
|
++components;
|
|
status_mutex.unlock();
|
|
|
|
// stall component until VM is ready
|
|
if (status == Status::Uninited) {
|
|
wait_vm_init();
|
|
}
|
|
}
|
|
|
|
void unsubscribe_component() {
|
|
status_mutex.lock();
|
|
--components;
|
|
status_mutex.unlock();
|
|
vm_dead_cv.notify_all();
|
|
}
|
|
|
|
/*!
|
|
* Return the GOAL pointer to a specified PS2 VM component based on the EE address.
|
|
*/
|
|
u64 get_vm_ptr(u32 ptr) {
|
|
// currently, only DMAC and DMA channel banks are implemented. add more as necessary.
|
|
if (ptr == 0x10008000) {
|
|
return VM::dmac_ch[0].offset;
|
|
} else if (ptr == 0x10009000) {
|
|
return VM::dmac_ch[1].offset;
|
|
} else if (ptr == 0x1000a000) {
|
|
return VM::dmac_ch[2].offset;
|
|
} else if (ptr == 0x1000b000) {
|
|
return VM::dmac_ch[3].offset;
|
|
} else if (ptr == 0x1000b400) {
|
|
return VM::dmac_ch[4].offset;
|
|
} else if (ptr == 0x1000c000) {
|
|
return VM::dmac_ch[5].offset;
|
|
} else if (ptr == 0x1000c400) {
|
|
return VM::dmac_ch[6].offset;
|
|
} else if (ptr == 0x1000c800) {
|
|
return VM::dmac_ch[7].offset;
|
|
} else if (ptr == 0x1000d000) {
|
|
return VM::dmac_ch[8].offset;
|
|
} else if (ptr == 0x1000d400) {
|
|
return VM::dmac_ch[9].offset;
|
|
} else if (ptr == 0x1000e000) {
|
|
return VM::dmac.offset;
|
|
} else {
|
|
// return zero, using this result will segfault GOAL!
|
|
// we could die immediately, but it might be worth it to keep going just on the off chance more
|
|
// errors are reported, and not just only this one.
|
|
lg::error("unknown EE register for VM at #x{:08x}", ptr);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
} // namespace VM
|