i cannot figure it out istg
This commit is contained in:
@@ -116,6 +116,7 @@ void Core::Run(const float volumeL, const float volumeR) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (int cycles = 0; cycles < mem->mmio.vi.cyclesPerHalfline;) {
|
for (int cycles = 0; cycles < mem->mmio.vi.cyclesPerHalfline;) {
|
||||||
|
Scheduler::GetInstance().HandleEvents();
|
||||||
const u32 taken = StepCPU();
|
const u32 taken = StepCPU();
|
||||||
cycles += taken;
|
cycles += taken;
|
||||||
frameCycles += taken;
|
frameCycles += taken;
|
||||||
|
|||||||
@@ -96,65 +96,63 @@ u32 Interpreter::Step() {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 DivideAddr(u32 addr, u32 &offset) {
|
const CachedLine &CachedState::GetLine(u64 addr) {
|
||||||
offset = (addr & (MAX_LINES_PER_BLOCK - 1)) / 4;
|
|
||||||
return addr / MAX_LINES_PER_BLOCK;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<CachedLine> CachedState::GetLine(u64 addr) {
|
|
||||||
u32 offset;
|
u32 offset;
|
||||||
u32 page = DivideAddr(addr, offset);
|
u32 page = DivideAddr(addr, offset);
|
||||||
if (blocks[page])
|
return blocks[page].lines[offset];
|
||||||
return blocks[page]->lines[offset];
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CachedState::InsertLine(u64 addr, std::shared_ptr<CachedLine> line) {
|
void CachedState::InsertLine(u64 addr, const CachedLine &line) {
|
||||||
u32 offset;
|
u32 offset;
|
||||||
u32 page = DivideAddr(addr, offset);
|
u32 page = DivideAddr(addr, offset);
|
||||||
|
|
||||||
if (!blocks[page])
|
blocks[page].lines[offset] = line;
|
||||||
blocks[page] = std::make_unique<CachedBlock>();
|
|
||||||
|
|
||||||
blocks[page]->lines[offset] = line;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CachedState::EvictLine(u64 addr) {
|
void CachedState::EvictLine(u64 addr) {
|
||||||
u32 offset;
|
u32 offset;
|
||||||
u32 page = DivideAddr(addr, offset);
|
u32 page = DivideAddr(addr, offset);
|
||||||
|
|
||||||
if (blocks[page]) {
|
blocks[page].lines[offset] = CachedLine();
|
||||||
blocks[page]->lines[offset].reset();
|
|
||||||
blocks[page].reset();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 Interpreter::ExecuteCached() {
|
u32 Interpreter::ExecuteCached() {
|
||||||
auto addr = regs.pc;
|
auto addr = regs.pc;
|
||||||
auto blockAddr = addr;
|
auto blockAddr = addr;
|
||||||
auto line = cachedState.GetLine(addr);
|
auto page = (addr >> 12) & 0xfffff;
|
||||||
|
auto offset = addr & 0xfff;
|
||||||
|
auto &blocks = cachedState.blocks;
|
||||||
|
|
||||||
if (line) {
|
if (page >= blocks.size())
|
||||||
for (u32 i = 0; i < line->len; i++) {
|
blocks.push_back();
|
||||||
if (!MaybeAdvance())
|
|
||||||
return i + 1;
|
|
||||||
|
|
||||||
Instruction instr = line->code[i];
|
if (page < blocks.size()) {
|
||||||
DecodeExecute(instr);
|
const auto &block = blocks[page];
|
||||||
|
if (blocks[page].len > 0) {
|
||||||
|
for (u32 i = 0; i < block.len; i++) {
|
||||||
|
if (!MaybeAdvance())
|
||||||
|
return i + 1;
|
||||||
|
|
||||||
// Branch likely with false condition, it wasn't taken so don't execute the delay slot
|
Instruction instr = block.code[i];
|
||||||
if (IsBranchLikely(instr) && !regs.delaySlot)
|
DecodeExecute(instr);
|
||||||
break;
|
|
||||||
|
// Branch likely with false condition, it wasn't taken so don't execute the delay slot
|
||||||
|
if (IsBranchLikely(instr) && !regs.delaySlot)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (block.cycles == 0)
|
||||||
|
Scheduler::GetInstance().SkipToNext();
|
||||||
|
|
||||||
|
return block.cycles;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (line->cycles == 0)
|
|
||||||
Scheduler::GetInstance().SkipToNext();
|
|
||||||
|
|
||||||
return line->cycles;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::array<Instruction, MAX_INSTRUCTION_PER_LINE> code;
|
if (line.code.size() > 0) {
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Instruction> code;
|
||||||
|
code.resize(MAX_INSTRUCTION_PER_LINE);
|
||||||
|
|
||||||
u32 i;
|
u32 i;
|
||||||
bool fetchDelaySlot = false;
|
bool fetchDelaySlot = false;
|
||||||
@@ -180,7 +178,7 @@ u32 Interpreter::ExecuteCached() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cachedState.InsertLine(blockAddr, std::make_shared<CachedLine>(code, i, i));
|
cachedState.InsertLine(blockAddr, {code, i, i});
|
||||||
|
|
||||||
return ExecuteCached();
|
return ExecuteCached();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,36 +5,26 @@
|
|||||||
namespace n64 {
|
namespace n64 {
|
||||||
struct Core;
|
struct Core;
|
||||||
|
|
||||||
static constexpr u32 MAX_LINES_PER_BLOCK = 1 << 12;
|
static constexpr u32 MAX_INSTRUCTION_PER_BLOCK = 1 << 12;
|
||||||
static constexpr u32 MAX_INSTRUCTION_PER_LINE = MAX_LINES_PER_BLOCK >> 2;
|
|
||||||
|
|
||||||
struct CachedLine {
|
struct CachedBlock {
|
||||||
std::array<Instruction, MAX_INSTRUCTION_PER_LINE> code = {};
|
std::array<Instruction, MAX_INSTRUCTION_PER_BLOCK> code = {};
|
||||||
u32 cycles = 0;
|
u32 cycles = 0;
|
||||||
u32 len = 0;
|
u32 len = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CachedBlock {
|
|
||||||
std::array<std::shared_ptr<CachedLine>, MAX_LINES_PER_BLOCK / 4> lines = {};
|
|
||||||
};
|
|
||||||
|
|
||||||
using CachedBlocks = std::vector<std::unique_ptr<CachedBlock>>;
|
|
||||||
|
|
||||||
struct CachedState {
|
struct CachedState {
|
||||||
CachedState() { blocks.resize((u64(std::numeric_limits<u32>::max()) + 1) / MAX_LINES_PER_BLOCK); }
|
CachedState() {}
|
||||||
CachedBlocks blocks = {};
|
std::vector<CachedBlock> blocks = {};
|
||||||
bool exception = false;
|
bool exception = false;
|
||||||
|
|
||||||
void Reset() {
|
void Reset() {
|
||||||
for (auto &block : blocks) {
|
blocks = {};
|
||||||
block.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
exception = false;
|
exception = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<CachedLine> GetLine(u64);
|
const CachedLine &GetLine(u64);
|
||||||
void InsertLine(u64, std::shared_ptr<CachedLine>);
|
void InsertLine(u64, const CachedLine &);
|
||||||
void EvictLine(u64);
|
void EvictLine(u64);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -51,7 +41,6 @@ struct Interpreter final {
|
|||||||
cachedState.Reset();
|
cachedState.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
CachedState cachedState;
|
CachedState cachedState;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
+418
-390
@@ -5,456 +5,484 @@
|
|||||||
#include <ImGuiImpl/StatusBar.hpp>
|
#include <ImGuiImpl/StatusBar.hpp>
|
||||||
#include <resources/gamecontrollerdb.h>
|
#include <resources/gamecontrollerdb.h>
|
||||||
|
|
||||||
KaizenGui::KaizenGui() noexcept : window("Kaizen " KAIZEN_VERSION_STR, 1280, 720), settingsWindow(window), vulkanWidget(window.getHandle()), emuThread(fpsCounter, settingsWindow) {
|
KaizenGui::KaizenGui() noexcept :
|
||||||
gui::Initialize(n64::Core::GetInstance().parallel.wsi, window.getHandle());
|
window("Kaizen " KAIZEN_VERSION_STR, 1280, 720), settingsWindow(window), vulkanWidget(window.getHandle()),
|
||||||
SDL_InitSubSystem(SDL_INIT_GAMEPAD);
|
emuThread(fpsCounter, settingsWindow) {
|
||||||
|
gui::Initialize(n64::Core::GetInstance().parallel.wsi, window.getHandle());
|
||||||
|
SDL_InitSubSystem(SDL_INIT_GAMEPAD);
|
||||||
|
|
||||||
SDL_AddGamepadMapping(gamecontrollerdb_str);
|
SDL_AddGamepadMapping(gamecontrollerdb_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
KaizenGui::~KaizenGui() {
|
KaizenGui::~KaizenGui() {
|
||||||
gui::Cleanup();
|
gui::Cleanup();
|
||||||
SDL_Quit();
|
SDL_Quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
void KaizenGui::QueryDevices(const SDL_Event &event) {
|
void KaizenGui::QueryDevices(const SDL_Event &event) {
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case SDL_EVENT_GAMEPAD_ADDED:
|
case SDL_EVENT_GAMEPAD_ADDED:
|
||||||
if (!gamepad) {
|
if (!gamepad) {
|
||||||
const auto index = event.gdevice.which;
|
const auto index = event.gdevice.which;
|
||||||
|
|
||||||
gamepad = SDL_OpenGamepad(index);
|
gamepad = SDL_OpenGamepad(index);
|
||||||
info("Found controller!");
|
info("Found controller!");
|
||||||
info("Name: {}", SDL_GetGamepadName(gamepad));
|
info("Name: {}", SDL_GetGamepadName(gamepad));
|
||||||
info("Vendor: {}", SDL_GetGamepadVendor(gamepad));
|
info("Vendor: {}", SDL_GetGamepadVendor(gamepad));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SDL_EVENT_GAMEPAD_REMOVED:
|
||||||
|
if (gamepad)
|
||||||
|
SDL_CloseGamepad(gamepad);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
case SDL_EVENT_GAMEPAD_REMOVED:
|
|
||||||
if (gamepad)
|
|
||||||
SDL_CloseGamepad(gamepad);
|
|
||||||
break;
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void KaizenGui::HandleInput(const SDL_Event &event) {
|
void KaizenGui::HandleInput(const SDL_Event &event) {
|
||||||
const n64::Core& core = n64::Core::GetInstance();
|
const n64::Core &core = n64::Core::GetInstance();
|
||||||
n64::PIF &pif = n64::Core::GetMem().mmio.si.pif;
|
n64::PIF &pif = n64::Core::GetMem().mmio.si.pif;
|
||||||
switch(event.type) {
|
switch (event.type) {
|
||||||
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
|
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
|
||||||
if(!gamepad)
|
if (!gamepad)
|
||||||
|
break;
|
||||||
|
{
|
||||||
|
pif.UpdateButton(0, n64::Controller::Key::Z,
|
||||||
|
SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER) == SDL_JOYSTICK_AXIS_MAX);
|
||||||
|
pif.UpdateButton(0, n64::Controller::Key::CUp,
|
||||||
|
SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) <= -127);
|
||||||
|
pif.UpdateButton(0, n64::Controller::Key::CDown,
|
||||||
|
SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) >= 127);
|
||||||
|
pif.UpdateButton(0, n64::Controller::Key::CLeft,
|
||||||
|
SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) <= -127);
|
||||||
|
pif.UpdateButton(0, n64::Controller::Key::CRight,
|
||||||
|
SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) >= 127);
|
||||||
|
|
||||||
|
float xclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX);
|
||||||
|
if (xclamped < 0) {
|
||||||
|
xclamped /= static_cast<float>(std::abs(SDL_JOYSTICK_AXIS_MAX));
|
||||||
|
} else {
|
||||||
|
xclamped /= SDL_JOYSTICK_AXIS_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
xclamped *= 86;
|
||||||
|
|
||||||
|
float yclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTY);
|
||||||
|
if (yclamped < 0) {
|
||||||
|
yclamped /= static_cast<float>(std::abs(SDL_JOYSTICK_AXIS_MIN));
|
||||||
|
} else {
|
||||||
|
yclamped /= SDL_JOYSTICK_AXIS_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
yclamped *= 86;
|
||||||
|
|
||||||
|
pif.UpdateAxis(0, n64::Controller::Axis::Y, static_cast<s8>(-yclamped));
|
||||||
|
pif.UpdateAxis(0, n64::Controller::Axis::X, static_cast<s8>(xclamped));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
{
|
|
||||||
pif.UpdateButton(0, n64::Controller::Key::Z, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER) == SDL_JOYSTICK_AXIS_MAX);
|
|
||||||
pif.UpdateButton(0, n64::Controller::Key::CUp, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) <= -127);
|
|
||||||
pif.UpdateButton(0, n64::Controller::Key::CDown, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) >= 127);
|
|
||||||
pif.UpdateButton(0, n64::Controller::Key::CLeft, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) <= -127);
|
|
||||||
pif.UpdateButton(0, n64::Controller::Key::CRight, SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) >= 127);
|
|
||||||
|
|
||||||
float xclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX);
|
|
||||||
if (xclamped < 0) {
|
|
||||||
xclamped /= static_cast<float>(std::abs(SDL_JOYSTICK_AXIS_MAX));
|
|
||||||
} else {
|
|
||||||
xclamped /= SDL_JOYSTICK_AXIS_MAX;
|
|
||||||
}
|
|
||||||
|
|
||||||
xclamped *= 86;
|
|
||||||
|
|
||||||
float yclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTY);
|
|
||||||
if (yclamped < 0) {
|
|
||||||
yclamped /= static_cast<float>(std::abs(SDL_JOYSTICK_AXIS_MIN));
|
|
||||||
} else {
|
|
||||||
yclamped /= SDL_JOYSTICK_AXIS_MAX;
|
|
||||||
}
|
|
||||||
|
|
||||||
yclamped *= 86;
|
|
||||||
|
|
||||||
pif.UpdateAxis(0, n64::Controller::Axis::Y, static_cast<s8>(-yclamped));
|
|
||||||
pif.UpdateAxis(0, n64::Controller::Axis::X, static_cast<s8>( xclamped));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
|
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
|
||||||
case SDL_EVENT_GAMEPAD_BUTTON_UP:
|
case SDL_EVENT_GAMEPAD_BUTTON_UP:
|
||||||
if(!gamepad)
|
if (!gamepad)
|
||||||
|
break;
|
||||||
|
|
||||||
|
pif.UpdateButton(0, n64::Controller::Key::A, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_SOUTH));
|
||||||
|
pif.UpdateButton(0, n64::Controller::Key::B, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_WEST));
|
||||||
|
pif.UpdateButton(0, n64::Controller::Key::Start, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_START));
|
||||||
|
pif.UpdateButton(0, n64::Controller::Key::DUp, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_UP));
|
||||||
|
pif.UpdateButton(0, n64::Controller::Key::DDown, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_DOWN));
|
||||||
|
pif.UpdateButton(0, n64::Controller::Key::DLeft, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_LEFT));
|
||||||
|
pif.UpdateButton(0, n64::Controller::Key::DRight, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_RIGHT));
|
||||||
|
pif.UpdateButton(0, n64::Controller::Key::LT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER));
|
||||||
|
pif.UpdateButton(0, n64::Controller::Key::RT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
pif.UpdateButton(0, n64::Controller::Key::A, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_SOUTH));
|
|
||||||
pif.UpdateButton(0, n64::Controller::Key::B, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_WEST));
|
|
||||||
pif.UpdateButton(0, n64::Controller::Key::Start, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_START));
|
|
||||||
pif.UpdateButton(0, n64::Controller::Key::DUp, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_UP));
|
|
||||||
pif.UpdateButton(0, n64::Controller::Key::DDown, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_DOWN));
|
|
||||||
pif.UpdateButton(0, n64::Controller::Key::DLeft, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_LEFT));
|
|
||||||
pif.UpdateButton(0, n64::Controller::Key::DRight, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_RIGHT));
|
|
||||||
pif.UpdateButton(0, n64::Controller::Key::LT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER));
|
|
||||||
pif.UpdateButton(0, n64::Controller::Key::RT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER));
|
|
||||||
break;
|
|
||||||
case SDL_EVENT_KEY_DOWN:
|
case SDL_EVENT_KEY_DOWN:
|
||||||
case SDL_EVENT_KEY_UP:
|
case SDL_EVENT_KEY_UP:
|
||||||
{
|
{
|
||||||
const auto keys = SDL_GetKeyboardState(nullptr);
|
const auto keys = SDL_GetKeyboardState(nullptr);
|
||||||
if((keys[SDL_SCANCODE_LCTRL] || keys[SDL_SCANCODE_RCTRL]) && keys[SDL_SCANCODE_O]) {
|
if ((keys[SDL_SCANCODE_LCTRL] || keys[SDL_SCANCODE_RCTRL]) && keys[SDL_SCANCODE_O]) {
|
||||||
fileDialogOpen = true;
|
fileDialogOpen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fastForward = keys[SDL_SCANCODE_SPACE];
|
||||||
|
if (!unlockFramerate)
|
||||||
|
core.parallel.SetFramerateUnlocked(fastForward);
|
||||||
|
|
||||||
|
if (core.romLoaded) {
|
||||||
|
if (keys[SDL_SCANCODE_P]) {
|
||||||
|
emuThread.TogglePause();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keys[SDL_SCANCODE_R]) {
|
||||||
|
emuThread.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keys[SDL_SCANCODE_Q]) {
|
||||||
|
emuThread.Stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gamepad)
|
||||||
|
break;
|
||||||
|
|
||||||
|
pif.UpdateButton(0, n64::Controller::Key::Z, keys[SDL_SCANCODE_Z]);
|
||||||
|
pif.UpdateButton(0, n64::Controller::Key::CUp, keys[SDL_SCANCODE_HOME]);
|
||||||
|
pif.UpdateButton(0, n64::Controller::Key::CDown, keys[SDL_SCANCODE_END]);
|
||||||
|
pif.UpdateButton(0, n64::Controller::Key::CLeft, keys[SDL_SCANCODE_DELETE]);
|
||||||
|
pif.UpdateButton(0, n64::Controller::Key::CRight, keys[SDL_SCANCODE_PAGEDOWN]);
|
||||||
|
pif.UpdateButton(0, n64::Controller::Key::A, keys[SDL_SCANCODE_X]);
|
||||||
|
pif.UpdateButton(0, n64::Controller::Key::B, keys[SDL_SCANCODE_C]);
|
||||||
|
pif.UpdateButton(0, n64::Controller::Key::Start, keys[SDL_SCANCODE_RETURN]);
|
||||||
|
pif.UpdateButton(0, n64::Controller::Key::DUp, keys[SDL_SCANCODE_I]);
|
||||||
|
pif.UpdateButton(0, n64::Controller::Key::DDown, keys[SDL_SCANCODE_K]);
|
||||||
|
pif.UpdateButton(0, n64::Controller::Key::DLeft, keys[SDL_SCANCODE_J]);
|
||||||
|
pif.UpdateButton(0, n64::Controller::Key::DRight, keys[SDL_SCANCODE_L]);
|
||||||
|
pif.UpdateButton(0, n64::Controller::Key::LT, keys[SDL_SCANCODE_A]);
|
||||||
|
pif.UpdateButton(0, n64::Controller::Key::RT, keys[SDL_SCANCODE_S]);
|
||||||
|
|
||||||
|
float x = 0, y = 0;
|
||||||
|
|
||||||
|
if (keys[SDL_SCANCODE_UP])
|
||||||
|
y = 86;
|
||||||
|
if (keys[SDL_SCANCODE_DOWN])
|
||||||
|
y = -86;
|
||||||
|
if (keys[SDL_SCANCODE_LEFT])
|
||||||
|
x = -86;
|
||||||
|
if (keys[SDL_SCANCODE_RIGHT])
|
||||||
|
x = 86;
|
||||||
|
|
||||||
|
pif.UpdateAxis(0, n64::Controller::Axis::X, x);
|
||||||
|
pif.UpdateAxis(0, n64::Controller::Axis::Y, y);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
fastForward = keys[SDL_SCANCODE_SPACE];
|
default:
|
||||||
if(!unlockFramerate)
|
break;
|
||||||
core.parallel.SetFramerateUnlocked(fastForward);
|
}
|
||||||
|
|
||||||
if(core.romLoaded) {
|
|
||||||
if(keys[SDL_SCANCODE_P]) {
|
|
||||||
emuThread.TogglePause();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(keys[SDL_SCANCODE_R]) {
|
|
||||||
emuThread.Reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(keys[SDL_SCANCODE_Q]) {
|
|
||||||
emuThread.Stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(gamepad)
|
|
||||||
break;
|
|
||||||
|
|
||||||
pif.UpdateButton(0, n64::Controller::Key::Z, keys[SDL_SCANCODE_Z]);
|
|
||||||
pif.UpdateButton(0, n64::Controller::Key::CUp, keys[SDL_SCANCODE_HOME]);
|
|
||||||
pif.UpdateButton(0, n64::Controller::Key::CDown, keys[SDL_SCANCODE_END]);
|
|
||||||
pif.UpdateButton(0, n64::Controller::Key::CLeft, keys[SDL_SCANCODE_DELETE]);
|
|
||||||
pif.UpdateButton(0, n64::Controller::Key::CRight, keys[SDL_SCANCODE_PAGEDOWN]);
|
|
||||||
pif.UpdateButton(0, n64::Controller::Key::A, keys[SDL_SCANCODE_X]);
|
|
||||||
pif.UpdateButton(0, n64::Controller::Key::B, keys[SDL_SCANCODE_C]);
|
|
||||||
pif.UpdateButton(0, n64::Controller::Key::Start, keys[SDL_SCANCODE_RETURN]);
|
|
||||||
pif.UpdateButton(0, n64::Controller::Key::DUp, keys[SDL_SCANCODE_I]);
|
|
||||||
pif.UpdateButton(0, n64::Controller::Key::DDown, keys[SDL_SCANCODE_K]);
|
|
||||||
pif.UpdateButton(0, n64::Controller::Key::DLeft, keys[SDL_SCANCODE_J]);
|
|
||||||
pif.UpdateButton(0, n64::Controller::Key::DRight, keys[SDL_SCANCODE_L]);
|
|
||||||
pif.UpdateButton(0, n64::Controller::Key::LT, keys[SDL_SCANCODE_A]);
|
|
||||||
pif.UpdateButton(0, n64::Controller::Key::RT, keys[SDL_SCANCODE_S]);
|
|
||||||
|
|
||||||
float x = 0, y = 0;
|
|
||||||
|
|
||||||
if (keys[SDL_SCANCODE_UP]) y = 86;
|
|
||||||
if (keys[SDL_SCANCODE_DOWN]) y = -86;
|
|
||||||
if (keys[SDL_SCANCODE_LEFT]) x = -86;
|
|
||||||
if (keys[SDL_SCANCODE_RIGHT]) x = 86;
|
|
||||||
|
|
||||||
pif.UpdateAxis(0, n64::Controller::Axis::X, x);
|
|
||||||
pif.UpdateAxis(0, n64::Controller::Axis::Y, y);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<std::optional<s64>, std::optional<Util::Error::MemoryAccess>> RenderErrorMessageDetails() {
|
std::pair<std::optional<s64>, std::optional<Util::Error::MemoryAccess>> RenderErrorMessageDetails() {
|
||||||
auto lastPC = Util::Error::GetLastPC();
|
auto lastPC = Util::Error::GetLastPC();
|
||||||
if(lastPC.has_value()) {
|
if (lastPC.has_value()) {
|
||||||
ImGui::Text("%s", std::format("Occurred @ PC = {:016X}", Util::Error::GetLastPC().value()).c_str());
|
ImGui::Text("%s", std::format("Occurred @ PC = {:016X}", Util::Error::GetLastPC().value()).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto memoryAccess = Util::Error::GetMemoryAccess();
|
auto memoryAccess = Util::Error::GetMemoryAccess();
|
||||||
if(memoryAccess.has_value()) {
|
if (memoryAccess.has_value()) {
|
||||||
const auto [is_write, size, address, written_val] = memoryAccess.value();
|
const auto [is_write, size, address, written_val] = memoryAccess.value();
|
||||||
ImGui::Text("%s", std::format("{} {}-bit value @ {:08X}{}", is_write ? "Writing" : "Reading",
|
ImGui::Text("%s",
|
||||||
static_cast<u8>(size), address,
|
std::format("{} {}-bit value @ {:08X}{}", is_write ? "Writing" : "Reading", static_cast<u8>(size),
|
||||||
is_write ? std::format(" (value = 0x{:X})", written_val) : "")
|
address, is_write ? std::format(" (value = 0x{:X})", written_val) : "")
|
||||||
.c_str());
|
.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
return {lastPC, memoryAccess};
|
return {lastPC, memoryAccess};
|
||||||
}
|
}
|
||||||
|
|
||||||
void KaizenGui::RenderUI() {
|
void KaizenGui::RenderUI() {
|
||||||
n64::Core& core = n64::Core::GetInstance();
|
n64::Core &core = n64::Core::GetInstance();
|
||||||
gui::StartFrame();
|
gui::StartFrame();
|
||||||
|
|
||||||
if(ImGui::BeginMainMenuBar()) {
|
if (ImGui::BeginMainMenuBar()) {
|
||||||
if(ImGui::BeginMenu("File")) {
|
if (ImGui::BeginMenu("File")) {
|
||||||
if(ImGui::MenuItem("Open", "Ctrl-O")) {
|
if (ImGui::MenuItem("Open", "Ctrl-O")) {
|
||||||
fileDialogOpen = true;
|
fileDialogOpen = true;
|
||||||
}
|
}
|
||||||
if(ImGui::MenuItem("Exit")) {
|
if (ImGui::MenuItem("Exit")) {
|
||||||
quit = true;
|
quit = true;
|
||||||
emuThread.Stop();
|
emuThread.Stop();
|
||||||
}
|
}
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
if (ImGui::BeginMenu("Emulation")) {
|
||||||
|
ImGui::BeginDisabled(!core.romLoaded);
|
||||||
|
|
||||||
|
if (ImGui::MenuItem(core.pause ? "Resume" : "Pause", "P")) {
|
||||||
|
emuThread.TogglePause();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::MenuItem("Reset", "R")) {
|
||||||
|
emuThread.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::MenuItem("Stop", "Q")) {
|
||||||
|
emuThread.Stop();
|
||||||
|
core.romLoaded = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::Checkbox("Unlock framerate", &unlockFramerate)) {
|
||||||
|
core.parallel.SetFramerateUnlocked(unlockFramerate);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::MenuItem("Open Debugger")) {
|
||||||
|
debugger.Open();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
|
if (ImGui::MenuItem("Options")) {
|
||||||
|
settingsWindow.isOpen = true;
|
||||||
|
}
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
if (ImGui::BeginMenu("Help")) {
|
||||||
|
if (ImGui::MenuItem("About")) {
|
||||||
|
aboutOpen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
ImGui::EndMainMenuBar();
|
||||||
}
|
}
|
||||||
if(ImGui::BeginMenu("Emulation")) {
|
|
||||||
ImGui::BeginDisabled(!core.romLoaded);
|
|
||||||
|
|
||||||
if(ImGui::MenuItem(core.pause ? "Resume" : "Pause", "P")) {
|
|
||||||
emuThread.TogglePause();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(ImGui::MenuItem("Reset", "R")) {
|
|
||||||
emuThread.Reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(ImGui::MenuItem("Stop", "Q")) {
|
|
||||||
emuThread.Stop();
|
|
||||||
core.romLoaded = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(ImGui::Checkbox("Unlock framerate", &unlockFramerate)) {
|
|
||||||
core.parallel.SetFramerateUnlocked(unlockFramerate);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(ImGui::MenuItem("Open Debugger")) {
|
if (!Util::Error::IsHandled()) {
|
||||||
debugger.Open();
|
ImGui::OpenPopup(Util::Error::GetSeverity().as_c_str());
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::EndDisabled();
|
|
||||||
|
|
||||||
if(ImGui::MenuItem("Options")) {
|
|
||||||
settingsWindow.isOpen = true;
|
|
||||||
}
|
|
||||||
ImGui::EndMenu();
|
|
||||||
}
|
}
|
||||||
if(ImGui::BeginMenu("Help")) {
|
|
||||||
if(ImGui::MenuItem("About")) {
|
if (settingsWindow.isOpen) {
|
||||||
aboutOpen = true;
|
ImGui::OpenPopup("Settings", ImGuiPopupFlags_None);
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::EndMenu();
|
|
||||||
}
|
}
|
||||||
ImGui::EndMainMenuBar();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!Util::Error::IsHandled()) {
|
if (aboutOpen) {
|
||||||
ImGui::OpenPopup(Util::Error::GetSeverity().as_c_str());
|
ImGui::OpenPopup("About Kaizen");
|
||||||
}
|
|
||||||
|
|
||||||
if(settingsWindow.isOpen) {
|
|
||||||
ImGui::OpenPopup("Settings", ImGuiPopupFlags_None);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(aboutOpen) {
|
|
||||||
ImGui::OpenPopup("About Kaizen");
|
|
||||||
}
|
|
||||||
|
|
||||||
settingsWindow.render();
|
|
||||||
debugger.render();
|
|
||||||
|
|
||||||
const ImVec2 center = ImGui::GetMainViewport()->GetCenter();
|
|
||||||
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
|
|
||||||
|
|
||||||
if (ImGui::BeginPopupModal("About Kaizen", &aboutOpen, ImGuiWindowFlags_AlwaysAutoResize)) {
|
|
||||||
ImGui::Text("Kaizen is a Nintendo 64 emulator that strives");
|
|
||||||
ImGui::Text("to offer a friendly user experience and compatibility.");
|
|
||||||
ImGui::Text("Kaizen is licensed under the BSD 3-clause license.");
|
|
||||||
ImGui::Text("Nintendo 64 is a registered trademark of Nintendo Co., Ltd.");
|
|
||||||
ImGui::Separator();
|
|
||||||
ImGui::Text("Kaizen %s%s", KAIZEN_USE_HASH ? "dev build " : "", KAIZEN_VERSION_STR);
|
|
||||||
ImGui::Separator();
|
|
||||||
if(ImGui::Button("OK")) {
|
|
||||||
aboutOpen = false;
|
|
||||||
ImGui::CloseCurrentPopup();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::EndPopup();
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
|
settingsWindow.render();
|
||||||
|
debugger.render();
|
||||||
|
|
||||||
if (ImGui::BeginPopupModal(Util::Error::GetSeverity().as_c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
const ImVec2 center = ImGui::GetMainViewport()->GetCenter();
|
||||||
emuThread.TogglePause();
|
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
|
||||||
switch(Util::Error::GetSeverity().as_enum) {
|
|
||||||
case Util::Error::Severity::WARN: {
|
|
||||||
ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x8054eae5);
|
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text, 0xff7be4e1);
|
|
||||||
ImGui::Text("Warning of type: %s", Util::Error::GetType().as_c_str());
|
|
||||||
ImGui::PopStyleColor();
|
|
||||||
ImGui::PopStyleColor();
|
|
||||||
ImGui::Text(R"(Warning message: "%s")", Util::Error::GetError().c_str());
|
|
||||||
RenderErrorMessageDetails();
|
|
||||||
|
|
||||||
if(n64::Core::GetInstance().romLoaded && !n64::Core::GetInstance().pause) {
|
if (ImGui::BeginPopupModal("About Kaizen", &aboutOpen, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||||
const bool ignore = ImGui::Button("Try continuing"); ImGui::SameLine();
|
ImGui::Text("Kaizen is a Nintendo 64 emulator that strives");
|
||||||
const bool stop = ImGui::Button("Stop emulation"); ImGui::SameLine();
|
ImGui::Text("to offer a friendly user experience and compatibility.");
|
||||||
const bool chooseAnother = ImGui::Button("Choose another ROM");
|
ImGui::Text("Kaizen is licensed under the BSD 3-clause license.");
|
||||||
if(ignore || stop || chooseAnother) {
|
ImGui::Text("Nintendo 64 is a registered trademark of Nintendo Co., Ltd.");
|
||||||
Util::Error::SetHandled();
|
ImGui::Separator();
|
||||||
|
ImGui::Text("Kaizen %s%s", KAIZEN_USE_HASH ? "dev build " : "", KAIZEN_VERSION_STR);
|
||||||
|
ImGui::Separator();
|
||||||
|
if (ImGui::Button("OK")) {
|
||||||
|
aboutOpen = false;
|
||||||
ImGui::CloseCurrentPopup();
|
ImGui::CloseCurrentPopup();
|
||||||
}
|
|
||||||
|
|
||||||
if(ignore) {
|
|
||||||
emuThread.TogglePause();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(stop || chooseAnother) {
|
|
||||||
emuThread.Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(chooseAnother) {
|
|
||||||
fileDialogOpen = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ImGui::Button("OK"))
|
ImGui::EndPopup();
|
||||||
ImGui::CloseCurrentPopup();
|
|
||||||
} break;
|
|
||||||
case Util::Error::Severity::UNRECOVERABLE: {
|
|
||||||
emuThread.Stop();
|
|
||||||
ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff);
|
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf);
|
|
||||||
ImGui::Text("An unrecoverable error has occurred! Emulation has been stopped...");
|
|
||||||
ImGui::Text("Error of type: %s", Util::Error::GetType().as_c_str());
|
|
||||||
ImGui::PopStyleColor();
|
|
||||||
ImGui::PopStyleColor();
|
|
||||||
ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str());
|
|
||||||
RenderErrorMessageDetails();
|
|
||||||
if(ImGui::Button("OK"))
|
|
||||||
ImGui::CloseCurrentPopup();
|
|
||||||
} break;
|
|
||||||
case Util::Error::Severity::NON_FATAL: {
|
|
||||||
ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff);
|
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf);
|
|
||||||
ImGui::Text("An error has occurred!");
|
|
||||||
ImGui::Text("Error of type: %s", Util::Error::GetType().as_c_str());
|
|
||||||
ImGui::PopStyleColor();
|
|
||||||
ImGui::PopStyleColor();
|
|
||||||
ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str());
|
|
||||||
auto [lastPC, memoryAccess] = RenderErrorMessageDetails();
|
|
||||||
|
|
||||||
const bool ignore = ImGui::Button("Try continuing"); ImGui::SameLine();
|
|
||||||
const bool stop = ImGui::Button("Stop emulation"); ImGui::SameLine();
|
|
||||||
const bool chooseAnother = ImGui::Button("Choose another ROM");
|
|
||||||
const bool openInDebugger = lastPC.has_value() ? ImGui::Button("Add breakpoint at this PC and open the debugger") : false;
|
|
||||||
if(ignore || stop || chooseAnother || openInDebugger) {
|
|
||||||
Util::Error::SetHandled();
|
|
||||||
ImGui::CloseCurrentPopup();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(ignore) {
|
|
||||||
emuThread.TogglePause();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(stop || chooseAnother) {
|
|
||||||
emuThread.Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(chooseAnother) {
|
|
||||||
fileDialogOpen = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(openInDebugger) {
|
|
||||||
if(!n64::Core::GetInstance().breakpoints.contains(lastPC.value()))
|
|
||||||
n64::Core::GetInstance().ToggleBreakpoint(lastPC.value());
|
|
||||||
|
|
||||||
debugger.Open();
|
|
||||||
emuThread.Reset();
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
default: break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::EndPopup();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(ImGui::BeginMainStatusBar()) {
|
|
||||||
ImGui::Text("FPS: %.2f", ImGui::GetIO().Framerate);
|
|
||||||
ImGui::EndMainStatusBar();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shouldDisplaySpinner) {
|
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
|
||||||
ImGui::SetNextWindowPos({static_cast<float>(width) * 0.5f, static_cast<float>(height) * 0.5f}, 0, ImVec2(0.5f, 0.5f));
|
|
||||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, IM_COL32_BLACK_TRANS);
|
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
|
|
||||||
|
|
||||||
ImGui::Begin("##spinnerContainer", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration);
|
if (ImGui::BeginPopupModal(Util::Error::GetSeverity().as_c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||||
|
emuThread.TogglePause();
|
||||||
|
switch (Util::Error::GetSeverity().as_enum) {
|
||||||
|
case Util::Error::Severity::WARN:
|
||||||
|
{
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x8054eae5);
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, 0xff7be4e1);
|
||||||
|
ImGui::Text("Warning of type: %s", Util::Error::GetType().as_c_str());
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
ImGui::Text(R"(Warning message: "%s")", Util::Error::GetError().c_str());
|
||||||
|
RenderErrorMessageDetails();
|
||||||
|
|
||||||
ImGui::Spinner("##spinner", 10.f, 4.f, ImGui::GetColorU32(ImGui::GetStyle().Colors[ImGuiCol_TitleBgActive]));
|
if (n64::Core::GetInstance().romLoaded && !n64::Core::GetInstance().pause) {
|
||||||
ImGui::SameLine();
|
const bool ignore = ImGui::Button("Try continuing");
|
||||||
|
ImGui::SameLine();
|
||||||
|
const bool stop = ImGui::Button("Stop emulation");
|
||||||
|
ImGui::SameLine();
|
||||||
|
const bool chooseAnother = ImGui::Button("Choose another ROM");
|
||||||
|
if (ignore || stop || chooseAnother) {
|
||||||
|
Util::Error::SetHandled();
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::PushFont(nullptr, ImGui::GetStyle().FontSizeBase * 2.f);
|
if (ignore) {
|
||||||
ImGui::Text("Loading \"%s\"...", fs::path(fileToLoad).filename().string().c_str());
|
emuThread.TogglePause();
|
||||||
ImGui::PopFont();
|
}
|
||||||
|
|
||||||
ImGui::End();
|
if (stop || chooseAnother) {
|
||||||
|
emuThread.Stop();
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::PopStyleVar();
|
if (chooseAnother) {
|
||||||
ImGui::PopStyleColor();
|
fileDialogOpen = true;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::Render();
|
if (ImGui::Button("OK"))
|
||||||
if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
|
ImGui::CloseCurrentPopup();
|
||||||
ImGui::UpdatePlatformWindows();
|
}
|
||||||
ImGui::RenderPlatformWindowsDefault();
|
break;
|
||||||
}
|
case Util::Error::Severity::UNRECOVERABLE:
|
||||||
|
{
|
||||||
|
emuThread.Stop();
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff);
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf);
|
||||||
|
ImGui::Text("An unrecoverable error has occurred! Emulation has been stopped...");
|
||||||
|
ImGui::Text("Error of type: %s", Util::Error::GetType().as_c_str());
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str());
|
||||||
|
RenderErrorMessageDetails();
|
||||||
|
if (ImGui::Button("OK"))
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Util::Error::Severity::NON_FATAL:
|
||||||
|
{
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_TitleBg, 0x800000ff);
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, 0xff3b3bbf);
|
||||||
|
ImGui::Text("An error has occurred!");
|
||||||
|
ImGui::Text("Error of type: %s", Util::Error::GetType().as_c_str());
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str());
|
||||||
|
auto [lastPC, memoryAccess] = RenderErrorMessageDetails();
|
||||||
|
|
||||||
if(fileDialogOpen) {
|
const bool ignore = ImGui::Button("Try continuing");
|
||||||
fileDialogOpen = false;
|
ImGui::SameLine();
|
||||||
constexpr SDL_DialogFileFilter filters[] = {{"All files", "*"}, {"Nintendo 64 executable", "n64;z64;v64"}, {"Nintendo 64 executable archive", "rar;tar;zip;7z"}};
|
const bool stop = ImGui::Button("Stop emulation");
|
||||||
SDL_ShowOpenFileDialog([](void *userdata, const char * const *filelist, int) {
|
ImGui::SameLine();
|
||||||
auto kaizen = static_cast<KaizenGui*>(userdata);
|
const bool chooseAnother = ImGui::Button("Choose another ROM");
|
||||||
|
const bool openInDebugger =
|
||||||
if (!filelist) {
|
lastPC.has_value() ? ImGui::Button("Add breakpoint at this PC and open the debugger") : false;
|
||||||
panic("An error occured: {}", SDL_GetError());
|
if (ignore || stop || chooseAnother || openInDebugger) {
|
||||||
}
|
Util::Error::SetHandled();
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
|
||||||
if (!*filelist) {
|
if (ignore) {
|
||||||
warn("The user did not select any file.");
|
emuThread.TogglePause();
|
||||||
warn("Most likely, the dialog was canceled.");
|
}
|
||||||
|
|
||||||
|
if (stop || chooseAnother) {
|
||||||
|
emuThread.Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chooseAnother) {
|
||||||
|
fileDialogOpen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (openInDebugger) {
|
||||||
|
if (!n64::Core::GetInstance().breakpoints.contains(lastPC.value()))
|
||||||
|
n64::Core::GetInstance().ToggleBreakpoint(lastPC.value());
|
||||||
|
|
||||||
|
debugger.Open();
|
||||||
|
emuThread.Reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::BeginMainStatusBar()) {
|
||||||
|
ImGui::Text("FPS: %.2f", ImGui::GetIO().Framerate);
|
||||||
|
ImGui::EndMainStatusBar();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldDisplaySpinner) {
|
||||||
|
ImGui::SetNextWindowPos({static_cast<float>(width) * 0.5f, static_cast<float>(height) * 0.5f}, 0,
|
||||||
|
ImVec2(0.5f, 0.5f));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_WindowBg, IM_COL32_BLACK_TRANS);
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
|
||||||
|
|
||||||
|
ImGui::Begin("##spinnerContainer", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration);
|
||||||
|
|
||||||
|
ImGui::Spinner("##spinner", 10.f, 4.f, ImGui::GetColorU32(ImGui::GetStyle().Colors[ImGuiCol_TitleBgActive]));
|
||||||
|
ImGui::SameLine();
|
||||||
|
|
||||||
|
ImGui::PushFont(nullptr, ImGui::GetStyle().FontSizeBase * 2.f);
|
||||||
|
ImGui::Text("Loading \"%s\"...", fs::path(fileToLoad).filename().string().c_str());
|
||||||
|
ImGui::PopFont();
|
||||||
|
|
||||||
|
ImGui::End();
|
||||||
|
|
||||||
|
ImGui::PopStyleVar();
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Render();
|
||||||
|
if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
|
||||||
|
ImGui::UpdatePlatformWindows();
|
||||||
|
ImGui::RenderPlatformWindowsDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileDialogOpen) {
|
||||||
|
fileDialogOpen = false;
|
||||||
|
constexpr SDL_DialogFileFilter filters[] = {{"All files", "*"},
|
||||||
|
{"Nintendo 64 executable", "n64;z64;v64"},
|
||||||
|
{"Nintendo 64 executable archive", "rar;tar;zip;7z"}};
|
||||||
|
SDL_ShowOpenFileDialog(
|
||||||
|
[](void *userdata, const char *const *filelist, int) {
|
||||||
|
auto kaizen = static_cast<KaizenGui *>(userdata);
|
||||||
|
|
||||||
|
if (!filelist) {
|
||||||
|
panic("An error occured: {}", SDL_GetError());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!*filelist) {
|
||||||
|
warn("The user did not select any file.");
|
||||||
|
warn("Most likely, the dialog was canceled.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
kaizen->fileToLoad = *filelist;
|
||||||
|
kaizen->shouldDisplaySpinner = true;
|
||||||
|
std::thread fileWorker(&KaizenGui::FileWorker, kaizen);
|
||||||
|
fileWorker.detach();
|
||||||
|
},
|
||||||
|
this, window.getHandle(), filters, 3, nullptr, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (minimized)
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
kaizen->fileToLoad = *filelist;
|
if (core.romLoaded) {
|
||||||
kaizen->shouldDisplaySpinner = true;
|
core.parallel.UpdateScreen<true>();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::thread fileWorker(&KaizenGui::FileWorker, kaizen);
|
core.parallel.UpdateScreen<false>();
|
||||||
fileWorker.detach();
|
|
||||||
}, this, window.getHandle(), filters, 3, nullptr, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(minimized)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if(core.romLoaded) {
|
|
||||||
core.parallel.UpdateScreen<true>();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
core.parallel.UpdateScreen<false>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void KaizenGui::LoadROM(const std::string &path) noexcept {
|
void KaizenGui::LoadROM(const std::string &path) noexcept {
|
||||||
n64::Core& core = n64::Core::GetInstance();
|
n64::Core &core = n64::Core::GetInstance();
|
||||||
core.LoadROM(path);
|
core.LoadROM(path);
|
||||||
const auto gameNameDB = n64::Core::GetMem().rom.gameNameDB;
|
const auto gameNameDB = n64::Core::GetMem().rom.gameNameDB;
|
||||||
SDL_SetWindowTitle(window.getHandle(), ("Kaizen " KAIZEN_VERSION_STR " - " + gameNameDB).c_str());
|
SDL_SetWindowTitle(window.getHandle(), ("Kaizen " KAIZEN_VERSION_STR " - " + gameNameDB).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void KaizenGui::run() {
|
void KaizenGui::run() {
|
||||||
while(!quit) {
|
while (!quit) {
|
||||||
SDL_Event e;
|
SDL_Event e;
|
||||||
while (SDL_PollEvent(&e)) {
|
while (SDL_PollEvent(&e)) {
|
||||||
ImGui_ImplSDL3_ProcessEvent(&e);
|
ImGui_ImplSDL3_ProcessEvent(&e);
|
||||||
switch(e.type) {
|
switch (e.type) {
|
||||||
case SDL_EVENT_QUIT:
|
case SDL_EVENT_QUIT:
|
||||||
quit = true;
|
quit = true;
|
||||||
emuThread.Stop();
|
emuThread.Stop();
|
||||||
break;
|
break;
|
||||||
case SDL_EVENT_WINDOW_MINIMIZED:
|
case SDL_EVENT_WINDOW_MINIMIZED:
|
||||||
minimized = true;
|
minimized = true;
|
||||||
break;
|
break;
|
||||||
case SDL_EVENT_WINDOW_RESTORED:
|
case SDL_EVENT_WINDOW_RESTORED:
|
||||||
minimized = false;
|
minimized = false;
|
||||||
break;
|
break;
|
||||||
default: break;
|
default:
|
||||||
}
|
break;
|
||||||
QueryDevices(e);
|
}
|
||||||
HandleInput(e);
|
QueryDevices(e);
|
||||||
|
HandleInput(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_GetWindowSize(window.getHandle(), &width, &height);
|
||||||
|
|
||||||
|
emuThread.run();
|
||||||
|
RenderUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_GetWindowSize(window.getHandle(), &width, &height);
|
|
||||||
|
|
||||||
emuThread.run();
|
|
||||||
RenderUI();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void KaizenGui::LoadTAS(const std::string &path) noexcept {
|
void KaizenGui::LoadTAS(const std::string &path) noexcept { n64::Core::GetInstance().LoadTAS(fs::path(path)); }
|
||||||
n64::Core::GetInstance().LoadTAS(fs::path(path));
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ class KaizenGui final {
|
|||||||
public:
|
public:
|
||||||
explicit KaizenGui() noexcept;
|
explicit KaizenGui() noexcept;
|
||||||
~KaizenGui();
|
~KaizenGui();
|
||||||
|
|
||||||
double fpsCounter = -1.0;
|
double fpsCounter = -1.0;
|
||||||
bool fastForward = false;
|
bool fastForward = false;
|
||||||
bool unlockFramerate = false;
|
bool unlockFramerate = false;
|
||||||
@@ -22,7 +22,7 @@ public:
|
|||||||
Debugger debugger;
|
Debugger debugger;
|
||||||
|
|
||||||
SDL_Gamepad* gamepad = nullptr;
|
SDL_Gamepad* gamepad = nullptr;
|
||||||
|
|
||||||
void run();
|
void run();
|
||||||
static void LoadTAS(const std::string &path) noexcept;
|
static void LoadTAS(const std::string &path) noexcept;
|
||||||
void LoadROM(const std::string &path) noexcept;
|
void LoadROM(const std::string &path) noexcept;
|
||||||
@@ -37,12 +37,13 @@ private:
|
|||||||
void HandleInput(const SDL_Event &event);
|
void HandleInput(const SDL_Event &event);
|
||||||
void QueryDevices(const SDL_Event &event);
|
void QueryDevices(const SDL_Event &event);
|
||||||
|
|
||||||
[[noreturn]] void FileWorker() {
|
void FileWorker() {
|
||||||
while (true) {
|
while (true) {
|
||||||
if (!fileToLoad.empty()) {
|
if (!fileToLoad.empty()) {
|
||||||
LoadROM(fileToLoad);
|
LoadROM(fileToLoad);
|
||||||
shouldDisplaySpinner = false;
|
shouldDisplaySpinner = false;
|
||||||
fileToLoad = "";
|
fileToLoad = "";
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user