/*! * @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 #include 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 lk(status_mutex); status = new_status; } void wait_vm_init() { std::unique_lock 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 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