2014-06-14 15:27:55 +00:00
|
|
|
#include "stdafx.h"
|
2015-07-02 03:17:14 +00:00
|
|
|
#include <thread>
|
2014-06-14 15:27:55 +00:00
|
|
|
#include "Console.h"
|
2015-07-02 03:17:14 +00:00
|
|
|
#include "BaseMapper.h"
|
2014-06-24 06:47:32 +00:00
|
|
|
#include "MapperFactory.h"
|
2015-07-02 03:17:14 +00:00
|
|
|
#include "Debugger.h"
|
2014-06-23 23:02:09 +00:00
|
|
|
#include "../Utilities/Timer.h"
|
2014-07-09 22:29:07 +00:00
|
|
|
#include "../Utilities/FolderUtilities.h"
|
2015-07-02 03:17:14 +00:00
|
|
|
#include "../Core/MessageManager.h"
|
2014-06-14 15:27:55 +00:00
|
|
|
|
2015-07-02 03:17:14 +00:00
|
|
|
shared_ptr<Console> Console::Instance = nullptr;
|
2014-06-21 23:03:13 +00:00
|
|
|
uint32_t Console::Flags = 0;
|
2014-06-23 20:38:01 +00:00
|
|
|
uint32_t Console::CurrentFPS = 0;
|
2014-07-06 23:54:47 +00:00
|
|
|
SimpleLock Console::PauseLock;
|
|
|
|
SimpleLock Console::RunningLock;
|
2014-06-21 23:03:13 +00:00
|
|
|
|
2014-06-21 01:48:55 +00:00
|
|
|
Console::Console(wstring filename)
|
2014-06-14 15:27:55 +00:00
|
|
|
{
|
2014-07-09 22:29:07 +00:00
|
|
|
Initialize(filename);
|
|
|
|
}
|
|
|
|
|
|
|
|
Console::~Console()
|
|
|
|
{
|
|
|
|
Movie::Stop();
|
2015-07-02 03:17:14 +00:00
|
|
|
if(Console::Instance.get() == this) {
|
|
|
|
Console::Instance.reset();
|
2014-07-09 22:29:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-02 03:17:14 +00:00
|
|
|
shared_ptr<Console> Console::GetInstance()
|
2014-07-09 22:29:07 +00:00
|
|
|
{
|
|
|
|
return Console::Instance;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Console::Initialize(wstring filename)
|
|
|
|
{
|
2015-07-02 03:17:14 +00:00
|
|
|
if(Console::Instance == nullptr) {
|
|
|
|
Console::Instance.reset(this);
|
|
|
|
}
|
|
|
|
|
2014-07-10 23:25:35 +00:00
|
|
|
shared_ptr<BaseMapper> mapper = MapperFactory::InitializeFromFile(filename);
|
|
|
|
if(mapper) {
|
|
|
|
_romFilepath = filename;
|
2014-06-22 12:38:42 +00:00
|
|
|
|
2014-07-10 23:25:35 +00:00
|
|
|
_mapper = mapper;
|
|
|
|
_memoryManager.reset(new MemoryManager(_mapper));
|
|
|
|
_cpu.reset(new CPU(_memoryManager.get()));
|
|
|
|
_ppu.reset(new PPU(_memoryManager.get()));
|
|
|
|
_apu.reset(new APU(_memoryManager.get()));
|
2014-06-23 02:15:35 +00:00
|
|
|
|
2014-07-10 23:25:35 +00:00
|
|
|
_controlManager.reset(new ControlManager());
|
2014-06-21 19:43:41 +00:00
|
|
|
|
2014-07-10 23:25:35 +00:00
|
|
|
_memoryManager->RegisterIODevice(_mapper.get());
|
|
|
|
_memoryManager->RegisterIODevice(_ppu.get());
|
|
|
|
_memoryManager->RegisterIODevice(_apu.get());
|
|
|
|
_memoryManager->RegisterIODevice(_controlManager.get());
|
2014-06-19 23:58:15 +00:00
|
|
|
|
2014-07-10 23:25:35 +00:00
|
|
|
ResetComponents(false);
|
|
|
|
|
2015-07-02 03:17:14 +00:00
|
|
|
MessageManager::DisplayMessage(L"Game loaded", FolderUtilities::GetFilename(filename, false));
|
2014-07-10 23:25:35 +00:00
|
|
|
} else {
|
2015-07-02 03:17:14 +00:00
|
|
|
MessageManager::DisplayMessage(L"Error", wstring(L"Could not load file: ") + FolderUtilities::GetFilename(filename, true));
|
2014-07-10 23:25:35 +00:00
|
|
|
}
|
2014-07-01 16:44:01 +00:00
|
|
|
|
2014-06-14 15:27:55 +00:00
|
|
|
}
|
|
|
|
|
2014-07-09 22:29:07 +00:00
|
|
|
void Console::LoadROM(wstring filename)
|
2014-06-14 15:27:55 +00:00
|
|
|
{
|
2014-07-09 22:29:07 +00:00
|
|
|
if(!Instance) {
|
|
|
|
new Console(filename);
|
|
|
|
} else {
|
|
|
|
Console::Pause();
|
|
|
|
Instance->Initialize(filename);
|
|
|
|
Console::Resume();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
wstring Console::GetROMPath()
|
|
|
|
{
|
|
|
|
wstring filepath;
|
|
|
|
if(Instance) {
|
|
|
|
filepath = Instance->_romFilepath;
|
2014-07-02 02:58:04 +00:00
|
|
|
}
|
2014-07-09 22:29:07 +00:00
|
|
|
return filepath;
|
2014-06-14 15:27:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Console::Reset()
|
|
|
|
{
|
2014-07-01 22:05:54 +00:00
|
|
|
Movie::Stop();
|
2014-07-01 16:44:01 +00:00
|
|
|
if(Instance) {
|
2015-07-02 03:17:14 +00:00
|
|
|
Console::Pause();
|
2014-07-01 16:44:01 +00:00
|
|
|
Instance->ResetComponents(true);
|
2015-07-02 03:17:14 +00:00
|
|
|
Console::Resume();
|
2014-07-01 16:44:01 +00:00
|
|
|
}
|
2014-06-14 15:27:55 +00:00
|
|
|
}
|
|
|
|
|
2014-06-25 17:30:02 +00:00
|
|
|
void Console::ResetComponents(bool softReset)
|
2014-06-24 06:47:32 +00:00
|
|
|
{
|
|
|
|
_ppu->Reset();
|
|
|
|
_apu->Reset();
|
2015-07-02 03:17:14 +00:00
|
|
|
_cpu->Reset(softReset);
|
|
|
|
if(softReset) {
|
|
|
|
MessageManager::SendNotification(ConsoleNotificationType::GameReset);
|
|
|
|
} else {
|
|
|
|
MessageManager::SendNotification(ConsoleNotificationType::GameLoaded);
|
|
|
|
}
|
2014-06-24 06:47:32 +00:00
|
|
|
}
|
|
|
|
|
2014-06-21 01:48:55 +00:00
|
|
|
void Console::Stop()
|
|
|
|
{
|
|
|
|
_stop = true;
|
2014-07-11 03:23:04 +00:00
|
|
|
Console::ClearFlags(EmulationFlags::Paused);
|
2015-07-02 03:17:14 +00:00
|
|
|
Console::RunningLock.Acquire();
|
|
|
|
Console::RunningLock.Release();
|
2014-06-21 23:03:13 +00:00
|
|
|
}
|
|
|
|
|
2014-07-01 16:44:01 +00:00
|
|
|
void Console::Pause()
|
|
|
|
{
|
2014-07-06 23:54:47 +00:00
|
|
|
Console::PauseLock.Acquire();
|
2014-07-01 16:44:01 +00:00
|
|
|
|
|
|
|
//Spin wait until emu pauses
|
2014-07-06 23:54:47 +00:00
|
|
|
Console::RunningLock.Acquire();
|
2014-07-01 16:44:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Console::Resume()
|
|
|
|
{
|
2014-07-06 23:54:47 +00:00
|
|
|
Console::RunningLock.Release();
|
|
|
|
Console::PauseLock.Release();
|
2014-07-01 16:44:01 +00:00
|
|
|
}
|
|
|
|
|
2014-06-21 23:03:13 +00:00
|
|
|
void Console::SetFlags(int flags)
|
|
|
|
{
|
|
|
|
Console::Flags |= flags;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Console::ClearFlags(int flags)
|
|
|
|
{
|
2014-06-24 00:00:51 +00:00
|
|
|
Console::Flags &= ~flags;
|
2014-06-21 23:03:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Console::CheckFlag(int flag)
|
|
|
|
{
|
|
|
|
return (Console::Flags & flag) == flag;
|
2014-06-21 01:48:55 +00:00
|
|
|
}
|
|
|
|
|
2014-06-23 20:38:01 +00:00
|
|
|
uint32_t Console::GetFPS()
|
|
|
|
{
|
|
|
|
return Console::CurrentFPS;
|
|
|
|
}
|
|
|
|
|
2014-06-14 15:27:55 +00:00
|
|
|
void Console::Run()
|
|
|
|
{
|
2014-06-21 19:43:41 +00:00
|
|
|
Timer clockTimer;
|
|
|
|
Timer fpsTimer;
|
2014-06-21 04:37:44 +00:00
|
|
|
uint32_t lastFrameCount = 0;
|
2014-06-22 14:07:40 +00:00
|
|
|
double elapsedTime = 0;
|
2014-06-23 17:52:53 +00:00
|
|
|
double targetTime = 16.6666666666666666;
|
2014-07-06 23:54:47 +00:00
|
|
|
|
|
|
|
Console::RunningLock.Acquire();
|
|
|
|
|
2014-06-21 19:43:41 +00:00
|
|
|
while(true) {
|
2015-07-05 02:21:14 +00:00
|
|
|
bool frameDone = _apu->Exec(_cpu->Exec());
|
2014-06-23 02:15:35 +00:00
|
|
|
|
2014-06-30 18:44:30 +00:00
|
|
|
if(frameDone) {
|
|
|
|
_cpu->EndFrame();
|
|
|
|
|
2014-07-01 16:44:01 +00:00
|
|
|
if(CheckFlag(EmulationFlags::LimitFPS) && frameDone) {
|
|
|
|
elapsedTime = clockTimer.GetElapsedMS();
|
|
|
|
while(targetTime > elapsedTime) {
|
|
|
|
if(targetTime - elapsedTime > 2) {
|
|
|
|
std::this_thread::sleep_for(std::chrono::duration<int, std::milli>((int)(targetTime - elapsedTime - 1)));
|
|
|
|
}
|
|
|
|
elapsedTime = clockTimer.GetElapsedMS();
|
|
|
|
}
|
|
|
|
}
|
2014-07-06 23:54:47 +00:00
|
|
|
|
|
|
|
if(!Console::PauseLock.IsFree()) {
|
|
|
|
//Need to temporarely pause the emu (to save/load a state, etc.)
|
|
|
|
Console::RunningLock.Release();
|
|
|
|
|
|
|
|
//Spin wait until we are allowed to start again
|
|
|
|
Console::PauseLock.WaitForRelease();
|
2014-07-01 16:44:01 +00:00
|
|
|
|
2014-07-06 23:54:47 +00:00
|
|
|
Console::RunningLock.Acquire();
|
2014-07-01 16:44:01 +00:00
|
|
|
}
|
2014-07-06 23:54:47 +00:00
|
|
|
|
2014-07-11 03:23:04 +00:00
|
|
|
if(CheckFlag(EmulationFlags::Paused) && !_stop) {
|
2015-07-02 03:17:14 +00:00
|
|
|
MessageManager::SendNotification(ConsoleNotificationType::GamePaused);
|
2014-07-10 01:48:54 +00:00
|
|
|
Console::RunningLock.Release();
|
2015-07-02 03:17:14 +00:00
|
|
|
|
|
|
|
//Prevent audio from looping endlessly while game is paused
|
|
|
|
_apu->StopAudio();
|
|
|
|
|
2014-07-10 01:48:54 +00:00
|
|
|
while(CheckFlag(EmulationFlags::Paused)) {
|
|
|
|
//Sleep until emulation is resumed
|
|
|
|
std::this_thread::sleep_for(std::chrono::duration<int, std::milli>(100));
|
|
|
|
}
|
|
|
|
Console::RunningLock.Acquire();
|
2015-07-02 03:17:14 +00:00
|
|
|
MessageManager::SendNotification(ConsoleNotificationType::GameResumed);
|
2014-07-10 01:48:54 +00:00
|
|
|
}
|
2014-07-06 23:54:47 +00:00
|
|
|
clockTimer.Reset();
|
|
|
|
|
|
|
|
if(_stop) {
|
|
|
|
_stop = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2015-07-02 03:17:14 +00:00
|
|
|
|
2014-07-06 23:54:47 +00:00
|
|
|
if(fpsTimer.GetElapsedMS() > 1000) {
|
|
|
|
uint32_t frameCount = _ppu->GetFrameCount();
|
2015-07-02 03:17:14 +00:00
|
|
|
if((int32_t)frameCount - (int32_t)lastFrameCount < 0) {
|
|
|
|
Console::CurrentFPS = 0;
|
|
|
|
} else {
|
|
|
|
Console::CurrentFPS = (int)(std::round((double)(frameCount - lastFrameCount) / (fpsTimer.GetElapsedMS() / 1000)));
|
|
|
|
}
|
2014-07-06 23:54:47 +00:00
|
|
|
lastFrameCount = frameCount;
|
|
|
|
fpsTimer.Reset();
|
2014-06-23 17:52:53 +00:00
|
|
|
}
|
2014-06-14 22:20:56 +00:00
|
|
|
}
|
2014-07-06 23:54:47 +00:00
|
|
|
Console::RunningLock.Release();
|
2015-07-02 03:17:14 +00:00
|
|
|
_apu->StopAudio();
|
2014-06-26 01:52:37 +00:00
|
|
|
}
|
|
|
|
|
2014-07-01 16:44:01 +00:00
|
|
|
void Console::SaveState(ostream &saveStream)
|
2014-06-26 01:52:37 +00:00
|
|
|
{
|
2014-07-06 23:54:47 +00:00
|
|
|
if(Instance) {
|
|
|
|
Instance->_cpu->SaveSnapshot(&saveStream);
|
|
|
|
Instance->_ppu->SaveSnapshot(&saveStream);
|
|
|
|
Instance->_memoryManager->SaveSnapshot(&saveStream);
|
|
|
|
Instance->_mapper->SaveSnapshot(&saveStream);
|
|
|
|
Instance->_apu->SaveSnapshot(&saveStream);
|
|
|
|
Instance->_controlManager->SaveSnapshot(&saveStream);
|
|
|
|
}
|
2014-07-01 16:44:01 +00:00
|
|
|
}
|
2014-06-26 01:52:37 +00:00
|
|
|
|
2014-07-01 16:44:01 +00:00
|
|
|
void Console::LoadState(istream &loadStream)
|
|
|
|
{
|
2014-07-06 23:54:47 +00:00
|
|
|
if(Instance) {
|
|
|
|
Instance->_cpu->LoadSnapshot(&loadStream);
|
|
|
|
Instance->_ppu->LoadSnapshot(&loadStream);
|
|
|
|
Instance->_memoryManager->LoadSnapshot(&loadStream);
|
|
|
|
Instance->_mapper->LoadSnapshot(&loadStream);
|
|
|
|
Instance->_apu->LoadSnapshot(&loadStream);
|
|
|
|
Instance->_controlManager->LoadSnapshot(&loadStream);
|
2014-07-10 01:11:02 +00:00
|
|
|
|
2015-07-02 03:17:14 +00:00
|
|
|
MessageManager::SendNotification(ConsoleNotificationType::StateLoaded);
|
2014-07-06 23:54:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Console::LoadState(uint8_t *buffer, uint32_t bufferSize)
|
|
|
|
{
|
|
|
|
stringstream stream;
|
|
|
|
stream.write((char*)buffer, bufferSize);
|
|
|
|
stream.seekg(0, ios::beg);
|
|
|
|
LoadState(stream);
|
2014-06-26 01:52:37 +00:00
|
|
|
}
|
|
|
|
|
2015-06-24 23:26:19 +00:00
|
|
|
shared_ptr<Debugger> Console::GetDebugger()
|
|
|
|
{
|
2015-07-02 03:17:14 +00:00
|
|
|
return shared_ptr<Debugger>(new Debugger(Console::Instance, _cpu, _ppu, _memoryManager, _mapper));
|
2015-06-24 23:26:19 +00:00
|
|
|
}
|
|
|
|
|
2014-06-22 12:38:42 +00:00
|
|
|
bool Console::RunTest(uint8_t *expectedResult)
|
2014-06-14 15:27:55 +00:00
|
|
|
{
|
2014-06-19 02:54:23 +00:00
|
|
|
Timer timer;
|
2014-06-27 00:55:22 +00:00
|
|
|
uint8_t maxWait = 60;
|
|
|
|
uint8_t* lastFrameBuffer = new uint8_t[256 * 240 * 4];
|
2014-07-27 23:42:32 +00:00
|
|
|
bool result = false;
|
|
|
|
|
|
|
|
Console::RunningLock.Acquire();
|
|
|
|
|
2014-06-14 22:20:56 +00:00
|
|
|
while(true) {
|
2015-07-05 02:21:14 +00:00
|
|
|
if(_apu->Exec(_cpu->Exec())) {
|
2014-06-30 18:44:30 +00:00
|
|
|
_cpu->EndFrame();
|
|
|
|
}
|
|
|
|
|
2014-06-22 12:38:42 +00:00
|
|
|
if(timer.GetElapsedMS() > 100) {
|
2014-06-25 16:47:15 +00:00
|
|
|
if(memcmp(_ppu->GetFrameBuffer(), expectedResult, 256 * 240 * 4) == 0) {
|
2014-07-27 23:42:32 +00:00
|
|
|
result = true;
|
|
|
|
break;
|
2014-06-22 12:38:42 +00:00
|
|
|
}
|
2014-06-27 00:55:22 +00:00
|
|
|
|
2014-06-19 02:54:23 +00:00
|
|
|
timer.Reset();
|
2014-06-22 12:38:42 +00:00
|
|
|
|
2014-06-27 00:55:22 +00:00
|
|
|
if(memcmp(lastFrameBuffer, _ppu->GetFrameBuffer(), 256 * 240 * 4) != 0) {
|
|
|
|
memcpy(lastFrameBuffer, _ppu->GetFrameBuffer(), 256 * 240 * 4);
|
|
|
|
maxWait = 60;
|
|
|
|
}
|
|
|
|
|
|
|
|
maxWait--;
|
2014-06-22 12:38:42 +00:00
|
|
|
if(maxWait == 0) {
|
2014-07-27 23:42:32 +00:00
|
|
|
result = false;
|
|
|
|
break;
|
2014-06-22 12:38:42 +00:00
|
|
|
}
|
2014-06-19 02:54:23 +00:00
|
|
|
}
|
2014-06-14 22:20:56 +00:00
|
|
|
}
|
2014-07-27 23:42:32 +00:00
|
|
|
|
|
|
|
Console::RunningLock.Release();
|
2014-06-27 00:55:22 +00:00
|
|
|
|
|
|
|
delete[] lastFrameBuffer;
|
2014-07-27 23:42:32 +00:00
|
|
|
return result;
|
2014-06-21 01:48:55 +00:00
|
|
|
}
|
|
|
|
|
2014-06-22 12:38:42 +00:00
|
|
|
void Console::SaveTestResult()
|
2014-06-14 22:20:56 +00:00
|
|
|
{
|
2014-07-09 22:29:07 +00:00
|
|
|
wstring filename = _romFilepath + L".trt";
|
2014-06-22 12:38:42 +00:00
|
|
|
|
|
|
|
ofstream testResultFile(filename, ios::out | ios::binary);
|
|
|
|
|
|
|
|
if(!testResultFile) {
|
|
|
|
throw std::exception("File could not be opened");
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t* buffer = new uint8_t[256 * 240 * 4];
|
|
|
|
memcpy(buffer, _ppu->GetFrameBuffer(), 256 * 240 * 4);
|
|
|
|
|
|
|
|
testResultFile.write((char *)buffer, 256 * 240 * 4);
|
|
|
|
|
|
|
|
testResultFile.close();
|
|
|
|
|
|
|
|
delete[] buffer;
|
2014-06-14 15:27:55 +00:00
|
|
|
}
|