It's definitely possible, just that the interpreter doesn't run often enough...
This commit is contained in:
+55
-41
@@ -3,6 +3,8 @@
|
||||
#include <Scheduler.hpp>
|
||||
#include <Options.hpp>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace n64 {
|
||||
Core::Core() {
|
||||
const auto selectedCpu = Options::GetInstance().GetValue<std::string>("cpu", "type");
|
||||
@@ -28,8 +30,10 @@ void Core::Stop() {
|
||||
}
|
||||
|
||||
void Core::Reset() {
|
||||
Scheduler::GetInstance().Reset();
|
||||
regs.Reset();
|
||||
mem->Reset();
|
||||
|
||||
cpu->Reset();
|
||||
if(romLoaded)
|
||||
mem->mmio.si.pif.Execute();
|
||||
@@ -88,49 +92,59 @@ void Core::StepRSP(const u32 cpuCycles) {
|
||||
}
|
||||
}
|
||||
|
||||
void Core::VIHalflineComplete(u64 time, float volumeL, float volumeR) {
|
||||
MMIO& mmio = mem->mmio;
|
||||
VI& vi = mmio.vi;
|
||||
AI& ai = mmio.ai;
|
||||
MI& mi = mmio.mi;
|
||||
|
||||
s64 taken = time - vi.lastHalflineTime;
|
||||
vi.lastHalflineTime = time;
|
||||
|
||||
u64 overshot;
|
||||
|
||||
do {
|
||||
overshot = taken - vi.cyclesPerHalfline;
|
||||
vi.halfline++;
|
||||
|
||||
if(vi.halfline > vi.numHalflines) {
|
||||
vi.halfline = 0;
|
||||
vi.field++;
|
||||
|
||||
ai.Step(vi.missingCycles, volumeL, volumeR);
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(mtxPresentationReady);
|
||||
readyForPresentation.notify_all();
|
||||
}
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(mtxPresentationFinished);
|
||||
finishedPresentation.wait_for(lk, 1ms);
|
||||
}
|
||||
//parallel.UpdateScreen<true>();
|
||||
}
|
||||
|
||||
if(vi.field > vi.numFields)
|
||||
vi.field = 0;
|
||||
|
||||
vi.current = (vi.halfline << 1) + vi.field;
|
||||
|
||||
if ((vi.current & 0x3FE) == vi.intr) {
|
||||
mi.InterruptRaise(MI::Interrupt::VI);
|
||||
}
|
||||
|
||||
taken -= vi.cyclesPerHalfline;
|
||||
} while(taken > vi.cyclesPerHalfline);
|
||||
|
||||
u64 next = vi.cyclesPerHalfline - overshot;
|
||||
Scheduler::GetInstance().EnqueueRelative(next, VI_HALFLINE);
|
||||
}
|
||||
|
||||
void Core::Run(const float volumeL, const float volumeR) {
|
||||
MMIO &mmio = mem->mmio;
|
||||
|
||||
const u32 taken = StepCPU();
|
||||
cycles += taken;
|
||||
|
||||
bool broken = false;
|
||||
for (int field = 0; field < mmio.vi.numFields; field++) {
|
||||
u32 frameCycles = 0;
|
||||
for (int i = 0; i < mmio.vi.numHalflines; i++) {
|
||||
mmio.vi.current = (i << 1) + field;
|
||||
|
||||
if ((mmio.vi.current & 0x3FE) == mmio.vi.intr) {
|
||||
mmio.mi.InterruptRaise(MI::Interrupt::VI);
|
||||
}
|
||||
|
||||
while(cycles < mem->mmio.vi.cyclesPerHalfline) {
|
||||
const u32 taken = StepCPU();
|
||||
cycles += taken;
|
||||
|
||||
if((broken = breakpoints.contains(regs.nextPC)))
|
||||
break;
|
||||
|
||||
StepRSP(taken);
|
||||
frameCycles += taken;
|
||||
Scheduler::GetInstance().Tick(taken);
|
||||
}
|
||||
|
||||
if(broken)
|
||||
break;
|
||||
|
||||
cycles -= mmio.vi.cyclesPerHalfline;
|
||||
}
|
||||
|
||||
if(broken)
|
||||
break;
|
||||
|
||||
if ((mmio.vi.current & 0x3FE) == mmio.vi.intr) {
|
||||
mmio.mi.InterruptRaise(MI::Interrupt::VI);
|
||||
}
|
||||
|
||||
mmio.ai.Step(frameCycles, volumeL, volumeR);
|
||||
Scheduler::GetInstance().Tick(frameCycles);
|
||||
}
|
||||
|
||||
if(broken)
|
||||
pause = true;
|
||||
StepRSP(taken);
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
@@ -29,6 +29,8 @@ struct Core {
|
||||
return *GetInstance().mem;
|
||||
}
|
||||
|
||||
void VIHalflineComplete(u64 time, float, float);
|
||||
|
||||
u32 StepCPU();
|
||||
void StepRSP(u32 cpuCycles);
|
||||
void Stop();
|
||||
@@ -56,6 +58,9 @@ struct Core {
|
||||
std::unique_ptr<Mem> mem = std::make_unique<Mem>();
|
||||
std::unique_ptr<BaseCPU> cpu;
|
||||
|
||||
std::mutex mtxPresentationFinished, mtxPresentationReady;
|
||||
std::condition_variable readyForPresentation, finishedPresentation;
|
||||
|
||||
Registers regs;
|
||||
ParallelRDP parallel;
|
||||
};
|
||||
|
||||
@@ -18,15 +18,15 @@ u64 Scheduler::Remove(const EventType eventType) const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Scheduler::Tick(const u64 t) {
|
||||
void Scheduler::Tick(const u64 t, float volumeL, float volumeR) {
|
||||
n64::Mem& mem = n64::Core::GetMem();
|
||||
ticks += t;
|
||||
n64::MI &mi = mem.mmio.mi;
|
||||
n64::SI &si = mem.mmio.si;
|
||||
n64::PI &pi = mem.mmio.pi;
|
||||
|
||||
while (ticks >= events.top().time) {
|
||||
switch (const auto type = events.top().type) {
|
||||
for (auto event = events.top(); ticks >= event.time; events.pop()) {
|
||||
switch (event.type) {
|
||||
case SI_DMA:
|
||||
si.DMA();
|
||||
break;
|
||||
@@ -37,15 +37,17 @@ void Scheduler::Tick(const u64 t) {
|
||||
case PI_BUS_WRITE_COMPLETE:
|
||||
pi.ioBusy = false;
|
||||
break;
|
||||
case VI_HALFLINE:
|
||||
n64::Core::GetInstance().VIHalflineComplete(ticks, volumeL, volumeR);
|
||||
break;
|
||||
case NONE:
|
||||
break;
|
||||
case IMPOSSIBLE:
|
||||
Util::Error::GetInstance().Throw({Util::Error::Severity::UNRECOVERABLE}, {Util::Error::Type::ROM_LOAD_ERROR}, {}, {}, "Unrecognized rom endianness");
|
||||
return;
|
||||
default:
|
||||
Util::Error::GetInstance().Throw({Util::Error::Severity::UNRECOVERABLE}, {Util::Error::Type::ROM_LOAD_ERROR}, {}, {}, "Unknown scheduler event type {}", static_cast<int>(type));
|
||||
Util::Error::GetInstance().Throw({Util::Error::Severity::UNRECOVERABLE}, {Util::Error::Type::ROM_LOAD_ERROR}, {}, {}, "Unknown scheduler event type {}", static_cast<int>(event.type));
|
||||
return;
|
||||
}
|
||||
events.pop();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <log.hpp>
|
||||
#include <queue>
|
||||
|
||||
enum EventType { NONE, PI_BUS_WRITE_COMPLETE, PI_DMA_COMPLETE, SI_DMA, IMPOSSIBLE };
|
||||
enum EventType { NONE, VI_HALFLINE, COMPARE_INTERRUPT, PI_BUS_WRITE_COMPLETE, PI_DMA_COMPLETE, SI_DMA, IMPOSSIBLE };
|
||||
|
||||
struct Event {
|
||||
u64 time;
|
||||
@@ -30,6 +30,13 @@ struct IterableEvents {
|
||||
struct Scheduler {
|
||||
Scheduler() { EnqueueAbsolute(std::numeric_limits<u64>::max(), IMPOSSIBLE); }
|
||||
|
||||
void Reset() {
|
||||
ticks = 0;
|
||||
index = 0;
|
||||
events.events = {};
|
||||
EnqueueAbsolute(std::numeric_limits<u64>::max(), IMPOSSIBLE);
|
||||
}
|
||||
|
||||
static Scheduler &GetInstance() {
|
||||
static Scheduler instance;
|
||||
return instance;
|
||||
@@ -37,8 +44,8 @@ struct Scheduler {
|
||||
|
||||
void EnqueueRelative(u64, EventType);
|
||||
void EnqueueAbsolute(u64, EventType);
|
||||
[[nodiscard]] u64 Remove(EventType) const;
|
||||
void Tick(u64 t);
|
||||
u64 Remove(EventType) const;
|
||||
void Tick(u64 t, float, float);
|
||||
|
||||
u8 index = 0;
|
||||
u64 ticks = 0;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <Core.hpp>
|
||||
#include <log.hpp>
|
||||
#include <Scheduler.hpp>
|
||||
|
||||
namespace n64 {
|
||||
VI::VI() { Reset(); }
|
||||
@@ -19,7 +20,10 @@ void VI::Reset() {
|
||||
hsyncLeap = {}, burst = {}, vburst = {};
|
||||
hstart = {}, vstart = {};
|
||||
isPal = false;
|
||||
swaps = {};
|
||||
halfline = 0;
|
||||
field = 0;
|
||||
lastHalflineTime = 1000;
|
||||
Scheduler::GetInstance().EnqueueRelative(cyclesPerHalfline, VI_HALFLINE);
|
||||
}
|
||||
|
||||
u32 VI::Read(const u32 paddr) const {
|
||||
@@ -70,13 +74,7 @@ void VI::Write(const u32 paddr, const u32 val) {
|
||||
numFields = status.serrate ? 2 : 1;
|
||||
break;
|
||||
case 0x04400004:
|
||||
{
|
||||
const u32 masked = val & 0xFFFFFF;
|
||||
if (origin != masked) {
|
||||
swaps++;
|
||||
}
|
||||
origin = masked;
|
||||
}
|
||||
origin = val & 0xFFFFFF;
|
||||
break;
|
||||
case 0x04400008:
|
||||
width = val & 0x7FF;
|
||||
@@ -94,6 +92,9 @@ void VI::Write(const u32 paddr, const u32 val) {
|
||||
vsync = val & 0x3FF;
|
||||
numHalflines = vsync >> 1;
|
||||
cyclesPerHalfline = GetCyclesPerFrame(isPal) / numHalflines;
|
||||
missingCycles = GetCyclesPerFrame(isPal) % numHalflines;
|
||||
Scheduler::GetInstance().Remove(VI_HALFLINE);
|
||||
Scheduler::GetInstance().EnqueueRelative((u64)cyclesPerHalfline, VI_HALFLINE);
|
||||
break;
|
||||
case 0x0440001C:
|
||||
hsync = val & 0x3FF;
|
||||
|
||||
@@ -82,9 +82,12 @@ struct VI {
|
||||
u32 origin{}, width{}, current{};
|
||||
u32 vsync{}, hsync{}, intr{};
|
||||
AxisStart hstart{}, vstart{};
|
||||
int swaps{};
|
||||
int halfline{};
|
||||
int numHalflines{};
|
||||
int numFields{};
|
||||
int cyclesPerHalfline{};
|
||||
int field{};
|
||||
int missingCycles{};
|
||||
u64 lastHalflineTime;
|
||||
};
|
||||
} // namespace n64
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <KaizenGui.hpp>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <Scheduler.hpp>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
@@ -10,7 +11,8 @@ EmuThread::EmuThread(double &fps, SettingsWindow &settings) noexcept : settings(
|
||||
|
||||
void EmuThread::run() noexcept {
|
||||
n64::Core& core = n64::Core::GetInstance();
|
||||
rdramCopy = core.GetMem().GetRDRAM();
|
||||
if(!core.romLoaded) return;
|
||||
if(core.pause) return;
|
||||
|
||||
while(!shouldExit) {
|
||||
if(!core.romLoaded) {
|
||||
@@ -23,12 +25,10 @@ void EmuThread::run() noexcept {
|
||||
continue;
|
||||
}
|
||||
|
||||
core.Run(settings.getVolumeL(), settings.getVolumeR());
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(presentMutex);
|
||||
memcpy(rdramCopy.data(), core.GetMem().GetRDRAMPtr(), RDRAM_SIZE);
|
||||
}
|
||||
readyForPresentation.notify_all();
|
||||
const auto volumeL = settings.getVolumeL(), volumeR = settings.getVolumeR();
|
||||
core.Run(volumeL, volumeR);
|
||||
core.mem->mmio.ai.Step(core.cycles, volumeL, volumeR);
|
||||
Scheduler::GetInstance().Tick(core.cycles, volumeL, volumeR);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,9 +43,7 @@ void EmuThread::TogglePause() const noexcept {
|
||||
}
|
||||
|
||||
void EmuThread::Reset() noexcept {
|
||||
n64::Core& core = n64::Core::GetInstance();
|
||||
core.Reset();
|
||||
rdramCopy = core.GetMem().GetRDRAM();
|
||||
n64::Core::GetInstance().Reset();
|
||||
}
|
||||
|
||||
void EmuThread::Stop() noexcept {
|
||||
|
||||
@@ -23,7 +23,4 @@ public:
|
||||
SettingsWindow &settings;
|
||||
double& fps;
|
||||
std::atomic_bool shouldExit = false;
|
||||
std::mutex presentMutex;
|
||||
std::condition_variable readyForPresentation;
|
||||
std::vector<u8> rdramCopy;
|
||||
};
|
||||
|
||||
@@ -10,7 +10,7 @@ using namespace std::chrono_literals;
|
||||
|
||||
KaizenGui::KaizenGui() noexcept :
|
||||
window("Kaizen " KAIZEN_VERSION_STR, 1280, 720), settingsWindow(window), emuThread(fpsCounter, settingsWindow),
|
||||
vulkanWidget(window.getHandle(), emuThread.rdramCopy.data()) {
|
||||
vulkanWidget(window.getHandle(), n64::Core::GetMem().GetRDRAMPtr()) {
|
||||
gui::Initialize(n64::Core::GetInstance().parallel.wsi, window.getHandle());
|
||||
SDL_InitSubSystem(SDL_INIT_GAMEPAD);
|
||||
|
||||
@@ -440,16 +440,21 @@ void KaizenGui::RenderUI() {
|
||||
if (minimized)
|
||||
return;
|
||||
|
||||
if (core.romLoaded) {
|
||||
std::unique_lock<std::mutex> lk(emuThread.presentMutex);
|
||||
if (emuThread.readyForPresentation.wait_for(lk, 16.6667ms) == std::cv_status::no_timeout)
|
||||
core.parallel.UpdateScreen<true>();
|
||||
else
|
||||
core.parallel.UpdateScreen<false>();
|
||||
|
||||
if(!core.romLoaded) {
|
||||
core.parallel.UpdateScreen<false>();
|
||||
return;
|
||||
}
|
||||
|
||||
core.parallel.UpdateScreen<false>();
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(core.mtxPresentationReady);
|
||||
core.readyForPresentation.wait_for(lk, 16.66667ms);
|
||||
}
|
||||
core.parallel.UpdateScreen<true>();
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(core.mtxPresentationFinished);
|
||||
core.finishedPresentation.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
void KaizenGui::LoadROM(const std::string &path) noexcept {
|
||||
@@ -461,6 +466,7 @@ void KaizenGui::LoadROM(const std::string &path) noexcept {
|
||||
}
|
||||
|
||||
void KaizenGui::run() {
|
||||
n64::Core& core = n64::Core::GetInstance();
|
||||
while (!quit) {
|
||||
SDL_Event e;
|
||||
while (SDL_PollEvent(&e)) {
|
||||
@@ -483,7 +489,7 @@ void KaizenGui::run() {
|
||||
HandleInput(e);
|
||||
}
|
||||
|
||||
SDL_GetWindowSize(window.getHandle(), &width, &height);
|
||||
SDL_GetWindowSize(window.getHandle(), &width, &height);
|
||||
RenderUI();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user