You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
184 lines
5.9 KiB
184 lines
5.9 KiB
/* GameLoop.cpp |
|
* |
|
* The main emulation loop. |
|
*/ |
|
|
|
#include "GameLoop.h" |
|
|
|
namespace PlipSdl { |
|
GameLoop::GameLoop(Plip::PlipInstance *plip, Console *console, SdlEvent *event, Timer *timer, int updateRate) { |
|
m_plip = plip; |
|
m_event = event; |
|
m_timer = timer; |
|
m_wnd = (SdlWindow*)plip->GetVideo(); |
|
|
|
m_updateRate = updateRate; |
|
m_updateTime = 1000000000 / m_updateRate; |
|
|
|
m_console = console; |
|
|
|
m_console->RegisterCommand("bp", |
|
[this](auto &&console, auto &&args) { ConsoleSetBreak(console, args); }); |
|
m_console->RegisterCommand("clearbp", |
|
[this](auto&&, auto&&) { m_plip->GetCore()->ClearBreakpoint(); }); |
|
m_console->RegisterCommand("dump", |
|
[this](auto &&console, auto &&args) { ConsoleDump(console, args); }); |
|
m_console->RegisterCommand("mem", |
|
[this](auto &&console, auto &&args) { ConsoleMem(console, args); }); |
|
m_console->RegisterCommand("quit", |
|
[this](auto&&, auto&&) { m_running = false; }); |
|
} |
|
|
|
void GameLoop::ConsoleDump(Console *console, const std::vector<std::string> &args) { |
|
console->WriteLine(m_plip->GetCore()->DumpRegisters()); |
|
} |
|
|
|
void GameLoop::ConsoleMem(Console *console, const std::vector<std::string> &args) { |
|
std::stringstream ss; |
|
auto mem = m_plip->GetCore()->GetMemoryMap(); |
|
|
|
if(args.size() < 2) { |
|
console->WriteLine("usage: mem [address] ([value])"); |
|
return; |
|
} |
|
|
|
unsigned long input; |
|
if(!Console::ParseULong(args[1], &input)) { |
|
console->WriteError("unable to parse address"); |
|
return; |
|
} |
|
|
|
auto addr = (uint32_t)input; |
|
if(addr >= mem->GetLength()) { |
|
ss << "specified address is out of range\n" |
|
<< "maximum value: " << PrintHex(mem->GetLength() - 1, 8) << "\n"; |
|
console->WriteError(ss.str()); |
|
return; |
|
} |
|
|
|
uint8_t val; |
|
if(args.size() < 3) { |
|
val = mem->GetByte(addr); |
|
} else { |
|
if(!Console::ParseULong(args[2], &input)) { |
|
console->WriteError("unable to parse value"); |
|
return; |
|
} |
|
|
|
if(input > 0xFF) |
|
console->WriteWarn("value will be truncated"); |
|
|
|
val = input & 0xFF; |
|
mem->SetByte(addr, val); |
|
} |
|
|
|
ss << "[" << PrintHex(addr, 8) << "] == " << std::to_string(val) << " (" |
|
<< PrintHex(val, 2) << ")"; |
|
console->WriteLine(ss.str()); |
|
} |
|
|
|
void GameLoop::ConsoleSetBreak(Console *console, const std::vector<std::string> &args) { |
|
std::stringstream ss; |
|
|
|
if(args.size() < 2) { |
|
console->WriteLine("usage: bp [addr]"); |
|
return; |
|
} |
|
|
|
unsigned long input; |
|
if(!Console::ParseULong(args[1], &input)) { |
|
console->WriteError("unable to parse address"); |
|
return; |
|
} |
|
|
|
auto core = m_plip->GetCore(); |
|
auto mem = core->GetMemoryMap(); |
|
if(input >= core->GetMemoryMap()->GetLength()) { |
|
ss << "specified address is out of range\n" |
|
<< "maximum value: " << PrintHex(mem->GetLength() - 1, 8) << "\n"; |
|
console->WriteError(ss.str()); |
|
return; |
|
} |
|
|
|
core->SetBreakpoint((uint32_t)input); |
|
} |
|
|
|
void GameLoop::Play() { |
|
auto audio = m_plip->GetAudio(); |
|
auto core = m_plip->GetCore(); |
|
auto turbo = false; |
|
|
|
m_running = true; |
|
SdlUiEvent uiEvent; |
|
while(m_running) { |
|
m_timer->StopwatchStart(); |
|
m_wnd->SetDrawPauseIcon(core->GetPaused()); |
|
|
|
if(m_console->GetConsoleEnabled()) { |
|
// Console is enabled. Don't run the core, but make sure that its |
|
// video output remains current. |
|
uiEvent = m_console->ProcessEvents(); |
|
core->Redraw(); |
|
m_console->Run(); |
|
} else { |
|
uiEvent = m_event->ProcessEvents(); |
|
|
|
if(core->GetPaused()) { |
|
if(uiEvent == SdlUiEvent::Step) { |
|
m_plip->Run(core->GetStepTime()); |
|
} else if(uiEvent == SdlUiEvent::FrameAdvance) { |
|
m_plip->Run(m_updateTime); |
|
} |
|
|
|
// Ensure that the screen stays updated. |
|
core->Redraw(); |
|
} else { |
|
// As implemented, this will not be able to compensate for the host being |
|
// unable to keep up with the emulation core. |
|
// TODO: Fix this so that it will skip frames where appropriate. |
|
m_plip->Run(m_updateTime); |
|
} |
|
} |
|
|
|
switch(uiEvent) { |
|
case SdlUiEvent::PlayPause: |
|
core->SetPaused(!core->GetPaused()); |
|
break; |
|
|
|
case SdlUiEvent::ToggleConsole: |
|
m_console->ToggleConsole(); |
|
break; |
|
|
|
case SdlUiEvent::TurboOff: |
|
turbo = false; |
|
break; |
|
|
|
case SdlUiEvent::TurboOn: |
|
turbo = true; |
|
break; |
|
|
|
case SdlUiEvent::Quit: |
|
m_running = false; |
|
break; |
|
|
|
default: break; // clang-tidy appeasement |
|
} |
|
|
|
auto time = m_timer->StopwatchStop(); |
|
auto delay = m_updateTime - time; |
|
|
|
if(!turbo) { |
|
while(delay < 0) |
|
delay += m_updateTime; |
|
|
|
m_timer->Nanosleep(delay); |
|
} |
|
} |
|
|
|
audio->DequeueAll(); |
|
} |
|
|
|
void GameLoop::SetStep(bool value) { |
|
m_plip->GetCore()->SetPaused(value); |
|
} |
|
}
|
|
|