#include #include #include #include namespace n64 { Core::Core() : interpreter(*mem, regs) #ifdef KAIZEN_JIT_ENABLED , jit(*mem, regs) #endif { const auto selectedCpu = Options::GetInstance().GetValue("cpu", "type"); if (selectedCpu == "interpreter") { cpuType = Interpreted; } else if (selectedCpu == "jit") { cpuType = DynamicRecompiler; } else if (selectedCpu == "cached_interpreter") { cpuType = CachedInterpreter; } else { panic("Unimplemented CPU type"); } } void Core::Stop() { pause = true; romLoaded = false; Reset(); } void Core::Reset() { regs.Reset(); mem->Reset(); interpreter.Reset(); if (romLoaded) mem->mmio.si.pif.Execute(); } void Core::LoadTAS(const fs::path &path) const { mem->mmio.si.pif.movie.Load(path); } void Core::LoadROM(const std::string &rom_) { Stop(); rom = rom_; std::string archive_types[] = {".zip", ".7z", ".rar", ".tar"}; auto extension = fs::path(rom).extension().string(); const bool isArchive = std::ranges::any_of(archive_types, [&extension](const auto &e) { return e == extension; }); mem->LoadROM(isArchive, rom); GameDB::match(); if (mem->rom.gameNameDB.empty()) { mem->rom.gameNameDB = fs::path(rom).stem().string(); } mem->mmio.vi.isPal = mem->IsROMPAL(); mem->mmio.si.pif.InitDevices(mem->saveType); mem->mmio.si.pif.mempakPath = rom; mem->mmio.si.pif.LoadEeprom(mem->saveType, rom); mem->flash.Load(mem->saveType, rom); mem->LoadSRAM(mem->saveType, rom); mem->mmio.si.pif.Execute(); pause = false; romLoaded = true; } u32 Core::StepCPU() { if (cpuType == Interpreted) { auto taken = interpreter.Step() + regs.PopStalledCycles(); StepRSP(taken); return taken; } if (cpuType == CachedInterpreter) return interpreter.ExecuteCached() + regs.PopStalledCycles(); #ifdef KAIZEN_JIT_ENABLED if (cpuType == DynamicRecompiler) return jit.Step() + regs.PopStalledCycles(); #endif panic("Invalid CPU type?"); } void Core::StepRSP(const u32 cpuCycles) { MMIO &mmio = mem->mmio; if (mmio.rsp.spStatus.halt) { regs.steps = 0; mmio.rsp.steps = 0; return; } static constexpr u32 cpuRatio = 3, rspRatio = 2; regs.steps += cpuCycles; const auto sets = regs.steps / cpuRatio; mmio.rsp.steps += sets * rspRatio; regs.steps -= sets * cpuRatio; while (mmio.rsp.steps > 0) { mmio.rsp.steps--; mmio.rsp.Step(); } } void Core::Run(const float volumeL, const float volumeR) { MMIO &mmio = mem->mmio; bool broken = false; for (int field = 0; field < mmio.vi.numFields; field++) { Scheduler::GetInstance().HandleEvents(); u32 frameCycles = 0; for (int halfline = 0; halfline < mmio.vi.numHalflines; halfline++) { mmio.vi.current = (halfline << 1) + field; if ((mmio.vi.current & 0x3FE) == mmio.vi.intr) { mmio.mi.InterruptRaise(MI::Interrupt::VI); } for (int cycles = 0; cycles < mem->mmio.vi.cyclesPerHalfline;) { Scheduler::GetInstance().HandleEvents(); const u32 taken = StepCPU(); cycles += taken; frameCycles += taken; Scheduler::GetInstance().Tick(taken); } } if ((mmio.vi.current & 0x3FE) == mmio.vi.intr) { mmio.mi.InterruptRaise(MI::Interrupt::VI); } mmio.ai.Step(frameCycles, volumeL, volumeR); Scheduler::GetInstance().Tick(frameCycles); } } } // namespace n64