Skip to content
This repository was archived by the owner on Sep 11, 2023. It is now read-only.

Commit 47fbe93

Browse files
committed
Added support for VS DualSystem (WIP - dual video/audio, input, save states, movies & netplay working)
1 parent 1489868 commit 47fbe93

23 files changed

+409
-85
lines changed

Core/BaseMapper.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -643,7 +643,7 @@ void BaseMapper::ApplyCheats()
643643

644644
void BaseMapper::GetMemoryRanges(MemoryRanges &ranges)
645645
{
646-
if(_gameSystem == GameSystem::VsUniSystem) {
646+
if(_gameSystem == GameSystem::VsUniSystem || _gameSystem == GameSystem::VsDualSystem) {
647647
ranges.AddHandler(MemoryOperation::Read, 0x6000, 0xFFFF);
648648
ranges.AddHandler(MemoryOperation::Write, 0x6000, 0xFFFF);
649649
} else {

Core/BaseRenderer.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@
99
BaseRenderer::BaseRenderer(shared_ptr<Console> console)
1010
{
1111
_console = console;
12+
13+
if(console->IsMaster()) {
14+
//Only display messages on the master CPU's screen
15+
MessageManager::RegisterMessageManager(this);
16+
}
1217
}
1318

1419
void BaseRenderer::DisplayMessage(string title, string message)

Core/Console.cpp

Lines changed: 147 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -45,36 +45,55 @@
4545
#include "DebugHud.h"
4646
#include "NotificationManager.h"
4747

48-
Console::Console()
48+
Console::Console(shared_ptr<Console> master)
4949
{
50+
_master = master;
5051
_model = NesModel::NTSC;
52+
53+
if(_master) {
54+
_master->_notificationManager->SendNotification(ConsoleNotificationType::VsDualSystemStarted);
55+
}
5156
}
5257

5358
Console::~Console()
5459
{
5560
MovieManager::Stop();
56-
GetSoundMixer()->StopRecording();
5761
}
5862

5963
void Console::Init()
6064
{
6165
_notificationManager.reset(new NotificationManager());
62-
_saveStateManager.reset(new SaveStateManager(shared_from_this()));
66+
6367
_videoRenderer.reset(new VideoRenderer(shared_from_this()));
6468
_videoDecoder.reset(new VideoDecoder(shared_from_this()));
69+
70+
_saveStateManager.reset(new SaveStateManager(shared_from_this()));
6571
_cheatManager.reset(new CheatManager(shared_from_this()));
6672
_debugHud.reset(new DebugHud());
73+
6774
_soundMixer.reset(new SoundMixer(shared_from_this()));
75+
_soundMixer->SetNesModel(_model);
6876
}
6977

7078
void Console::Release(bool forShutdown)
7179
{
7280
if(forShutdown) {
73-
_saveStateManager.reset();
74-
_videoRenderer.reset();
81+
_videoDecoder->StopThread();
82+
_videoRenderer->StopThread();
83+
7584
_videoDecoder.reset();
85+
_videoRenderer.reset();
86+
7687
_debugHud.reset();
88+
_saveStateManager.reset();
7789
_cheatManager.reset();
90+
91+
_soundMixer.reset();
92+
_notificationManager.reset();
93+
}
94+
95+
if(_master) {
96+
_master->_notificationManager->SendNotification(ConsoleNotificationType::VsDualSystemStopped);
7897
}
7998

8099
_rewindManager.reset();
@@ -85,7 +104,13 @@ void Console::Release(bool forShutdown)
85104
_hdAudioDevice.reset();
86105

87106
_systemActionManager.reset();
107+
108+
if(_slave) {
109+
_slave->Release(true);
110+
_slave.reset();
111+
}
88112

113+
_master.reset();
89114
_cpu.reset();
90115
_ppu.reset();
91116
_apu.reset();
@@ -246,20 +271,26 @@ bool Console::Initialize(VirtualFile &romFile, VirtualFile &patchFile)
246271

247272
//Changed game, stop all recordings
248273
MovieManager::Stop();
249-
GetSoundMixer()->StopRecording();
274+
_soundMixer->StopRecording();
250275
StopRecordingHdPack();
251276
}
252277

253-
#ifndef LIBRETRO
254-
//Don't use auto-save manager for libretro
255-
_autoSaveManager.reset(new AutoSaveManager(shared_from_this()));
256-
#endif
257-
258278
_mapper = mapper;
259279
_memoryManager.reset(new MemoryManager(shared_from_this()));
260280
_cpu.reset(new CPU(shared_from_this()));
261281
_apu.reset(new APU(shared_from_this()));
262282

283+
if(_slave) {
284+
_slave->Release(false);
285+
_slave.reset();
286+
}
287+
288+
if(!_master && _mapper->GetMapperInfo().System == GameSystem::VsDualSystem) {
289+
_slave.reset(new Console(shared_from_this()));
290+
_slave->Init();
291+
_slave->Initialize(romFile, patchFile);
292+
}
293+
263294
GameSystem system = _mapper->GetMapperInfo().System;
264295

265296
switch(system) {
@@ -269,6 +300,7 @@ bool Console::Initialize(VirtualFile &romFile, VirtualFile &patchFile)
269300
break;
270301

271302
case GameSystem::VsUniSystem:
303+
case GameSystem::VsDualSystem:
272304
_systemActionManager.reset(new VsSystemActionManager(shared_from_this()));
273305
break;
274306

@@ -285,7 +317,8 @@ bool Console::Initialize(VirtualFile &romFile, VirtualFile &patchFile)
285317
//When power cycling, poll counter must be preserved to allow movies to playback properly
286318
pollCounter = _controlManager->GetPollCounter();
287319
}
288-
if(system == GameSystem::VsUniSystem) {
320+
321+
if(system == GameSystem::VsUniSystem || system == GameSystem::VsDualSystem) {
289322
_controlManager.reset(new VsControlManager(shared_from_this(), _systemActionManager, _mapper->GetMapperControlDevice()));
290323
} else {
291324
_controlManager.reset(new ControlManager(shared_from_this(), _systemActionManager, _mapper->GetMapperControlDevice()));
@@ -330,20 +363,29 @@ bool Console::Initialize(VirtualFile &romFile, VirtualFile &patchFile)
330363

331364
ResetComponents(false);
332365

366+
#ifndef LIBRETRO
367+
//Don't use auto-save manager for libretro
368+
//Only enable auto-save for the master console (VS Dualsystem)
369+
if(IsMaster()) {
370+
_autoSaveManager.reset(new AutoSaveManager(shared_from_this()));
371+
}
372+
#endif
333373
_rewindManager.reset(new RewindManager(shared_from_this()));
334374
_notificationManager->RegisterNotificationListener(_rewindManager);
335375

336376
_videoDecoder->StartThread();
337377

338378
FolderUtilities::AddKnownGameFolder(romFile.GetFolderPath());
339379

340-
string modelName = _model == NesModel::PAL ? "PAL" : (_model == NesModel::Dendy ? "Dendy" : "NTSC");
341-
string messageTitle = MessageManager::Localize("GameLoaded") + " (" + modelName + ")";
342-
MessageManager::DisplayMessage(messageTitle, FolderUtilities::GetFilename(GetMapperInfo().RomName, false));
343-
if(EmulationSettings::GetOverclockRate() != 100) {
344-
MessageManager::DisplayMessage("ClockRate", std::to_string(EmulationSettings::GetOverclockRate()) + "%");
380+
if(IsMaster()) {
381+
string modelName = _model == NesModel::PAL ? "PAL" : (_model == NesModel::Dendy ? "Dendy" : "NTSC");
382+
string messageTitle = MessageManager::Localize("GameLoaded") + " (" + modelName + ")";
383+
MessageManager::DisplayMessage(messageTitle, FolderUtilities::GetFilename(GetMapperInfo().RomName, false));
384+
if(EmulationSettings::GetOverclockRate() != 100) {
385+
MessageManager::DisplayMessage("ClockRate", std::to_string(EmulationSettings::GetOverclockRate()) + "%");
386+
}
387+
EmulationSettings::ClearFlags(EmulationFlags::ForceMaxSpeed);
345388
}
346-
EmulationSettings::ClearFlags(EmulationFlags::ForceMaxSpeed);
347389
Resume();
348390
return true;
349391
}
@@ -392,6 +434,24 @@ shared_ptr<NotificationManager> Console::GetNotificationManager()
392434
return _notificationManager;
393435
}
394436

437+
bool Console::IsDualSystem()
438+
{
439+
return _slave != nullptr || _master != nullptr;
440+
}
441+
442+
shared_ptr<Console> Console::GetDualConsole()
443+
{
444+
//When called from the master, returns the slave.
445+
//When called from the slave, returns the master.
446+
//Returns a null pointer when not running a dualsystem game
447+
return _slave ? _slave : _master;
448+
}
449+
450+
bool Console::IsMaster()
451+
{
452+
return !_master;
453+
}
454+
395455
BaseMapper* Console::GetMapper()
396456
{
397457
return _mapper.get();
@@ -476,6 +536,11 @@ void Console::Reset(bool softReset)
476536

477537
void Console::ResetComponents(bool softReset)
478538
{
539+
if(_slave) {
540+
//Always reset/power cycle the slave alongside the master CPU
541+
_slave->ResetComponents(softReset);
542+
}
543+
479544
_soundMixer->StopAudio(true);
480545

481546
_memoryManager->Reset(softReset);
@@ -523,15 +588,26 @@ void Console::Pause()
523588
//Make sure debugger resumes if we try to pause the emu, otherwise we will get deadlocked.
524589
debugger->Suspend();
525590
}
526-
_pauseLock.Acquire();
527-
//Spin wait until emu pauses
528-
_runLock.Acquire();
591+
592+
if(_master) {
593+
//When trying to pause/resume the slave, we need to pause/resume the master instead
594+
_master->Pause();
595+
} else {
596+
_pauseLock.Acquire();
597+
//Spin wait until emu pauses
598+
_runLock.Acquire();
599+
}
529600
}
530601

531602
void Console::Resume()
532603
{
533-
_runLock.Release();
534-
_pauseLock.Release();
604+
if(_master) {
605+
//When trying to pause/resume the slave, we need to pause/resume the master instead
606+
_master->Resume();
607+
} else {
608+
_runLock.Release();
609+
_pauseLock.Release();
610+
}
535611

536612
shared_ptr<Debugger> debugger = _debugger;
537613
if(debugger) {
@@ -567,6 +643,8 @@ void Console::Run()
567643
int timeLagDataIndex = 0;
568644
double lastFrameMin = 9999;
569645
double lastFrameMax = 0;
646+
int32_t cycleGap = 0;
647+
uint32_t currentFrameNumber = 0;
570648

571649
uint32_t lastFrameNumber = -1;
572650

@@ -590,13 +668,32 @@ void Console::Run()
590668
while(true) {
591669
_cpu->Exec();
592670

593-
uint32_t currentFrameNumber = _ppu->GetFrameCount();
671+
currentFrameNumber = _ppu->GetFrameCount();
672+
673+
if(_slave) {
674+
while(true) {
675+
//Run the slave until it catches up to the master CPU (and take into account the CPU count overflow that occurs every ~20mins)
676+
cycleGap = _cpu->GetCycleCount() - _slave->_cpu->GetCycleCount();
677+
if(cycleGap > 5 || cycleGap < -10000 || currentFrameNumber > _slave->_ppu->GetFrameCount()) {
678+
_slave->_cpu->Exec();
679+
} else {
680+
break;
681+
}
682+
}
683+
}
684+
594685
if(currentFrameNumber != lastFrameNumber) {
595686
_soundMixer->ProcessEndOfFrame();
687+
if(_slave) {
688+
_slave->_soundMixer->ProcessEndOfFrame();
689+
}
596690

597691
bool displayDebugInfo = EmulationSettings::CheckFlag(EmulationFlags::DisplayDebugInfo);
598692
if(displayDebugInfo) {
599693
DisplayDebugInformation(clockTimer, lastFrameTimer, lastFrameMin, lastFrameMax, timeLagData);
694+
if(_slave) {
695+
_slave->DisplayDebugInformation(clockTimer, lastFrameTimer, lastFrameMin, lastFrameMax, timeLagData);
696+
}
600697
}
601698
lastFrameTimer.Reset();
602699

@@ -623,6 +720,9 @@ void Console::Run()
623720

624721
//Prevent audio from looping endlessly while game is paused
625722
_soundMixer->StopAudio();
723+
if(_slave) {
724+
_slave->_soundMixer->StopAudio();
725+
}
626726

627727
_runLock.Release();
628728

@@ -695,7 +795,7 @@ void Console::Run()
695795

696796
_soundMixer->StopAudio();
697797
MovieManager::Stop();
698-
GetSoundMixer()->StopRecording();
798+
_soundMixer->StopRecording();
699799

700800
PlatformUtilities::EnableScreensaver();
701801
PlatformUtilities::RestoreTimerResolution();
@@ -725,12 +825,22 @@ void Console::Run()
725825

726826
bool Console::IsRunning()
727827
{
728-
return !_stopLock.IsFree() && _running;
828+
if(_master) {
829+
//For slave CPU, return the master's state
830+
return _master->IsRunning();
831+
} else {
832+
return !_stopLock.IsFree() && _running;
833+
}
729834
}
730835

731836
bool Console::IsPaused()
732837
{
733-
return _runLock.IsFree() || !_pauseLock.IsFree() || !_running;
838+
if(_master) {
839+
//For slave CPU, return the master's state
840+
return _master->IsPaused();
841+
} else {
842+
return _runLock.IsFree() || !_pauseLock.IsFree() || !_running;
843+
}
734844
}
735845

736846
void Console::UpdateNesModel(bool sendNotification)
@@ -801,6 +911,11 @@ void Console::SaveState(ostream &saveStream)
801911
} else {
802912
Snapshotable::WriteEmptyBlock(&saveStream);
803913
}
914+
915+
if(_slave) {
916+
//For VS Dualsystem, append the 2nd console's savestate
917+
_slave->SaveState(saveStream);
918+
}
804919
}
805920
}
806921

@@ -823,6 +938,11 @@ void Console::LoadState(istream &loadStream, uint32_t stateVersion)
823938
} else {
824939
Snapshotable::SkipBlock(&loadStream);
825940
}
941+
942+
if(_slave) {
943+
//For VS Dualsystem, the slave console's savestate is appended to the end of the file
944+
_slave->LoadState(loadStream, stateVersion);
945+
}
826946

827947
shared_ptr<Debugger> debugger = _debugger;
828948
if(debugger) {

Core/Console.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ class Console : public std::enable_shared_from_this<Console>
5050
shared_ptr<BaseMapper> _mapper;
5151
shared_ptr<ControlManager> _controlManager;
5252
shared_ptr<MemoryManager> _memoryManager;
53+
54+
//Used by VS-DualSystem
55+
shared_ptr<Console> _master;
56+
shared_ptr<Console> _slave;
5357

5458
shared_ptr<SystemActionManager> _systemActionManager;
5559

@@ -87,7 +91,7 @@ class Console : public std::enable_shared_from_this<Console>
8791
void DisplayDebugInformation(Timer &clockTimer, Timer &lastFrameTimer, double &lastFrameMin, double &lastFrameMax, double *timeLagData);
8892

8993
public:
90-
Console();
94+
Console(shared_ptr<Console> master = nullptr);
9195
~Console();
9296

9397
void Init();
@@ -100,6 +104,10 @@ class Console : public std::enable_shared_from_this<Console>
100104
shared_ptr<SoundMixer> GetSoundMixer();
101105
shared_ptr<NotificationManager> GetNotificationManager();
102106

107+
bool IsDualSystem();
108+
shared_ptr<Console> GetDualConsole();
109+
bool IsMaster();
110+
103111
void ProcessCpuClock();
104112
CPU* GetCpu();
105113
PPU* GetPpu();

0 commit comments

Comments
 (0)