Compare commits

...

No commits in common. "master" and "gh-pages" have entirely different histories.

1911 changed files with 12891 additions and 410098 deletions

View file

@ -1,5 +0,0 @@
root = true
[*]
indent_style = tab
indent_size = 3

63
.gitattributes vendored
View file

@ -1,63 +0,0 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain

1
.github/FUNDING.yml vendored
View file

@ -1 +0,0 @@
patreon: Mesen

View file

@ -1,31 +0,0 @@
name: github pages
on:
push:
branches:
- master # Set a branch to deploy
jobs:
deploy:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
with:
submodules: true # Fetch Hugo themes (true OR recursive)
fetch-depth: 0 # Fetch all history for .GitInfo and .Lastmod
- name: Setup Hugo
uses: peaceiris/actions-hugo@v2
with:
hugo-version: 'latest'
# extended: true
- name: Build
run: cd Docs && hugo --minify
- name: Deploy 🚀
uses: JamesIves/github-pages-deploy-action@4.1.6
if: github.ref == 'refs/heads/master'
with:
branch: gh-pages # The branch the action should deploy to.
folder: Docs/docs # The folder the action should deploy.

View file

@ -1,28 +0,0 @@
name: "Linux build"
on:
push:
workflow_dispatch:
jobs:
linux-build:
runs-on: ubuntu-latest
container: ubuntu:bionic
steps:
- uses: actions/checkout@v2
- name: Set up dependencies
run: |
apt-get update
apt-get install -yq --no-install-recommends gnupg ca-certificates
apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
echo "deb https://download.mono-project.com/repo/ubuntu stable-bionic main" | tee /etc/apt/sources.list.d/mono-official-stable.list
apt-get update
apt-get install -yq --no-install-recommends zip unzip clang mono-devel libsdl2-dev libsdl2-2.0 gnome-themes-standard xvfb x11-apps
apt-get clean && rm -rf /var/cache/apt/lists/*
- name: Build
run: |
make -j$(nproc)
- name: Upload binary
uses: actions/upload-artifact@v1
with:
name: Mesen-Linux
path: bin/x64/Release/Mesen.exe

View file

@ -1,27 +0,0 @@
name: "Windows build"
on: push
jobs:
win-build:
runs-on: windows-2019
steps:
- uses: actions/checkout@v2
- name: Set up dependencies
shell: bash
run: |
mkdir -p "bin/Any CPU/Release"
cp -v -r GUI.NET/Dependencies "bin/Any CPU/Release"
git describe --tags --dirty --always >"bin\Any CPU\Release\Dependencies\DevBuild.txt"
- name: Build core
working-directory: bin
shell: cmd
run: |
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
msbuild ..\Mesen.sln /t:Build /p:Configuration=Release /p:Platform=x64
copy "x64\Release\MesenCore.dll" "Any CPU\Release\Dependencies\MesenCore.x64.dll"
msbuild ..\Mesen.sln /t:Build /p:Configuration=Release /p:Platform="Any CPU" /property:DefineConstants="HIDETESTMENU;AUTOBUILD"
- name: Upload binary
uses: actions/upload-artifact@v1
with:
name: Mesen-win
path: bin/Any CPU/Release/Mesen.exe

183
.gitignore vendored
View file

@ -1,183 +0,0 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.sln.docstates
.vs/*
# Build results
[Dd]ebug/
[Rr]elease/
x64/
build/
[Bb]in/
[Oo]bj/
[Oo]bj.x86/
[Oo]bj.x64/
# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
!packages/*/build/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
*_i.c
*_p.c
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.log
*.scc
# Libretro build files
*.o
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
*.ncrunch*
.*crunch*.local.xml
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.Publish.xml
# NuGet Packages Directory
## TODO: If you have NuGet Package Restore enabled, uncomment the next line
#packages/
# Windows Azure Build Output
csx
*.build.csdef
# Windows Store app package directory
AppPackages/
# Others
sql/
*.Cache
ClientBin/
[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
*.[Pp]ublish.xml
*.pfx
*.publishsettings
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file to a newer
# Visual Studio version. Backup files are not needed, because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
App_Data/*.mdf
App_Data/*.ldf
#LightSwitch generated files
GeneratedArtifacts/
_Pvt_Extensions/
ModelManifest.xml
# =========================
# Windows detritus
# =========================
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Mac desktop service store files
.DS_Store
*.nes
*.sav
*.svs
*.trt
*.rar
*.VC.opendb
*.VC.db
*.VC.db-wal
*.VC.db-shm
Docs/docs/
Docs/*.exe
PGOHelper/PGOMesenHome
*.profraw
*.profdata
packages/*
!Libretro/hakchi/bin
Docs/docs.zip

35
404.html Normal file
View file

@ -0,0 +1,35 @@
<!doctype html><html lang=en class="js csstransforms3d">
<head>
<meta charset=utf-8> <meta name=description content>
<link rel="shortcut icon" href=./images/favicon.png type=image/x-icon>
<link rel=icon href=./images/favicon.png type=image/x-icon>
<title>404 Page not found</title>
<link href=./css/nucleus.css?1637878444 rel=stylesheet>
<link href=./css/font-awesome.min.css?1637878444 rel=stylesheet>
<link href=./css/hybrid.css?1637878444 rel=stylesheet>
<link href=./css/featherlight.min.css?1637878444 rel=stylesheet>
<link href=./css/perfect-scrollbar.min.css?1637878444 rel=stylesheet>
<link href=./css/horsey.css?1637878444 rel=stylesheet>
<link href=./css/theme.css?1637878444 rel=stylesheet>
<link href=./css/hugo-theme.css?1637878444 rel=stylesheet>
<link href=./css/theme-green.css?1637878444 rel=stylesheet>
<style type=text/css>:root #header+#content>#left>#rlblock_left{display:none!important}p,li,ul{text-align:center}ul{list-style-type:none}</style>
</head>
<body>
<body data-url=./>
<section id=body style=margin-left:0>
<div id=overlay></div>
<div id=chapter>
<div id=body-inner>
<h1>Error</h1>
<p>
</p>
<p>Woops. Looks like this page doesn't exist ¯\_(ツ)_/¯.</p>
<p></p>
<p><a href>Go to homepage</a></p>
<p><img src=./images/gopher-404.jpg style=width:50%></img></p>
</div>
</div>
</section>
</body>
</html>

View file

@ -1,41 +0,0 @@
### Windows
#### *Standalone*
1) Open the solution in Visual Studio 2019
2) Set "GUI.NET" as the Startup Project
3) Compile as Release/x64 or Release/x86
4) Run the project from Visual Studio
5) If you got an error, try running it a second time
Note: When loading the the solution in Visual Studio make sure all the projects are loaded successfully.
Note: If you get an error about the project targeted .NET Framework 4.5 chose to download the missing packages then install ".NET Framework 4.5 targeting pack" from the Visual Studio Installer.
Note: If you get a "compiler is out of heap space" error when building the project, then try running `set PreferredToolArchitecture=x64` and `devenv` in "x64 Native Tools Command Prompt for VS 2019"
#### *Libretro*
1) Open the solution in Visual Studio 2019
2) Compile as Libretro/x64 or Libretro/x86
3) Use the "mesen_libretro.dll" file in bin/(x64 or x86)/Libretro/mesen_libretro.dll
Note: It's also possible to build the Libretro core via MINGW by using the makefile in the Libretro subfolder.
### Linux
#### *Standalone*
To compile Mesen under Linux you will need clang 7.0+ or gcc 9.0+ (Mesen requires a C++17 compiler with support for the filesystem API.) Additionally, Mesen has the following dependencies:
* Mono 5.18+ (package: mono-devel)
* SDL2 (package: libsdl2-dev)
**Note:** **Mono 5.18 or higher is recommended**, some older versions of Mono (e.g 4.2.2) have some stability and performance issues which can cause crashes and slow down the UI.
The default Mono version in Ubuntu 18.04 is 4.6.2 (which also causes some layout issues in Mesen). To install the latest version of Mono, follow the instructions here: https://www.mono-project.com/download/stable/#download-lin
The makefile contains some more information at the top. Running "make" will build the x64 version by default, and then "make run" should start the emulator.
LTO is supported under clang, which gives a large performance boost (25-30%+), so turning it on is highly recommended (see makefile for details).
#### *Libretro*
To compile the Libretro core you will need a version of clang/gcc that supports C++14.
Run "make" from the "Libretro" subfolder to build the Libretro core.

View file

@ -1,55 +0,0 @@
#pragma once
#include "stdafx.h"
#include "PPU.h"
#include "Snapshotable.h"
enum class A12StateChange
{
None = 0,
Rise = 1,
Fall = 2
};
class A12Watcher : public Snapshotable
{
private:
uint32_t _lastCycle = 0;
uint32_t _cyclesDown = 0;
public:
void StreamState(bool saving) override
{
Stream(_lastCycle, _cyclesDown);
}
template<uint8_t minDelay = 10>
A12StateChange UpdateVramAddress(uint16_t addr, uint32_t frameCycle)
{
A12StateChange result = A12StateChange::None;
if(_cyclesDown > 0) {
if(_lastCycle > frameCycle) {
//We changed frames
_cyclesDown += (89342 - _lastCycle) + frameCycle;
} else {
_cyclesDown += (frameCycle - _lastCycle);
}
}
if((addr & 0x1000) == 0) {
if(_cyclesDown == 0) {
_cyclesDown = 1;
result = A12StateChange::Fall;
}
} else if(addr & 0x1000) {
if(_cyclesDown > minDelay) {
result = A12StateChange::Rise;
}
_cyclesDown = 0;
}
_lastCycle = frameCycle;
return result;
}
};

View file

@ -1,32 +0,0 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class A65AS : public BaseMapper
{
protected:
uint16_t GetPRGPageSize() override { return 0x4000; }
uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
SelectCHRPage(0, 0);
WriteRegister(0x8000, 0);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if(value & 0x40) {
SelectPrgPage2x(0, value & 0x1E);
} else {
SelectPRGPage(0, ((value & 0x30) >> 1) | (value & 0x07));
SelectPRGPage(1, ((value & 0x30) >> 1) | 0x07);
}
if(value & 0x80) {
SetMirroringType(value & 0x20 ? MirroringType::ScreenBOnly : MirroringType::ScreenAOnly);
} else {
SetMirroringType(value & 0x08 ? MirroringType::Horizontal : MirroringType::Vertical);
}
}
};

View file

@ -1,289 +0,0 @@
#include "stdafx.h"
#include "APU.h"
#include "CPU.h"
#include "SquareChannel.h"
#include "TriangleChannel.h"
#include "NoiseChannel.h"
#include "DeltaModulationChannel.h"
#include "ApuFrameCounter.h"
#include "EmulationSettings.h"
#include "SoundMixer.h"
#include "MemoryManager.h"
APU::APU(shared_ptr<Console> console)
{
_nesModel = NesModel::Auto;
_apuEnabled = true;
_needToRun = false;
_console = console;
_mixer = _console->GetSoundMixer();
_settings = _console->GetSettings();
_squareChannel[0].reset(new SquareChannel(AudioChannel::Square1, _console, _mixer.get(), true));
_squareChannel[1].reset(new SquareChannel(AudioChannel::Square2, _console, _mixer.get(), false));
_triangleChannel.reset(new TriangleChannel(AudioChannel::Triangle, _console, _mixer.get()));
_noiseChannel.reset(new NoiseChannel(AudioChannel::Noise, _console, _mixer.get()));
_deltaModulationChannel.reset(new DeltaModulationChannel(AudioChannel::DMC, _console, _mixer.get()));
_frameCounter.reset(new ApuFrameCounter(_console));
_console->GetMemoryManager()->RegisterIODevice(_squareChannel[0].get());
_console->GetMemoryManager()->RegisterIODevice(_squareChannel[1].get());
_console->GetMemoryManager()->RegisterIODevice(_frameCounter.get());
_console->GetMemoryManager()->RegisterIODevice(_triangleChannel.get());
_console->GetMemoryManager()->RegisterIODevice(_noiseChannel.get());
_console->GetMemoryManager()->RegisterIODevice(_deltaModulationChannel.get());
Reset(false);
}
APU::~APU()
{
}
void APU::SetNesModel(NesModel model, bool forceInit)
{
if(_nesModel != model || forceInit) {
//Finish the current apu frame before switching model
Run();
_nesModel = model;
_squareChannel[0]->SetNesModel(model);
_squareChannel[1]->SetNesModel(model);
_triangleChannel->SetNesModel(model);
_noiseChannel->SetNesModel(model);
_deltaModulationChannel->SetNesModel(model);
_frameCounter->SetNesModel(model);
_mixer->SetNesModel(model);
}
}
void APU::FrameCounterTick(FrameType type)
{
//Quarter & half frame clock envelope & linear counter
_squareChannel[0]->TickEnvelope();
_squareChannel[1]->TickEnvelope();
_triangleChannel->TickLinearCounter();
_noiseChannel->TickEnvelope();
if(type == FrameType::HalfFrame) {
//Half frames clock length counter & sweep
_squareChannel[0]->TickLengthCounter();
_squareChannel[1]->TickLengthCounter();
_triangleChannel->TickLengthCounter();
_noiseChannel->TickLengthCounter();
_squareChannel[0]->TickSweep();
_squareChannel[1]->TickSweep();
}
}
uint8_t APU::GetStatus()
{
uint8_t status = 0;
status |= _squareChannel[0]->GetStatus() ? 0x01 : 0x00;
status |= _squareChannel[1]->GetStatus() ? 0x02 : 0x00;
status |= _triangleChannel->GetStatus() ? 0x04 : 0x00;
status |= _noiseChannel->GetStatus() ? 0x08 : 0x00;
status |= _deltaModulationChannel->GetStatus() ? 0x10 : 0x00;
status |= _console->GetCpu()->HasIrqSource(IRQSource::FrameCounter) ? 0x40 : 0x00;
status |= _console->GetCpu()->HasIrqSource(IRQSource::DMC) ? 0x80 : 0x00;
return status;
}
uint8_t APU::ReadRAM(uint16_t addr)
{
//$4015 read
Run();
uint8_t status = GetStatus();
//Reading $4015 clears the Frame Counter interrupt flag.
_console->GetCpu()->ClearIrqSource(IRQSource::FrameCounter);
return status;
}
uint8_t APU::PeekRAM(uint16_t addr)
{
if(_console->GetEmulationThreadId() == std::this_thread::get_id()) {
//Only run the APU (to catch up) if we're running this in the emulation thread (not 100% accurate, but we can't run the APU from any other thread without locking)
Run();
}
return GetStatus();
}
void APU::WriteRAM(uint16_t addr, uint8_t value)
{
//$4015 write
Run();
//Writing to $4015 clears the DMC interrupt flag.
//This needs to be done before setting the enabled flag for the DMC (because doing so can trigger an IRQ)
_console->GetCpu()->ClearIrqSource(IRQSource::DMC);
_squareChannel[0]->SetEnabled((value & 0x01) == 0x01);
_squareChannel[1]->SetEnabled((value & 0x02) == 0x02);
_triangleChannel->SetEnabled((value & 0x04) == 0x04);
_noiseChannel->SetEnabled((value & 0x08) == 0x08);
_deltaModulationChannel->SetEnabled((value & 0x10) == 0x10);
}
void APU::GetMemoryRanges(MemoryRanges &ranges)
{
ranges.AddHandler(MemoryOperation::Read, 0x4015);
ranges.AddHandler(MemoryOperation::Write, 0x4015);
}
void APU::Run()
{
//Update framecounter and all channels
//This is called:
//-At the end of a frame
//-Before APU registers are read/written to
//-When a DMC or FrameCounter interrupt needs to be fired
int32_t cyclesToRun = _currentCycle - _previousCycle;
while(cyclesToRun > 0) {
_previousCycle += _frameCounter->Run(cyclesToRun);
//Reload counters set by writes to 4003/4008/400B/400F after running the frame counter to allow the length counter to be clocked first
//This fixes the test "len_reload_timing" (tests 4 & 5)
_squareChannel[0]->ReloadCounter();
_squareChannel[1]->ReloadCounter();
_noiseChannel->ReloadCounter();
_triangleChannel->ReloadCounter();
_squareChannel[0]->Run(_previousCycle);
_squareChannel[1]->Run(_previousCycle);
_noiseChannel->Run(_previousCycle);
_triangleChannel->Run(_previousCycle);
_deltaModulationChannel->Run(_previousCycle);
}
}
void APU::SetNeedToRun()
{
_needToRun = true;
}
bool APU::NeedToRun(uint32_t currentCycle)
{
if(_deltaModulationChannel->NeedToRun() || _needToRun) {
//Need to run whenever we alter the length counters
//Need to run every cycle when DMC is running to get accurate emulation (CPU stalling, interaction with sprite DMA, etc.)
_needToRun = false;
return true;
}
uint32_t cyclesToRun = currentCycle - _previousCycle;
return _frameCounter->NeedToRun(cyclesToRun) || _deltaModulationChannel->IrqPending(cyclesToRun);
}
void APU::Exec()
{
_currentCycle++;
if(_currentCycle == SoundMixer::CycleLength - 1) {
EndFrame();
} else if(NeedToRun(_currentCycle)) {
Run();
}
}
void APU::EndFrame()
{
Run();
_squareChannel[0]->EndFrame();
_squareChannel[1]->EndFrame();
_triangleChannel->EndFrame();
_noiseChannel->EndFrame();
_deltaModulationChannel->EndFrame();
_mixer->PlayAudioBuffer(_currentCycle);
_currentCycle = 0;
_previousCycle = 0;
}
void APU::ProcessCpuClock()
{
if(_apuEnabled) {
Exec();
}
}
void APU::Reset(bool softReset)
{
_apuEnabled = true;
_currentCycle = 0;
_previousCycle = 0;
_squareChannel[0]->Reset(softReset);
_squareChannel[1]->Reset(softReset);
_triangleChannel->Reset(softReset);
_noiseChannel->Reset(softReset);
_deltaModulationChannel->Reset(softReset);
_frameCounter->Reset(softReset);
}
void APU::StreamState(bool saving)
{
if(saving) {
//End the APU frame - makes it simpler to restore sound after a state reload
EndFrame();
} else {
_previousCycle = 0;
_currentCycle = 0;
}
SnapshotInfo squareChannel0{ _squareChannel[0].get() };
SnapshotInfo squareChannel1{ _squareChannel[1].get() };
SnapshotInfo triangleChannel{ _triangleChannel.get() };
SnapshotInfo noiseChannel{ _noiseChannel.get() };
SnapshotInfo deltaModulationChannel{ _deltaModulationChannel.get() };
SnapshotInfo frameCounter{ _frameCounter.get() };
SnapshotInfo mixer{ _mixer.get() };
Stream(_nesModel, squareChannel0, squareChannel1, triangleChannel, noiseChannel, deltaModulationChannel, frameCounter, mixer);
}
void APU::AddExpansionAudioDelta(AudioChannel channel, int16_t delta)
{
_mixer->AddDelta(channel, _currentCycle, delta);
}
void APU::SetApuStatus(bool enabled)
{
_apuEnabled = enabled;
}
bool APU::IsApuEnabled()
{
//Adding extra lines before/after NMI temporarely turns off the APU
//This appears to result in less side-effects than spreading out the APU's
//load over the entire PPU frame, like what was done before.
//This is most likely due to the timing of the Frame Counter & DMC IRQs.
return _apuEnabled;
}
uint16_t APU::GetDmcReadAddress()
{
return _deltaModulationChannel->GetDmcReadAddress();
}
void APU::SetDmcReadBuffer(uint8_t value)
{
_deltaModulationChannel->SetDmcReadBuffer(value);
}
ApuState APU::GetState()
{
ApuState state;
state.Dmc = _deltaModulationChannel->GetState();
state.FrameCounter = _frameCounter->GetState();
state.Noise = _noiseChannel->GetState();
state.Square1 = _squareChannel[0]->GetState();
state.Square2 = _squareChannel[1]->GetState();
state.Triangle = _triangleChannel->GetState();
return state;
}

View file

@ -1,77 +0,0 @@
#pragma once
#include "stdafx.h"
#include "IMemoryHandler.h"
#include "IAudioDevice.h"
#include "Snapshotable.h"
#include "EmulationSettings.h"
class Console;
class SquareChannel;
class TriangleChannel;
class NoiseChannel;
class DeltaModulationChannel;
class ApuFrameCounter;
class SoundMixer;
class Debugger;
enum class FrameType;
enum class NesModel;
class APU : public Snapshotable, public IMemoryHandler
{
friend ApuFrameCounter;
private:
bool _apuEnabled;
bool _needToRun;
uint32_t _previousCycle;
uint32_t _currentCycle;
unique_ptr<SquareChannel> _squareChannel[2];
unique_ptr<TriangleChannel> _triangleChannel;
unique_ptr<NoiseChannel> _noiseChannel;
unique_ptr<DeltaModulationChannel> _deltaModulationChannel;
unique_ptr<ApuFrameCounter> _frameCounter;
shared_ptr<Console> _console;
shared_ptr<SoundMixer> _mixer;
EmulationSettings* _settings;
NesModel _nesModel;
private:
__forceinline bool NeedToRun(uint32_t currentCycle);
void FrameCounterTick(FrameType type);
uint8_t GetStatus();
protected:
void StreamState(bool saving) override;
public:
APU(shared_ptr<Console> console);
~APU();
void Reset(bool softReset);
void SetNesModel(NesModel model, bool forceInit = false);
uint8_t ReadRAM(uint16_t addr) override;
uint8_t PeekRAM(uint16_t addr) override;
void WriteRAM(uint16_t addr, uint8_t value) override;
void GetMemoryRanges(MemoryRanges &ranges) override;
ApuState GetState();
void Exec();
void ProcessCpuClock();
void Run();
void EndFrame();
void AddExpansionAudioDelta(AudioChannel channel, int16_t delta);
void SetApuStatus(bool enabled);
bool IsApuEnabled();
uint16_t GetDmcReadAddress();
void SetDmcReadBuffer(uint8_t value);
void SetNeedToRun();
};

View file

@ -1,25 +0,0 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class AXROM : public BaseMapper
{
protected:
virtual uint16_t GetPRGPageSize() override { return 0x8000; }
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
SelectCHRPage(0, 0);
WriteRegister(0, GetPowerOnByte());
}
bool HasBusConflicts() override { return _romInfo.SubMapperID == 2; }
void WriteRegister(uint16_t addr, uint8_t value) override
{
SelectPRGPage(0, value & 0x0F);
SetMirroringType(((value & 0x10) == 0x10) ? MirroringType::ScreenBOnly : MirroringType::ScreenAOnly);
}
};

View file

@ -1,54 +0,0 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class Ac08 : public BaseMapper
{
private:
uint8_t _reg;
protected:
uint16_t GetPRGPageSize() override { return 0x2000; }
uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
AddRegisterRange(0x4025, 0x4025, MemoryOperation::Write);
_reg = 0;
SelectPrgPage4x(0, -4);
SelectCHRPage(0, 0);
UpdateState();
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_reg);
if(!saving) {
UpdateState();
}
}
void UpdateState()
{
SetCpuMemoryMapping(0x6000, 0x7FFF, _reg, PrgMemoryType::PrgRom);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if(addr == 0x4025) {
SetMirroringType(value & 0x08 ? MirroringType::Horizontal : MirroringType::Vertical);
} else {
if(addr == 0x8001) {
//Green beret
_reg = (value >> 1) & 0x0F;
} else {
//Castlevania?
_reg = value & 0x0F;
}
UpdateState();
}
}
};

View file

@ -1,91 +0,0 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class Action53 : public BaseMapper
{
private:
uint8_t _selectedReg;
uint8_t _regs[4];
uint8_t _mirroringBit;
protected:
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
_selectedReg = 0;
_mirroringBit = 0;
memset(_regs, 0, sizeof(_regs));
AddRegisterRange(0x5000, 0x5FFF, MemoryOperation::Write);
SelectPRGPage(1, -1);
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
ArrayInfo<uint8_t> regs{ _regs,4 };
Stream(_selectedReg, _mirroringBit, regs);
}
void UpdateState()
{
uint8_t mirroring = _regs[2] & 0x03;
if(!(mirroring & 0x02)) {
mirroring = _mirroringBit;
}
switch(mirroring) {
case 0: SetMirroringType(MirroringType::ScreenAOnly); break;
case 1: SetMirroringType(MirroringType::ScreenBOnly); break;
case 2: SetMirroringType(MirroringType::Vertical); break;
case 3: SetMirroringType(MirroringType::Horizontal); break;
}
uint8_t gameSize = (_regs[2] & 0x30) >> 4;
uint8_t prgSize = (_regs[2] & 0x08) >> 3;
uint8_t slotSelect = (_regs[2] & 0x04) >> 2;
uint8_t chrSelect = _regs[0] & 0x03;
uint8_t prgSelect = _regs[1] & 0x0F;
uint16_t outerPrgSelect = _regs[3] << 1;
SelectCHRPage(0, chrSelect);
if(prgSize) {
uint8_t bank = (slotSelect ? 0 : 1);
switch(gameSize) {
case 0: SelectPRGPage(bank, (outerPrgSelect & 0x1FE) | (prgSelect & 0x01)); break;
case 1: SelectPRGPage(bank, (outerPrgSelect & 0x1FC) | (prgSelect & 0x03)); break;
case 2: SelectPRGPage(bank, (outerPrgSelect & 0x1F8) | (prgSelect & 0x07)); break;
case 3: SelectPRGPage(bank, (outerPrgSelect & 0x1F0) | (prgSelect & 0x0F)); break;
}
SelectPRGPage(slotSelect ? 1 : 0, (outerPrgSelect & 0x1FE) | slotSelect);
} else {
prgSelect <<= 1;
uint16_t outerAnd[4]{ 0x1FE, 0x1FC, 0x1F8, 0x1F0 };
uint8_t innerAnd[4]{ 0x01, 0x03, 0x07, 0x0F };
SelectPRGPage(0, (outerPrgSelect & outerAnd[gameSize]) | (prgSelect & innerAnd[gameSize]));
SelectPRGPage(1, (outerPrgSelect & outerAnd[gameSize]) | ((prgSelect | 0x01) & innerAnd[gameSize]));
}
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if(addr <= 0x5FFF) {
_selectedReg = ((value & 0x80) >> 6) | (value & 0x01);
} else if(addr >= 0x8000) {
if(_selectedReg <= 1) {
_mirroringBit = (value >> 4) & 0x01;
} else if(_selectedReg == 2) {
_mirroringBit = (value & 0x01);
}
_regs[_selectedReg] = value;
UpdateState();
}
}
};

View file

@ -1,43 +0,0 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class ActionEnterprises : public BaseMapper
{
protected:
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
WriteRegister(0x8000, 0);
}
virtual void Reset(bool softReset) override
{
WriteRegister(0x8000, 0);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
uint8_t chipSelect = (addr >> 11) & 0x03;
if(chipSelect == 3) {
chipSelect = 2;
}
uint8_t prgPage = ((addr >> 6) & 0x1F) | (chipSelect << 5);
if(addr & 0x20) {
SelectPRGPage(0, prgPage);
SelectPRGPage(1, prgPage);
} else {
SelectPRGPage(0, prgPage & 0xFE);
SelectPRGPage(1, (prgPage & 0xFE) + 1);
}
SelectCHRPage(0, ((addr & 0x0F) << 2) | (value & 0x03));
SetMirroringType(addr & 0x2000 ? MirroringType::Horizontal : MirroringType::Vertical);
}
};

View file

@ -1,97 +0,0 @@
#pragma once
#include "stdafx.h"
#include "ApuLengthCounter.h"
#include "Console.h"
class ApuEnvelope : public ApuLengthCounter
{
private:
bool _constantVolume = false;
uint8_t _volume = 0;
uint8_t _envelopeCounter = 0;
bool _start = false;
int8_t _divider = 0;
uint8_t _counter = 0;
protected:
ApuEnvelope(AudioChannel channel, shared_ptr<Console> console, SoundMixer* mixer) : ApuLengthCounter(channel, console, mixer)
{
}
void InitializeEnvelope(uint8_t regValue)
{
_constantVolume = (regValue & 0x10) == 0x10;
_volume = regValue & 0x0F;
}
void ResetEnvelope()
{
_start = true;
}
uint32_t GetVolume()
{
if(_lengthCounter > 0) {
if(_constantVolume) {
return _volume;
} else {
return _counter;
}
} else {
return 0;
}
}
public:
virtual void Reset(bool softReset) override
{
ApuLengthCounter::Reset(softReset);
_constantVolume = false;
_volume = 0;
_envelopeCounter = 0;
_start = false;
_divider = 0;
_counter = 0;
}
virtual void StreamState(bool saving) override
{
ApuLengthCounter::StreamState(saving);
Stream(_constantVolume, _volume, _envelopeCounter, _start, _divider, _counter);
}
void TickEnvelope()
{
if(!_start) {
_divider--;
if(_divider < 0) {
_divider = _volume;
if(_counter > 0) {
_counter--;
} else if(_lengthCounterHalt) {
_counter = 15;
}
}
} else {
_start = false;
_counter = 15;
_divider = _volume;
}
}
ApuEnvelopeState GetState()
{
ApuEnvelopeState state;
state.ConstantVolume = _constantVolume;
state.Counter = _counter;
state.Divider = _divider;
state.Loop = _lengthCounterHalt;
state.StartFlag = _start;
state.Volume = _volume;
return state;
}
};

View file

@ -1,208 +0,0 @@
#pragma once
#include "stdafx.h"
#include "IMemoryHandler.h"
#include "EmulationSettings.h"
enum class FrameType
{
None = 0,
QuarterFrame = 1,
HalfFrame = 2,
};
class ApuFrameCounter : public IMemoryHandler, public Snapshotable
{
private:
const int32_t _stepCyclesNtsc[2][6] = { { 7457, 14913, 22371, 29828, 29829, 29830},
{ 7457, 14913, 22371, 29829, 37281, 37282} };
const int32_t _stepCyclesPal[2][6] = { { 8313, 16627, 24939, 33252, 33253, 33254},
{ 8313, 16627, 24939, 33253, 41565, 41566} };
const FrameType _frameType[2][6] = { { FrameType::QuarterFrame, FrameType::HalfFrame, FrameType::QuarterFrame, FrameType::None, FrameType::HalfFrame, FrameType::None },
{ FrameType::QuarterFrame, FrameType::HalfFrame, FrameType::QuarterFrame, FrameType::None, FrameType::HalfFrame, FrameType::None } };
shared_ptr<Console> _console;
int32_t _stepCycles[2][6];
NesModel _nesModel;
int32_t _previousCycle;
uint32_t _currentStep;
uint32_t _stepMode; //0: 4-step mode, 1: 5-step mode
bool _inhibitIRQ;
uint8_t _blockFrameCounterTick;
int16_t _newValue;
int8_t _writeDelayCounter;
public:
ApuFrameCounter(shared_ptr<Console> console)
{
_console = console;
Reset(false);
}
void Reset(bool softReset)
{
_nesModel = NesModel::Auto;
_previousCycle = 0;
//"After reset: APU mode in $4017 was unchanged", so we need to keep whatever value _stepMode has for soft resets
if(!softReset) {
_stepMode = 0;
}
_currentStep = 0;
//"After reset or power-up, APU acts as if $4017 were written with $00 from 9 to 12 clocks before first instruction begins."
//This is emulated in the CPU::Reset function
//Reset acts as if $00 was written to $4017
_newValue = _stepMode ? 0x80 : 0x00;
_writeDelayCounter = 3;
_inhibitIRQ = false;
_blockFrameCounterTick = 0;
}
void StreamState(bool saving) override
{
Stream(_previousCycle, _currentStep, _stepMode, _inhibitIRQ, _nesModel, _blockFrameCounterTick, _writeDelayCounter, _newValue);
if(!saving) {
SetNesModel(_nesModel);
}
}
void SetNesModel(NesModel model)
{
if(_nesModel != model) {
_nesModel = model;
switch(model) {
case NesModel::Auto:
//Auto should never be set here
break;
case NesModel::NTSC:
case NesModel::Dendy:
memcpy(_stepCycles, _stepCyclesNtsc, sizeof(_stepCycles));
break;
case NesModel::PAL:
memcpy(_stepCycles, _stepCyclesPal, sizeof(_stepCycles));
break;
}
}
}
uint32_t Run(int32_t &cyclesToRun)
{
uint32_t cyclesRan;
if(_previousCycle + cyclesToRun >= _stepCycles[_stepMode][_currentStep]) {
if(!_inhibitIRQ && _stepMode == 0 && _currentStep >= 3) {
//Set irq on the last 3 cycles for 4-step mode
_console->GetCpu()->SetIrqSource(IRQSource::FrameCounter);
}
FrameType type = _frameType[_stepMode][_currentStep];
if(type != FrameType::None && !_blockFrameCounterTick) {
_console->GetApu()->FrameCounterTick(type);
//Do not allow writes to 4017 to clock the frame counter for the next cycle (i.e this odd cycle + the following even cycle)
_blockFrameCounterTick = 2;
}
if(_stepCycles[_stepMode][_currentStep] < _previousCycle) {
//This can happen when switching from PAL to NTSC, which can cause a freeze (endless loop in APU)
cyclesRan = 0;
} else {
cyclesRan = _stepCycles[_stepMode][_currentStep] - _previousCycle;
}
cyclesToRun -= cyclesRan;
_currentStep++;
if(_currentStep == 6) {
_currentStep = 0;
_previousCycle = 0;
} else {
_previousCycle += cyclesRan;
}
} else {
cyclesRan = cyclesToRun;
cyclesToRun = 0;
_previousCycle += cyclesRan;
}
if(_newValue >= 0) {
_writeDelayCounter--;
if(_writeDelayCounter == 0) {
//Apply new value after the appropriate number of cycles has elapsed
_stepMode = ((_newValue & 0x80) == 0x80) ? 1 : 0;
_writeDelayCounter = -1;
_currentStep = 0;
_previousCycle = 0;
_newValue = -1;
if(_stepMode && !_blockFrameCounterTick) {
//"Writing to $4017 with bit 7 set will immediately generate a clock for both the quarter frame and the half frame units, regardless of what the sequencer is doing."
_console->GetApu()->FrameCounterTick(FrameType::HalfFrame);
_blockFrameCounterTick = 2;
}
}
}
if(_blockFrameCounterTick > 0) {
_blockFrameCounterTick--;
}
return cyclesRan;
}
bool NeedToRun(uint32_t cyclesToRun)
{
//Run APU when:
// -A new value is pending
// -The "blockFrameCounterTick" process is running
// -We're at the before-last or last tick of the current step
return _newValue >= 0 || _blockFrameCounterTick > 0 || (_previousCycle + (int32_t)cyclesToRun >= _stepCycles[_stepMode][_currentStep] - 1);
}
void GetMemoryRanges(MemoryRanges &ranges) override
{
ranges.AddHandler(MemoryOperation::Write, 0x4017);
}
uint8_t ReadRAM(uint16_t addr) override
{
return 0;
}
void WriteRAM(uint16_t addr, uint8_t value) override
{
_console->GetApu()->Run();
_newValue = value;
//Reset sequence after $4017 is written to
if(_console->GetCpu()->GetCycleCount() & 0x01) {
//"If the write occurs between APU cycles, the effects occur 4 CPU cycles after the write cycle. "
_writeDelayCounter = 4;
} else {
//"If the write occurs during an APU cycle, the effects occur 3 CPU cycles after the $4017 write cycle"
_writeDelayCounter = 3;
}
_inhibitIRQ = (value & 0x40) == 0x40;
if(_inhibitIRQ) {
_console->GetCpu()->ClearIrqSource(IRQSource::FrameCounter);
}
}
ApuFrameCounterState GetState()
{
ApuFrameCounterState state;
state.IrqEnabled = !_inhibitIRQ;
state.SequencePosition = std::min<uint8_t>(_currentStep, _stepMode ? 5 : 4);
state.FiveStepMode = _stepMode == 1;
return state;
}
};

View file

@ -1,111 +0,0 @@
#pragma once
#include "stdafx.h"
#include "BaseApuChannel.h"
#include "Console.h"
#include "APU.h"
class ApuLengthCounter : public BaseApuChannel
{
private:
uint8_t _lcLookupTable[32] = { 10, 254, 20, 2, 40, 4, 80, 6, 160, 8, 60, 10, 14, 12, 26, 14, 12, 16, 24, 18, 48, 20, 96, 22, 192, 24, 72, 26, 16, 28, 32, 30 };
bool _newHaltValue;
protected:
bool _enabled = false;
bool _lengthCounterHalt;
uint8_t _lengthCounter;
uint8_t _lengthCounterReloadValue;
uint8_t _lengthCounterPreviousValue;
void InitializeLengthCounter(bool haltFlag)
{
_console->GetApu()->SetNeedToRun();
_newHaltValue = haltFlag;
}
void LoadLengthCounter(uint8_t value)
{
if(_enabled) {
_lengthCounterReloadValue = _lcLookupTable[value];
_lengthCounterPreviousValue = _lengthCounter;
_console->GetApu()->SetNeedToRun();
}
}
public:
ApuLengthCounter(AudioChannel channel, shared_ptr<Console> console, SoundMixer* mixer) : BaseApuChannel(channel, console, mixer)
{
}
virtual void Reset(bool softReset) override
{
BaseApuChannel::Reset(softReset);
if(softReset) {
_enabled = false;
if(GetChannel() != AudioChannel::Triangle) {
//"At reset, length counters should be enabled, triangle unaffected"
_lengthCounterHalt = false;
_lengthCounter = 0;
_newHaltValue = false;
_lengthCounterReloadValue = 0;
_lengthCounterPreviousValue = 0;
}
} else {
_enabled = false;
_lengthCounterHalt = false;
_lengthCounter = 0;
_newHaltValue = false;
_lengthCounterReloadValue = 0;
_lengthCounterPreviousValue = 0;
}
}
virtual void StreamState(bool saving) override
{
BaseApuChannel::StreamState(saving);
Stream(_enabled, _lengthCounterHalt, _newHaltValue, _lengthCounter, _lengthCounterPreviousValue, _lengthCounterReloadValue);
}
bool GetStatus() override
{
return _lengthCounter > 0;
}
void ReloadCounter()
{
if(_lengthCounterReloadValue) {
if(_lengthCounter == _lengthCounterPreviousValue) {
_lengthCounter = _lengthCounterReloadValue;
}
_lengthCounterReloadValue = 0;
}
_lengthCounterHalt = _newHaltValue;
}
void TickLengthCounter()
{
if(_lengthCounter > 0 && !_lengthCounterHalt) {
_lengthCounter--;
}
}
void SetEnabled(bool enabled)
{
if(!enabled) {
_lengthCounter = 0;
}
_enabled = enabled;
}
ApuLengthCounterState GetState()
{
ApuLengthCounterState state;
state.Counter = _lengthCounter;
state.Halt = _lengthCounterHalt;
state.ReloadValue = _lengthCounterReloadValue;
return state;
}
};

View file

@ -1,87 +0,0 @@
#pragma once
#include "stdafx.h"
#include "BaseControlDevice.h"
#include "ControlManager.h"
#include "PPU.h"
#include "IKeyManager.h"
#include "KeyManager.h"
class ArkanoidController : public BaseControlDevice
{
private:
uint32_t _currentValue = (0xF4 - 0x54) / 2;
uint32_t _stateBuffer = 0;
enum Buttons { Fire };
protected:
bool HasCoordinates() override { return true; }
string GetKeyNames() override
{
return "F";
}
void InternalSetStateFromInput() override
{
if(_console->GetSettings()->InputEnabled()) {
SetPressedState(Buttons::Fire, KeyManager::IsMouseButtonPressed(MouseButton::LeftButton));
SetMovement(KeyManager::GetMouseMovement(_console->GetSettings()->GetMouseSensitivity(MouseDevice::ArkanoidController)));
}
}
void StreamState(bool saving) override
{
BaseControlDevice::StreamState(saving);
Stream(_stateBuffer, _currentValue);
}
void RefreshStateBuffer() override
{
MouseMovement mov = GetMovement();
_currentValue += mov.dx;
if(_currentValue < 0x54) {
_currentValue = 0x54;
} else if(_currentValue > 0xF4) {
_currentValue = 0xF4;
}
_stateBuffer = _currentValue;
}
public:
ArkanoidController(shared_ptr<Console> console, uint8_t port) : BaseControlDevice(console, port)
{
}
uint8_t ReadRAM(uint16_t addr) override
{
uint8_t output = 0;
if(IsExpansionDevice()) {
if(addr == 0x4016) {
//Fire button is on port 1
if(IsPressed(ArkanoidController::Buttons::Fire)) {
output |= 0x02;
}
} else if(addr == 0x4017) {
//Serial data is on port 2
output |= ((~_stateBuffer) >> 6) & 0x02;
_stateBuffer <<= 1;
}
} else if(IsCurrentPort(addr)) {
output = ((~_stateBuffer) >> 3) & 0x10;
_stateBuffer <<= 1;
if(IsPressed(ArkanoidController::Buttons::Fire)) {
output |= 0x08;
}
}
return output;
}
void WriteRAM(uint16_t addr, uint8_t value) override
{
StrobeProcessWrite(value);
}
};

View file

@ -1,65 +0,0 @@
#pragma once
#include "stdafx.h"
#include "../Utilities/FolderUtilities.h"
#include "Console.h"
#include "BaseControlDevice.h"
#include "IBattery.h"
#include "BatteryManager.h"
class AsciiTurboFile : public BaseControlDevice, public IBattery
{
private:
static constexpr int FileSize = 0x2000;
static constexpr int BitCount = FileSize * 8;
uint8_t _lastWrite = 0;
uint16_t _position = 0;
uint8_t _data[AsciiTurboFile::FileSize];
protected:
void StreamState(bool saving) override
{
BaseControlDevice::StreamState(saving);
ArrayInfo<uint8_t> data{ _data, AsciiTurboFile::FileSize };
Stream(_position, _lastWrite, data);
}
public:
AsciiTurboFile(shared_ptr<Console> console) : BaseControlDevice(console, BaseControlDevice::ExpDevicePort)
{
_console->GetBatteryManager()->LoadBattery(".tf", _data, AsciiTurboFile::FileSize);
}
~AsciiTurboFile()
{
SaveBattery();
}
void SaveBattery() override
{
_console->GetBatteryManager()->SaveBattery(".tf", _data, AsciiTurboFile::FileSize);
}
uint8_t ReadRAM(uint16_t addr) override
{
if(addr == 0x4017) {
return ((_data[_position / 8] >> (_position % 8)) & 0x01) << 2;
}
return 0;
}
void WriteRAM(uint16_t addr, uint8_t value) override
{
if(!(value & 0x02)) {
_position = 0;
}
if(!(value & 0x04) && (_lastWrite & 0x04)) {
//Clock, perform write, increase position
_data[_position / 8] &= ~(1 << (_position % 8));
_data[_position / 8] |= (value & 0x01) << (_position % 8);
_position = (_position + 1) & (AsciiTurboFile::BitCount - 1);
}
_lastWrite = value;
}
};

View file

@ -1,462 +0,0 @@
#include "stdafx.h"
#include <regex>
#include <unordered_map>
#include "../Utilities/HexUtilities.h"
#include "../Utilities/StringUtilities.h"
#include "Assembler.h"
#include "CPU.h"
#include "DisassemblyInfo.h"
#include "LabelManager.h"
static const std::regex instRegex = std::regex("^\\s*([a-zA-Z]{3})[*]{0,1}[\\s]*(#%|#){0,1}([(]{0,1})[\\s]*([$]{0,1})([^,)(;:]*)[\\s]*((,x\\)|\\),y|,x|,y|\\)){0,1})\\s*(;*)(.*)", std::regex_constants::icase);
static const std::regex isCommentOrBlank = std::regex("^\\s*([;]+.*$|\\s*$)", std::regex_constants::icase);
static const std::regex labelRegex = std::regex("^\\s*([@_a-zA-Z][@_a-zA-Z0-9]*):(.*)", std::regex_constants::icase);
static const std::regex byteRegex = std::regex("^\\s*[.]byte\\s+((\\$[a-fA-F0-9]{1,2},)*)(\\$[a-fA-F0-9]{1,2})+\\s*(;*)(.*)$", std::regex_constants::icase);
static string opName[256] = {
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
"BRK", "ORA", "STP", "SLO", "NOP", "ORA", "ASL", "SLO", "PHP", "ORA", "ASL", "ANC", "NOP", "ORA", "ASL", "SLO", //0
"BPL", "ORA", "STP", "SLO", "NOP", "ORA", "ASL", "SLO", "CLC", "ORA", "NOP", "SLO", "NOP", "ORA", "ASL", "SLO", //1
"JSR", "AND", "STP", "RLA", "BIT", "AND", "ROL", "RLA", "PLP", "AND", "ROL", "ANC", "BIT", "AND", "ROL", "RLA", //2
"BMI", "AND", "STP", "RLA", "NOP", "AND", "ROL", "RLA", "SEC", "AND", "NOP", "RLA", "NOP", "AND", "ROL", "RLA", //3
"RTI", "EOR", "STP", "SRE", "NOP", "EOR", "LSR", "SRE", "PHA", "EOR", "LSR", "ALR", "JMP", "EOR", "LSR", "SRE", //4
"BVC", "EOR", "STP", "SRE", "NOP", "EOR", "LSR", "SRE", "CLI", "EOR", "NOP", "SRE", "NOP", "EOR", "LSR", "SRE", //5
"RTS", "ADC", "STP", "RRA", "NOP", "ADC", "ROR", "RRA", "PLA", "ADC", "ROR", "ARR", "JMP", "ADC", "ROR", "RRA", //6
"BVS", "ADC", "STP", "RRA", "NOP", "ADC", "ROR", "RRA", "SEI", "ADC", "NOP", "RRA", "NOP", "ADC", "ROR", "RRA", //7
"NOP", "STA", "NOP", "SAX", "STY", "STA", "STX", "SAX", "DEY", "NOP", "TXA", "XAA", "STY", "STA", "STX", "SAX", //8
"BCC", "STA", "STP", "AHX", "STY", "STA", "STX", "SAX", "TYA", "STA", "TXS", "TAS", "SHY", "STA", "SHX", "AXA", //9
"LDY", "LDA", "LDX", "LAX", "LDY", "LDA", "LDX", "LAX", "TAY", "LDA", "TAX", "LAX", "LDY", "LDA", "LDX", "LAX", //A
"BCS", "LDA", "STP", "LAX", "LDY", "LDA", "LDX", "LAX", "CLV", "LDA", "TSX", "LAS", "LDY", "LDA", "LDX", "LAX", //B
"CPY", "CMP", "NOP", "DCP", "CPY", "CMP", "DEC", "DCP", "INY", "CMP", "DEX", "AXS", "CPY", "CMP", "DEC", "DCP", //C
"BNE", "CMP", "STP", "DCP", "NOP", "CMP", "DEC", "DCP", "CLD", "CMP", "NOP", "DCP", "NOP", "CMP", "DEC", "DCP", //D
"CPX", "SBC", "NOP", "ISC", "CPX", "SBC", "INC", "ISC", "INX", "SBC", "NOP", "SBC", "CPX", "SBC", "INC", "ISC", //E
"BEQ", "SBC", "STP", "ISC", "NOP", "SBC", "INC", "ISC", "SED", "SBC", "NOP", "ISC", "NOP", "SBC", "INC", "ISC" //F
};
void Assembler::ProcessLine(string code, uint16_t &instructionAddress, vector<int16_t>& output, std::unordered_map<string, uint16_t> &labels, bool firstPass, std::unordered_map<string, uint16_t> &currentPassLabels)
{
//Remove extra spaces as part of processing
size_t offset = code.find_first_of(',', 0);
if(offset != string::npos) {
code.erase(std::remove(code.begin() + offset + 1, code.end(), ' '), code.end());
}
offset = code.find_first_of(')', 0);
if(offset != string::npos) {
code.erase(std::remove(code.begin() + offset + 1, code.end(), ' '), code.end());
}
//Determine if the line is blank, a comment, a label or code
std::smatch match;
if(std::regex_search(code, match, byteRegex)) {
vector<string> bytes = StringUtilities::Split(match.str(1) + match.str(3), ',');
for(string &byte : bytes) {
output.push_back((uint8_t)(HexUtilities::FromHex(byte.substr(1))));
instructionAddress++;
}
output.push_back(AssemblerSpecialCodes::EndOfLine);
} else if(std::regex_search(code, match, labelRegex)) {
string label = match.str(1);
string afterLabel = match.str(2);
if(currentPassLabels.find(match.str(1)) != currentPassLabels.end()) {
output.push_back(AssemblerSpecialCodes::LabelRedefinition);
} else {
labels[match.str(1)] = instructionAddress;
currentPassLabels[match.str(1)] = instructionAddress;
ProcessLine(afterLabel, instructionAddress, output, labels, firstPass, currentPassLabels);
}
return;
} else if(std::regex_search(code, match, isCommentOrBlank)) {
output.push_back(AssemblerSpecialCodes::EndOfLine);
return;
} else if(std::regex_search(code, match, instRegex) && match.size() > 1) {
LineData lineData;
AssemblerSpecialCodes result = GetLineData(match, lineData, labels, firstPass);
if(result == AssemblerSpecialCodes::OK) {
AssembleInstruction(lineData, instructionAddress, output, firstPass);
} else {
output.push_back(result);
}
} else {
output.push_back(AssemblerSpecialCodes::ParsingError);
}
}
AssemblerSpecialCodes Assembler::GetLineData(std::smatch match, LineData &lineData, std::unordered_map<string, uint16_t> &labels, bool firstPass)
{
bool isBinary = match.str(2).length() > 1 && match.str(2)[1] == '%'; //Immediate + binary: "#%"
lineData.OpCode = match.str(1);
lineData.IsImmediate = !match.str(2).empty();
lineData.IsHex = !match.str(4).empty();
lineData.HasComment = !match.str(8).empty();
lineData.OperandSuffix = match.str(6);
lineData.HasOpeningParenthesis = !match.str(3).empty();
std::transform(lineData.OperandSuffix.begin(), lineData.OperandSuffix.end(), lineData.OperandSuffix.begin(), ::toupper);
std::transform(lineData.OpCode.begin(), lineData.OpCode.end(), lineData.OpCode.begin(), ::toupper);
bool foundSpace = false;
for(char c : match.str(5)) {
if(c != ' ' && c != '\t') {
if(foundSpace) {
//can't have spaces in operands (except at the very end)
return AssemblerSpecialCodes::InvalidSpaces;
} else if(lineData.IsHex && !((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'))) {
//invalid hex
return AssemblerSpecialCodes::InvalidHex;
} else if(isBinary && c != '0' && c != '1') {
return AssemblerSpecialCodes::InvalidBinaryValue;
}
lineData.Operand.push_back(c);
} else {
foundSpace = true;
}
}
if(isBinary) {
//Convert the binary value to hex
if(lineData.Operand.size() == 0) {
return AssemblerSpecialCodes::MissingOperand;
} else if(lineData.Operand.size() <= 8) {
lineData.IsHex = true;
int value = 0;
for(size_t i = 0; i < lineData.Operand.size(); i++) {
value <<= 1;
value |= lineData.Operand[i] == '1' ? 1 : 0;
}
lineData.Operand = HexUtilities::ToHex(value, false);
} else {
return AssemblerSpecialCodes::OperandOutOfRange;
}
}
if(!lineData.HasComment && !match.str(9).empty()) {
//something is trailing at the end of the line, and it's not a comment
return AssemblerSpecialCodes::TrailingText;
}
if(!lineData.IsHex) {
bool allNumeric = true;
for(size_t i = 0; i < lineData.Operand.size(); i++) {
if(lineData.Operand[i] == '-' && i == 0 && lineData.Operand.size() > 1) {
//First char is a minus sign, and more characters follow, continue
continue;
}
if((lineData.Operand[i] < '0' || lineData.Operand[i] > '9')) {
allNumeric = false;
break;
}
}
if(allNumeric && !lineData.Operand.empty()) {
//Operand is not empty, and it only contains decimal values
lineData.IsDecimal = true;
} else {
lineData.IsDecimal = false;
}
}
return GetAddrModeAndOperandSize(lineData, labels, firstPass);
}
AssemblerSpecialCodes Assembler::GetAddrModeAndOperandSize(LineData &lineData, std::unordered_map<string, uint16_t> &labels, bool firstPass)
{
int opSize = 0;
bool invalid = false;
string operand = lineData.Operand;
if(lineData.IsHex) {
if(operand.size() == 0) {
return AssemblerSpecialCodes::MissingOperand;
} else if(operand.size() <= 2) {
opSize = 1;
} else if(operand.size() <= 4) {
opSize = 2;
} else {
return AssemblerSpecialCodes::OperandOutOfRange;
}
} else if(lineData.IsDecimal) {
int value = std::stoi(operand.c_str());
if(value < -32768) {
//< -32768 is invalid
return AssemblerSpecialCodes::OperandOutOfRange;
} else if(value < -128) {
//-32768 to -129 is 2 bytes
opSize = 2;
} else if(value <= 255) {
//-128 to 255 is 2 bytes
opSize = 1;
} else if(value <= 65535) {
//256 to 65535 is 2 bytes
opSize = 2;
} else {
//> 65535 is invalid
return AssemblerSpecialCodes::OperandOutOfRange;
}
} else if(!operand.empty()) {
//Check if the operand is a known label
auto findResult = labels.find(operand);
if(findResult != labels.end()) {
lineData.Operand = HexUtilities::ToHex((uint16_t)findResult->second);
lineData.IsHex = true;
opSize = 2;
} else if(operand.size() == 1 && (operand[0] == 'A' || operand[0] == 'a') && lineData.OperandSuffix.empty() && !lineData.IsHex && !lineData.IsImmediate && !lineData.HasOpeningParenthesis) {
//Allow optional "A" after AddrMode == Accumulator instructions
lineData.Mode = AddrMode::Acc;
opSize = 0;
} else {
int32_t addr = _labelManager->GetLabelRelativeAddress(operand);
if(addr >= 256) {
lineData.Operand = HexUtilities::ToHex((uint16_t)addr);
lineData.IsHex = true;
opSize = 2;
} else if(addr >= 0) {
lineData.Operand = HexUtilities::ToHex((uint8_t)addr);
lineData.IsHex = true;
opSize = 1;
} else {
if(firstPass) {
//First pass, we couldn't find a matching label, so it might be defined later on
//Pretend it exists for now
_needSecondPass = true;
lineData.Operand = "FFFF";
lineData.IsHex = true;
opSize = 2;
} else {
return AssemblerSpecialCodes::UnknownLabel;
}
}
}
} else {
//No operand
opSize = 0;
}
if(lineData.Mode == AddrMode::None) {
if(lineData.IsImmediate) {
if(lineData.HasOpeningParenthesis || opSize == 0) {
invalid = true;
} else if(opSize > 1) {
if(lineData.IsHex && HexUtilities::FromHex(operand) > 0xFF) {
//Can't be a 2-byte operand
invalid = true;
} else if(lineData.IsDecimal) {
int value = std::stoi(operand.c_str());
if(value < -128 || value > 255) {
//Can't be a 2-byte operand
invalid = true;
}
}
opSize = 1;
}
lineData.Mode = AddrMode::Imm; //or Rel
} else if(lineData.HasOpeningParenthesis) {
if(lineData.OperandSuffix.compare(")") == 0) {
opSize = 2;
lineData.Mode = AddrMode::Ind;
} else if(lineData.OperandSuffix.compare(",X)") == 0) {
opSize = 1;
lineData.Mode = AddrMode::IndX;
} else if(lineData.OperandSuffix.compare("),Y") == 0) {
opSize = 1;
lineData.Mode = AddrMode::IndY;
} else {
invalid = true;
}
} else {
if(lineData.OperandSuffix.compare(",X") == 0) {
if(opSize == 2) {
lineData.Mode = AddrMode::AbsX;
} else if(opSize == 1) {
//Sometimes zero page addressing is not available, even if the operand is in the zero page
lineData.Mode = IsOpModeAvailable(lineData.OpCode, AddrMode::ZeroX) ? AddrMode::ZeroX : AddrMode::AbsX;
} else {
invalid = true;
}
} else if(lineData.OperandSuffix.compare(",Y") == 0) {
if(opSize == 2) {
lineData.Mode = AddrMode::AbsY;
} else if(opSize == 1) {
//Sometimes zero page addressing is not available, even if the operand is in the zero page
lineData.Mode = IsOpModeAvailable(lineData.OpCode, AddrMode::ZeroY) ? AddrMode::ZeroY : AddrMode::AbsY;
} else {
invalid = true;
}
} else if(lineData.OperandSuffix.empty()) {
if(opSize == 0) {
lineData.Mode = AddrMode::Imp; //or Acc
} else if(opSize == 2) {
lineData.Mode = AddrMode::Abs;
} else if(opSize == 1) {
//Sometimes zero page addressing is not available, even if the operand is in the zero page
lineData.Mode = IsOpModeAvailable(lineData.OpCode, AddrMode::Zero) ? AddrMode::Zero : AddrMode::Abs;
} else {
invalid = true;
}
} else {
invalid = true;
}
}
}
if(lineData.Mode == AddrMode::None) {
invalid = true;
}
lineData.OperandSize = opSize;
return invalid ? AssemblerSpecialCodes::ParsingError : AssemblerSpecialCodes::OK;
}
bool Assembler::IsOpModeAvailable(string &opCode, AddrMode mode)
{
return _availableModesByOpName[opCode].find((int)mode) != _availableModesByOpName[opCode].end();
}
void Assembler::AssembleInstruction(LineData &lineData, uint16_t &instructionAddress, vector<int16_t>& output, bool firstPass)
{
bool foundMatch = false;
if(lineData.Mode == AddrMode::Imp && lineData.OpCode.compare("NOP") == 0) {
//NOP has multiple name+addressing type collisions, the "official" NOP is 0xEA
output.push_back(0xEA);
instructionAddress++;
foundMatch = true;
} else {
for(int i = 0; i < 256; i++) {
AddrMode opMode = DisassemblyInfo::OPMode[i];
if(lineData.OpCode.compare(opName[i]) == 0) {
bool modeMatch = opMode == lineData.Mode;
if(!modeMatch) {
if((lineData.Mode == AddrMode::Imp && opMode == AddrMode::Acc) ||
(lineData.Mode == AddrMode::IndY && opMode == AddrMode::IndYW) ||
(lineData.Mode == AddrMode::AbsY && opMode == AddrMode::AbsYW) ||
(lineData.Mode == AddrMode::AbsX && opMode == AddrMode::AbsXW)) {
modeMatch = true;
} else if((lineData.Mode == AddrMode::Abs && opMode == AddrMode::Rel) ||
(lineData.Mode == AddrMode::Imm && opMode == AddrMode::Rel)) {
if(lineData.OperandSize == 2) {
if(lineData.Mode == AddrMode::Imm) {
//Hardcoded jump values must be 1-byte
if(firstPass) {
//Pretend this is ok on first pass, we're just trying to find all labels
lineData.OperandSize = 1;
lineData.IsHex = true;
lineData.Operand = "0";
modeMatch = true;
} else {
output.push_back(AssemblerSpecialCodes::OutOfRangeJump);
return;
}
} else {
modeMatch = true;
//Convert "absolute" jump to a relative jump
int value = lineData.IsHex ? HexUtilities::FromHex(lineData.Operand) : std::stoi(lineData.Operand);
int16_t addressGap = value - (instructionAddress + 2);
if(addressGap > 127 || addressGap < -128) {
//Gap too long, can't jump that far
if(!firstPass) {
//Pretend this is ok on first pass, we're just trying to find all labels
output.push_back(AssemblerSpecialCodes::OutOfRangeJump);
return;
}
}
//Update data to match relative jump
lineData.OperandSize = 1;
lineData.IsHex = true;
lineData.Operand = HexUtilities::ToHex((uint8_t)addressGap);
}
} else {
//Accept 1-byte relative jumps
modeMatch = true;
}
}
}
if(modeMatch) {
output.push_back(i);
instructionAddress += (lineData.OperandSize + 1);
if(lineData.OperandSize == 1) {
int value = lineData.IsHex ? HexUtilities::FromHex(lineData.Operand) : std::stoi(lineData.Operand);
output.push_back(value & 0xFF);
} else if(lineData.OperandSize == 2) {
int value = lineData.IsHex ? HexUtilities::FromHex(lineData.Operand) : std::stoi(lineData.Operand);
output.push_back(value & 0xFF);
output.push_back((value >> 8) & 0xFF);
}
foundMatch = true;
break;
}
}
}
}
if(!foundMatch) {
output.push_back(AssemblerSpecialCodes::InvalidInstruction);
} else {
output.push_back(AssemblerSpecialCodes::EndOfLine);
}
}
Assembler::Assembler(shared_ptr<LabelManager> labelManager)
{
_labelManager = labelManager;
}
uint32_t Assembler::AssembleCode(string code, uint16_t startAddress, int16_t* assembledCode)
{
for(uint8_t i = 0; i < 255; i++) {
if(_availableModesByOpName.find(opName[i]) == _availableModesByOpName.end()) {
_availableModesByOpName[opName[i]] = std::unordered_set<int>();
}
_availableModesByOpName[opName[i]].emplace((int)DisassemblyInfo::OPMode[i]);
}
std::unordered_map<string, uint16_t> temporaryLabels;
std::unordered_map<string, uint16_t> currentPassLabels;
size_t i = 0;
vector<int16_t> output;
output.reserve(1000);
uint16_t originalStartAddr = startAddress;
vector<string> codeLines;
codeLines.reserve(100);
while(i < code.size()) {
size_t offset = code.find_first_of('\n', i);
string line;
if(offset != string::npos) {
line = code.substr(i, offset - i);
i = offset + 1;
} else {
line = code.substr(i);
i = code.size();
}
codeLines.push_back(line);
}
//Make 2 passes - first one to find all labels, second to assemble
_needSecondPass = false;
for(string &line : codeLines) {
ProcessLine(line, startAddress, output, temporaryLabels, true, currentPassLabels);
}
if(_needSecondPass) {
currentPassLabels.clear();
output.clear();
startAddress = originalStartAddr;
for(string &line : codeLines) {
ProcessLine(line, startAddress, output, temporaryLabels, false, currentPassLabels);
}
}
memcpy(assembledCode, output.data(), output.size() * sizeof(uint16_t));
return (uint32_t)output.size();
}

View file

@ -1,58 +0,0 @@
#pragma once
#include "stdafx.h"
#include <unordered_set>
#include <regex>
#include "CPU.h"
class LabelManager;
struct LineData
{
string OpCode;
string Operand;
string OperandSuffix;
AddrMode Mode = AddrMode::None;
int OperandSize = 0;
bool IsHex = false;
bool IsDecimal = false;
bool IsImmediate = false;
bool HasComment = false;
bool HasOpeningParenthesis = false;
};
enum AssemblerSpecialCodes
{
OK = 0,
EndOfLine = -1,
ParsingError = -2,
OutOfRangeJump = -3,
LabelRedefinition = -4,
MissingOperand = -5,
OperandOutOfRange = -6,
InvalidHex = -7,
InvalidSpaces = -8,
TrailingText = -9,
UnknownLabel = -10,
InvalidInstruction = -11,
InvalidBinaryValue = -12,
};
class Assembler
{
private:
std::unordered_map<string, std::unordered_set<int>> _availableModesByOpName;
bool _needSecondPass;
shared_ptr<LabelManager> _labelManager;
void ProcessLine(string code, uint16_t &instructionAddress, vector<int16_t>& output, std::unordered_map<string, uint16_t> &labels, bool firstPass, std::unordered_map<string, uint16_t> &currentPassLabels);
AssemblerSpecialCodes GetLineData(std::smatch match, LineData &lineData, std::unordered_map<string, uint16_t> &labels, bool firstPass);
AssemblerSpecialCodes GetAddrModeAndOperandSize(LineData &lineData, std::unordered_map<string, uint16_t> &labels, bool firstPass);
void AssembleInstruction(LineData &lineData, uint16_t &instructionAddress, vector<int16_t>& output, bool firstPass);
bool IsOpModeAvailable(string &opCode, AddrMode mode);
public:
Assembler(shared_ptr<LabelManager> labelManager);
uint32_t AssembleCode(string code, uint16_t startAddress, int16_t* assembledCode);
};

View file

@ -1,47 +0,0 @@
#include "stdafx.h"
#include "AutoSaveManager.h"
#include "Console.h"
#include "EmulationSettings.h"
#include "SaveStateManager.h"
AutoSaveManager::AutoSaveManager(shared_ptr<Console> console)
{
_stopThread = false;
_timer.Reset();
_autoSaveThread = std::thread([=]() {
bool showMessage = false;
double targetTime = (double)console->GetSettings()->GetAutoSaveDelay(showMessage) * 60 * 1000;
while(!_stopThread) {
uint32_t autoSaveDelay = console->GetSettings()->GetAutoSaveDelay(showMessage) * 60 * 1000;
if(autoSaveDelay > 0) {
if(targetTime >= 0 && !console->IsExecutionStopped()) {
targetTime -= _timer.GetElapsedMS();
_timer.Reset();
if(targetTime <= 0) {
if(!console->IsDebuggerAttached()) {
console->GetSaveStateManager()->SaveState(_autoSaveSlot, showMessage);
}
targetTime = (double)console->GetSettings()->GetAutoSaveDelay(showMessage) * 60 * 1000;
_timer.Reset();
}
} else {
_timer.Reset();
}
} else {
_timer.Reset();
targetTime = autoSaveDelay;
}
if(!_stopThread) {
_signal.Wait(1000);
}
}
});
}
AutoSaveManager::~AutoSaveManager()
{
_stopThread = true;
_signal.Signal();
_autoSaveThread.join();
}

View file

@ -1,22 +0,0 @@
#pragma once
#include "stdafx.h"
#include <thread>
#include "../Utilities/Timer.h"
#include "../Utilities/AutoResetEvent.h"
class Console;
class AutoSaveManager
{
private:
const uint32_t _autoSaveSlot = 11;
std::thread _autoSaveThread;
atomic<bool> _stopThread;
AutoResetEvent _signal;
Timer _timer;
public:
AutoSaveManager(shared_ptr<Console> console);
~AutoSaveManager();
};

View file

@ -1,164 +0,0 @@
#include "stdafx.h"
#include "../Utilities/FolderUtilities.h"
#include "AutomaticRomTest.h"
#include "EmulationSettings.h"
#include "Console.h"
#include "PPU.h"
#include "VideoDecoder.h"
#include "StandardController.h"
#include "NotificationManager.h"
AutomaticRomTest::AutomaticRomTest()
{
_errorCode = 0;
}
AutomaticRomTest::~AutomaticRomTest()
{
}
void AutomaticRomTest::ProcessNotification(ConsoleNotificationType type, void* parameter)
{
if(type == ConsoleNotificationType::PpuFrameDone) {
uint16_t *frameBuffer = (uint16_t*)parameter;
if(_console->GetFrameCount() == 5) {
memcpy(_prevFrameBuffer, frameBuffer, sizeof(_prevFrameBuffer));
} else if(_console->GetFrameCount() == 300) {
if(memcmp(_prevFrameBuffer, frameBuffer, sizeof(_prevFrameBuffer)) == 0) {
//No change
_errorCode |= 0x20;
}
memcpy(_prevFrameBuffer, frameBuffer, sizeof(_prevFrameBuffer));
_console->GetVideoDecoder()->TakeScreenshot();
} else if(_console->GetFrameCount() == 900) {
if(memcmp(_prevFrameBuffer, frameBuffer, sizeof(_prevFrameBuffer)) == 0) {
//No change
_errorCode |= 0x01;
}
bool allZeros = true;
for(int i = 0; i < 256 * 240; i++) {
if(frameBuffer[i] != 0) {
allZeros = false;
break;
}
}
if(allZeros) {
_errorCode |= 0x04;
}
memcpy(_prevFrameBuffer, frameBuffer, sizeof(_prevFrameBuffer));
_console->GetVideoDecoder()->TakeScreenshot();
} else if(_console->GetFrameCount() == 1800) {
bool continueTest = false;
if(memcmp(_prevFrameBuffer, frameBuffer, sizeof(_prevFrameBuffer)) == 0) {
//No change, change input pattern and keep trying
continueTest = true;
}
bool allZeros = true;
for(int i = 0; i < 256 * 240; i++) {
if(frameBuffer[i] != 0) {
allZeros = false;
break;
}
}
if(allZeros) {
_errorCode |= 0x08;
}
_console->GetVideoDecoder()->TakeScreenshot();
if(!continueTest) {
//Stop test
_signal.Signal();
}
} else if(_console->GetFrameCount() == 3600) {
if(memcmp(_prevFrameBuffer, frameBuffer, sizeof(_prevFrameBuffer)) == 0) {
//No change
_errorCode |= 0x02;
}
bool allZeros = true;
for(int i = 0; i < 256 * 240; i++) {
if(frameBuffer[i] != 0) {
allZeros = false;
break;
}
}
if(allZeros) {
_errorCode |= 0x40;
}
_console->GetVideoDecoder()->TakeScreenshot();
//Stop test
_signal.Signal();
}
}
}
int32_t AutomaticRomTest::Run(string filename)
{
_console.reset(new Console());
EmulationSettings* settings = _console->GetSettings();
settings->SetMasterVolume(0);
_console->GetNotificationManager()->RegisterNotificationListener(shared_from_this());
if(_console->Initialize(filename)) {
_console->GetControlManager()->RegisterInputProvider(this);
settings->SetFlags(EmulationFlags::ForceMaxSpeed);
settings->ClearFlags(EmulationFlags::Paused);
_signal.Wait();
settings->SetFlags(EmulationFlags::Paused);
if(_console->GetFrameCount() < 1800) {
//Finished early
_errorCode |= 0x10;
}
settings->ClearFlags(EmulationFlags::ForceMaxSpeed);
settings->SetMasterVolume(1.0);
_console->GetControlManager()->UnregisterInputProvider(this);
_console->Stop();
return _errorCode;
}
return -1;
}
bool AutomaticRomTest::SetInput(BaseControlDevice* device)
{
if(device->GetPort() == 0) {
uint32_t frameNumber = _console->GetFrameCount();
ControlDeviceState state;
if(frameNumber <= 1800) {
if(frameNumber % 30 < 10) {
//Press 1 button for 10 frames every second
if((frameNumber / 30) % 8 != 1) {
state.State.push_back(1 << ((frameNumber / 60) % 8));
}
}
} else {
if(frameNumber % 30 < 10) {
if((frameNumber / 30) % 2) {
state.State.push_back(0x01);
} else {
state.State.push_back(0x08);
}
}
}
if(state.State.empty()) {
state.State.push_back(0);
}
device->SetRawState(state);
}
return true;
}

View file

@ -1,27 +0,0 @@
#pragma once
#include "stdafx.h"
#include "INotificationListener.h"
#include "IInputProvider.h"
#include "../Utilities/AutoResetEvent.h"
class Console;
class AutomaticRomTest : public INotificationListener, public IInputProvider, public std::enable_shared_from_this<AutomaticRomTest>
{
private:
shared_ptr<Console> _console;
AutoResetEvent _signal;
uint16_t _prevFrameBuffer[256 * 240];
uint32_t _errorCode;
public:
AutomaticRomTest();
virtual ~AutomaticRomTest();
void ProcessNotification(ConsoleNotificationType type, void* parameter) override;
int32_t Run(string filename);
// Inherited via IInputProvider
virtual bool SetInput(BaseControlDevice * device) override;
};

View file

@ -1,65 +0,0 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class Ax5705 : public BaseMapper
{
private:
uint8_t _chrReg[8];
protected:
uint16_t GetPRGPageSize() override { return 0x2000; }
uint16_t GetCHRPageSize() override { return 0x400; }
void InitMapper() override
{
memset(_chrReg, 0, sizeof(_chrReg));
SelectPRGPage(2, -2);
SelectPRGPage(3, -1);
for(int i = 0; i < 8; i++) {
SelectCHRPage(i, _chrReg[i]);
}
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
ArrayInfo<uint8_t> chrReg{ _chrReg, 8 };
Stream(chrReg);
}
void UpdateChrReg(int index, uint8_t value, bool low)
{
if(low) {
_chrReg[index] = (_chrReg[index] & 0xF0) | (value & 0x0F);
} else {
_chrReg[index] = (_chrReg[index] & 0x0F) | ((((value & 0x04) >> 1) | ((value & 0x02) << 1) | (value & 0x09)) << 4);
}
SelectCHRPage(index, _chrReg[index]);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if(addr >= 0xA008) {
bool low = (addr & 0x01) == 0x00;
switch(addr & 0xF00E) {
case 0xA008: UpdateChrReg(0, value, low); break;
case 0xA00A: UpdateChrReg(1, value, low); break;
case 0xC000: UpdateChrReg(2, value, low); break;
case 0xC002: UpdateChrReg(3, value, low); break;
case 0xC008: UpdateChrReg(4, value, low); break;
case 0xC00A: UpdateChrReg(5, value, low); break;
case 0xE000: UpdateChrReg(6, value, low); break;
case 0xE002: UpdateChrReg(7, value, low); break;
}
} else {
switch(addr & 0xF00F) {
case 0x8000: SelectPRGPage(0, ((value & 0x02) << 2) | ((value & 0x08) >> 2) | (value & 0x05)); break; // EPROM dump have mixed PRG and CHR banks, data lines to mapper seems to be mixed
case 0x8008: SetMirroringType(value & 0x01 ? MirroringType::Horizontal : MirroringType::Vertical); break;
case 0xA000: SelectPRGPage(1, ((value & 0x02) << 2) | ((value & 0x08) >> 2) | (value & 0x05)); break;
}
}
}
};

View file

@ -1,51 +0,0 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class BF9096 : public BaseMapper
{
private:
uint8_t _prgBlock;
uint8_t _prgPage;
protected:
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
_prgPage = 0;
_prgBlock = 0;
SelectPRGPage(0, 0);
SelectPRGPage(1, 3);
SelectCHRPage(0, 0);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if(addr >= 0xC000) {
_prgPage = value & 0x03;
} else if(addr < 0xC000) {
if(_romInfo.SubMapperID == 1) {
//"232: 1 Aladdin Deck Enhancer"
//"Aladdin Deck Enhancer variation.Swap the bits of the outer bank number."
//But this seems to match the Pegasus 4-in-1 behavior? Wiki wrong?
_prgBlock = ((value >> 4) & 0x01) | ((value >> 2) & 0x02);
} else {
_prgBlock = (value >> 3) & 0x03;
}
}
SelectPRGPage(0, (_prgBlock << 2) | _prgPage);
SelectPRGPage(1, (_prgBlock << 2) | 3);
}
virtual void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_prgBlock, _prgPage);
}
};

View file

@ -1,46 +0,0 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class BF909x : public BaseMapper
{
private:
bool _bf9097Mode = false; //Auto-detect for firehawk
protected:
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
if(_romInfo.SubMapperID == 1) {
_bf9097Mode = true;
}
//First and last PRG page
SelectPRGPage(0, 0);
SelectPRGPage(1, -1);
SelectCHRPage(0, 0);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if(addr == 0x9000) {
//Firehawk uses $9000 to change mirroring
_bf9097Mode = true;
}
if(addr >= 0xC000 || !_bf9097Mode) {
SelectPRGPage(0, value);
} else if(addr < 0xC000) {
SetMirroringType((value & 0x10) ? MirroringType::ScreenAOnly : MirroringType::ScreenBOnly);
}
}
virtual void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_bf9097Mode);
}
};

View file

@ -1,53 +0,0 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class Bandai74161_7432 : public BaseMapper
{
private:
bool _enableMirroringControl;
protected:
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
SelectPRGPage(0, 0);
SelectPRGPage(1, -1);
SelectCHRPage(0, 0);
//Hack to make Kamen Rider Club - Gekitotsu Shocker Land work correctly (bad header)
SetMirroringType(MirroringType::Vertical);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
bool mirroringBit = (value & 0x80) == 0x80;
if(mirroringBit) {
//If any game tries to set the bit to true, assume it will use mirroring switches
//This is a hack to make as many games as possible work without CRC checks
_enableMirroringControl = true;
}
if(_enableMirroringControl) {
SetMirroringType(mirroringBit ? MirroringType::ScreenBOnly : MirroringType::ScreenAOnly);
}
//Biggest PRG ROM I could find for mapper 70/152 is 128kb, so the 4th bit will never be used on those
SelectPRGPage(0, (value >> 4) & 0x07);
SelectCHRPage(0, value & 0x0F);
}
virtual void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_enableMirroringControl);
}
public:
Bandai74161_7432(bool enableMirroringControl) : _enableMirroringControl(enableMirroringControl)
{
//According to NesDev Wiki, Mapper 70 is meant to have mirroring forced (by the board) and Mapper 152 allows the code to specify the mirroring type
}
};

View file

@ -1,235 +0,0 @@
#pragma once
#include "BaseMapper.h"
#include "CPU.h"
#include "Console.h"
#include "MemoryManager.h"
#include "DatachBarcodeReader.h"
#include "BaseEeprom24C0X.h"
#include "Eeprom24C01.h"
#include "Eeprom24C02.h"
class BandaiFcg : public BaseMapper
{
private:
bool _irqEnabled;
uint16_t _irqCounter;
uint16_t _irqReload;
uint8_t _prgPage;
uint8_t _prgBankSelect;
uint8_t _chrRegs[8];
shared_ptr<DatachBarcodeReader> _barcodeReader;
shared_ptr<BaseEeprom24C0X> _standardEeprom;
shared_ptr<BaseEeprom24C0X> _extraEeprom;
protected:
uint16_t GetPRGPageSize() override { return 0x4000; }
uint16_t GetCHRPageSize() override { return 0x400; }
uint16_t RegisterStartAddress() override { return 0x6000; }
uint16_t RegisterEndAddress() override { return 0xFFFF; }
bool AllowRegisterRead() override { return true; }
ConsoleFeatures GetAvailableFeatures() override { return _romInfo.MapperID == 157 ? (ConsoleFeatures)((int)ConsoleFeatures::BarcodeReader | (int)ConsoleFeatures::DatachBarcodeReader) : ConsoleFeatures::None; }
void InitMapper() override
{
memset(_chrRegs, 0, sizeof(_chrRegs));
_irqEnabled = false;
_irqCounter = 0;
_irqReload = 0;
_prgPage = 0;
_prgBankSelect = 0;
//Only allow reads from 0x6000 to 0x7FFF
RemoveRegisterRange(0x8000, 0xFFFF, MemoryOperation::Read);
if(_romInfo.MapperID == 157) {
//"Mapper 157 is used for Datach Joint ROM System boards"
_barcodeReader.reset(new DatachBarcodeReader(_console));
_mapperControlDevice = _barcodeReader;
//Datach Joint ROM System
//"It contains an internal 256-byte serial EEPROM (24C02) that is shared among all Datach games."
//"One game, Battle Rush: Build up Robot Tournament, has an additional external 128-byte serial EEPROM (24C01) on the game cartridge."
//"The NES 2.0 header's PRG-NVRAM field will only denote whether the game cartridge has an additional 128-byte serial EEPROM"
if(!IsNes20() || _romInfo.NesHeader.GetSaveRamSize() == 128) {
_extraEeprom.reset(new Eeprom24C01(_console));
}
//All mapper 157 games have an internal 256-byte EEPROM
_standardEeprom.reset(new Eeprom24C02(_console));
} else if(_romInfo.MapperID == 159) {
//LZ93D50 with 128 byte serial EEPROM (24C01)
_standardEeprom.reset(new Eeprom24C01(_console));
} else if(_romInfo.MapperID == 16) {
//"INES Mapper 016 submapper 4: FCG-1/2 ASIC, no serial EEPROM, banked CHR-ROM"
//"INES Mapper 016 submapper 5: LZ93D50 ASIC and no or 256-byte serial EEPROM, banked CHR-ROM"
//Add a 256 byte serial EEPROM (24C02)
if(!IsNes20() || (_romInfo.SubMapperID == 5 && _romInfo.NesHeader.GetSaveRamSize() == 256)) {
//Connect a 256-byte EEPROM for iNES roms, and when submapper 5 + 256 bytes of save ram in header
_standardEeprom.reset(new Eeprom24C02(_console));
}
}
if(_romInfo.MapperID != 16) {
//"For iNES Mapper 153 (with SRAM), the writeable ports must only be mirrored across $8000-$FFFF."
//"Mappers 157 and 159 do not need to support the FCG-1 and -2 and so should only mirror the ports across $8000-$FFFF."
if(_romInfo.MapperID == 153) {
//Mapper 153 has regular save ram from $6000-$7FFF, need to remove the register for both read & writes
RemoveRegisterRange(0x6000, 0x7FFF, MemoryOperation::Any);
} else {
RemoveRegisterRange(0x6000, 0x7FFF, MemoryOperation::Write);
}
} else if(_romInfo.MapperID == 16 && _romInfo.SubMapperID == 4) {
RemoveRegisterRange(0x8000, 0xFFFF, MemoryOperation::Write);
} else if(_romInfo.MapperID == 16 && _romInfo.SubMapperID == 5) {
RemoveRegisterRange(0x6000, 0x7FFF, MemoryOperation::Write);
}
//Last bank
SelectPRGPage(1, 0x0F);
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
ArrayInfo<uint8_t> chrRegs{ _chrRegs, 8 };
Stream(_irqEnabled, _irqCounter, _irqReload, _prgPage, _prgBankSelect, chrRegs);
if(_standardEeprom) {
SnapshotInfo eeprom { _standardEeprom.get() };
Stream(eeprom);
}
if(_extraEeprom) {
SnapshotInfo eeprom { _extraEeprom.get() };
Stream(eeprom);
}
}
void SaveBattery() override
{
if(_standardEeprom) {
_standardEeprom->SaveBattery();
}
if(_extraEeprom) {
_extraEeprom->SaveBattery();
} else {
//Do not call BaseMapper::SaveBattery when the extra EEPROM exists (prevent unused .sav file from being created)
BaseMapper::SaveBattery();
}
}
void ProcessCpuClock() override
{
if(_irqEnabled) {
//Checking counter before decrementing seems to be the only way to get both
//Famicom Jump II - Saikyou no 7 Nin (J) and Magical Taruruuto-kun 2 - Mahou Daibouken (J)
//to work without glitches with the same code.
if(_irqCounter == 0) {
_console->GetCpu()->SetIrqSource(IRQSource::External);
}
_irqCounter--;
}
}
uint8_t ReadRegister(uint16_t addr) override
{
uint8_t output = 0;
if(_barcodeReader) {
output |= _barcodeReader->GetOutput();
}
if(_extraEeprom && _standardEeprom) {
output |= (_standardEeprom->Read() && _extraEeprom->Read()) << 4;
} else if(_standardEeprom) {
output |= (_standardEeprom->Read() << 4);
}
return output | _console->GetMemoryManager()->GetOpenBus(0xE7);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
switch(addr & 0x000F) {
case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07:
_chrRegs[addr & 0x07] = value;
if(_romInfo.MapperID == 153 || GetPRGPageCount() >= 0x20) {
_prgBankSelect = 0;
for(int i = 0; i < 8; i++) {
_prgBankSelect |= (_chrRegs[i] & 0x01) << 4;
}
SelectPRGPage(0, _prgPage | _prgBankSelect);
SelectPRGPage(1, 0x0F | _prgBankSelect);
} else if(!HasChrRam() && _romInfo.MapperID != 157) {
SelectCHRPage(addr & 0x07, value);
}
if(_extraEeprom && _romInfo.MapperID == 157 && (addr & 0x0F) <= 3) {
_extraEeprom->WriteScl((value >> 3) & 0x01);
}
break;
case 0x08:
_prgPage = value & 0x0F;
SelectPRGPage(0, _prgPage | _prgBankSelect);
break;
case 0x09:
switch(value & 0x03) {
case 0: SetMirroringType(MirroringType::Vertical); break;
case 1: SetMirroringType(MirroringType::Horizontal); break;
case 2: SetMirroringType(MirroringType::ScreenAOnly); break;
case 3: SetMirroringType(MirroringType::ScreenBOnly); break;
}
break;
case 0x0A:
_irqEnabled = (value & 0x01) == 0x01;
//Wiki claims there is no reload value, however this seems to be the only way to make Famicom Jump II - Saikyou no 7 Nin work properly
if(_romInfo.MapperID != 16 || !IsNes20() || _romInfo.SubMapperID == 5) {
//"On the LZ93D50 (Submapper 5), writing to this register also copies the latch to the actual counter."
_irqCounter = _irqReload;
}
_console->GetCpu()->ClearIrqSource(IRQSource::External);
break;
case 0x0B:
if(_romInfo.MapperID != 16 || !IsNes20() || _romInfo.SubMapperID != 4) {
//"On the LZ93D50 (Submapper 5), these registers instead modify a latch that will only be copied to the actual counter when register $800A is written to."
_irqReload = (_irqReload & 0xFF00) | value;
} else {
//"On the FCG-1/2 (Submapper 4), writing to these two registers directly modifies the counter itself; all such games therefore disable counting before changing the counter value."
_irqCounter = (_irqCounter & 0xFF00) | value;
}
break;
case 0x0C:
if(_romInfo.MapperID != 16 || !IsNes20() || _romInfo.SubMapperID != 4) {
_irqReload = (_irqReload & 0xFF) | (value << 8);
} else {
_irqCounter = (_irqCounter & 0xFF00) | value;
}
break;
case 0x0D:
if(_romInfo.MapperID == 153) {
SetCpuMemoryMapping(0x6000, 0x7FFF, 0, HasBattery() ? PrgMemoryType::SaveRam : PrgMemoryType::WorkRam, value & 0x20 ? MemoryAccessType::ReadWrite : MemoryAccessType::NoAccess);
} else {
uint8_t scl = (value & 0x20) >> 5;
uint8_t sda = (value & 0x40) >> 6;
if(_standardEeprom) {
_standardEeprom->Write(scl, sda);
}
if(_extraEeprom) {
_extraEeprom->WriteSda(sda);
}
}
break;
}
}
};

View file

@ -1,78 +0,0 @@
#pragma once
#include "stdafx.h"
#include "StandardController.h"
#include "Zapper.h"
#include "IKeyManager.h"
#include "KeyManager.h"
class BandaiHyperShot : public StandardController
{
private:
uint32_t _stateBuffer = 0;
shared_ptr<Console> _console;
protected:
enum ZapperButtons { Fire = 9 };
bool HasCoordinates() override { return true; }
string GetKeyNames() override
{
return StandardController::GetKeyNames() + "F";
}
void InternalSetStateFromInput() override
{
StandardController::InternalSetStateFromInput();
if(_console->GetSettings()->InputEnabled()) {
SetPressedState(ZapperButtons::Fire, KeyManager::IsMouseButtonPressed(MouseButton::LeftButton));
MousePosition pos = KeyManager::GetMousePosition();
if(KeyManager::IsMouseButtonPressed(MouseButton::RightButton)) {
pos.X = -1;
pos.Y = -1;
}
SetCoordinates(pos);
}
}
bool IsLightFound()
{
return Zapper::StaticIsLightFound(GetCoordinates(), _console);
}
void StreamState(bool saving) override
{
BaseControlDevice::StreamState(saving);
Stream(_stateBuffer);
}
public:
BandaiHyperShot(shared_ptr<Console> console, KeyMappingSet keyMappings) : StandardController(console, BaseControlDevice::ExpDevicePort, keyMappings)
{
_console = console;
}
void RefreshStateBuffer() override
{
_stateBuffer = (uint32_t)ToByte();
}
uint8_t ReadRAM(uint16_t addr) override
{
if(addr == 0x4016) {
StrobeProcessRead();
uint8_t output = (_stateBuffer & 0x01) << 1;
_stateBuffer >>= 1;
return output;
} else {
return (IsLightFound() ? 0 : 0x08) | (IsPressed(BandaiHyperShot::ZapperButtons::Fire) ? 0x10 : 0x00);
}
}
void WriteRAM(uint16_t addr, uint8_t value) override
{
StrobeProcessWrite(value);
}
};

View file

@ -1,51 +0,0 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
#include "ControlManager.h"
#include "StandardController.h"
#include "BandaiMicrophone.h"
class BandaiKaraoke : public BaseMapper
{
protected:
uint16_t GetPRGPageSize() override { return 0x4000; }
uint16_t GetCHRPageSize() override { return 0x2000; }
bool AllowRegisterRead() override { return true; }
bool HasBusConflicts() override { return true; }
ConsoleFeatures GetAvailableFeatures() override { return ConsoleFeatures::BandaiMicrophone; }
void InitMapper() override
{
AddRegisterRange(0x6000, 0x7FFF, MemoryOperation::Read);
RemoveRegisterRange(0x8000, 0xFFFF, MemoryOperation::Read);
SelectPRGPage(0, 0);
SelectPRGPage(1, 0x07);
SelectCHRPage(0, 0);
_mapperControlDevice.reset(new BandaiMicrophone(_console, _console->GetSettings()->GetControllerKeys(0)));
}
uint8_t ReadRegister(uint16_t addr) override
{
return _mapperControlDevice->ReadRAM(addr) | _console->GetMemoryManager()->GetOpenBus(0xF8);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if(value & 0x10) {
//Select internal rom
SelectPRGPage(0, value & 0x07);
} else {
//Select expansion rom
if(_prgSize >= 0x40000) {
SelectPRGPage(0, (value & 0x07) | 0x08);
} else {
//Open bus for roms that don't contain the expansion rom
RemoveCpuMemoryMapping(0x8000, 0xBFFF);
}
}
SetMirroringType(value & 0x20 ? MirroringType::Horizontal : MirroringType::Vertical);
}
};

View file

@ -1,51 +0,0 @@
#pragma once
#include "stdafx.h"
#include "BaseControlDevice.h"
#include "Console.h"
class BandaiMicrophone : public BaseControlDevice
{
protected:
enum Buttons { A, B, Microphone };
string GetKeyNames() override
{
return "ABM";
}
void InternalSetStateFromInput() override
{
//Make sure the key bindings are properly updated (not ideal, but good enough)
_keyMappings = _console->GetSettings()->GetControllerKeys(0).GetKeyMappingArray();
for(KeyMapping keyMapping : _keyMappings) {
SetPressedState(Buttons::A, keyMapping.BandaiMicrophoneButtons[0]);
SetPressedState(Buttons::B, keyMapping.BandaiMicrophoneButtons[1]);
if((_console->GetFrameCount() % 2) == 0) {
//Alternate between 1 and 0s (not sure if the game does anything with this data?)
SetPressedState(Buttons::Microphone, keyMapping.BandaiMicrophoneButtons[2]);
}
}
}
public:
BandaiMicrophone(shared_ptr<Console> console, KeyMappingSet keyMappings) : BaseControlDevice(console, BaseControlDevice::MapperInputPort, keyMappings)
{
}
uint8_t ReadRAM(uint16_t addr) override
{
if(addr >= 0x6000 && addr <= 0x7FFF) {
return
(IsPressed(Buttons::A) ? 0 : 0x01) |
(IsPressed(Buttons::B) ? 0 : 0x02) |
(IsPressed(Buttons::Microphone) ? 0x04 : 0);
} else {
return 0;
}
}
void WriteRAM(uint16_t addr, uint8_t value) override
{
}
};

View file

@ -1,105 +0,0 @@
#pragma once
#include "stdafx.h"
#include "BaseControlDevice.h"
#include "IBarcodeReader.h"
#include "MemoryManager.h"
class BarcodeBattlerReader : public BaseControlDevice, public IBarcodeReader
{
private:
static constexpr int StreamSize = 200;
uint64_t _newBarcode = 0;
uint32_t _newBarcodeDigitCount = 0;
uint8_t _barcodeStream[BarcodeBattlerReader::StreamSize];
uint64_t _insertCycle = 0;
protected:
void StreamState(bool saving) override
{
BaseControlDevice::StreamState(saving);
ArrayInfo<uint8_t> bitStream{ _barcodeStream, BarcodeBattlerReader::StreamSize };
Stream(_newBarcode, _newBarcodeDigitCount, _insertCycle, bitStream);
}
bool IsRawString() override
{
return true;
}
void InitBarcodeStream()
{
vector<uint8_t> state = GetRawState().State;
string barcodeText(state.begin(), state.end());
//Signature at the end, needed for code to be recognized
barcodeText += "EPOCH\xD\xA";
//Pad to 20 characters with spaces
barcodeText.insert(0, 20 - barcodeText.size(), ' ');
int pos = 0;
vector<uint8_t> bits;
for(int i = 0; i < 20; i++) {
_barcodeStream[pos++] = 1;
for(int j = 0; j < 8; j++) {
_barcodeStream[pos++] = ~((barcodeText[i] >> j) & 0x01);
}
_barcodeStream[pos++] = 0;
}
}
public:
BarcodeBattlerReader(shared_ptr<Console> console) : BaseControlDevice(console, BaseControlDevice::ExpDevicePort)
{
}
void InternalSetStateFromInput() override
{
ClearState();
if(_newBarcodeDigitCount > 0) {
string barcodeText = std::to_string(_newBarcode);
//Pad 8 or 13 character barcode with 0s at start
barcodeText.insert(0, _newBarcodeDigitCount - barcodeText.size(), '0');
SetTextState(barcodeText);
_newBarcode = 0;
_newBarcodeDigitCount = 0;
}
}
void OnAfterSetState() override
{
if(GetRawState().State.size() > 0) {
InitBarcodeStream();
if(_console) {
_insertCycle = _console->GetCpu()->GetCycleCount();
}
}
}
void InputBarcode(uint64_t barcode, uint32_t digitCount) override
{
_newBarcode = barcode;
_newBarcodeDigitCount = digitCount;
}
uint8_t ReadRAM(uint16_t addr) override
{
if(addr == 0x4017) {
uint64_t elapsedCycles = _console->GetCpu()->GetCycleCount() - _insertCycle;
constexpr uint32_t cyclesPerBit = CPU::ClockRateNtsc / 1200;
uint32_t streamPosition = (uint32_t)(elapsedCycles / cyclesPerBit);
if(streamPosition < BarcodeBattlerReader::StreamSize) {
return _barcodeStream[streamPosition] << 2;
}
}
return 0;
}
void WriteRAM(uint16_t addr, uint8_t value) override
{
}
};

View file

@ -1,109 +0,0 @@
#pragma once
#include "stdafx.h"
#include "IMemoryHandler.h"
#include "EmulationSettings.h"
#include "Snapshotable.h"
#include "SoundMixer.h"
#include "Console.h"
class BaseApuChannel : public IMemoryHandler, public Snapshotable
{
private:
SoundMixer *_mixer;
uint32_t _previousCycle;
AudioChannel _channel;
NesModel _nesModel;
protected:
int8_t _lastOutput;
uint16_t _timer = 0;
uint16_t _period = 0;
shared_ptr<Console> _console;
AudioChannel GetChannel()
{
return _channel;
}
public:
virtual void Clock() = 0;
virtual bool GetStatus() = 0;
BaseApuChannel(AudioChannel channel, shared_ptr<Console> console, SoundMixer *mixer)
{
_channel = channel;
_mixer = mixer;
_console = console;
_nesModel = NesModel::NTSC;
Reset(false);
}
virtual void Reset(bool softReset)
{
_timer = 0;
_period = 0;
_lastOutput = 0;
_previousCycle = 0;
if(_mixer) {
//_mixer is null/not needed for MMC5 square channels
_mixer->Reset();
}
}
virtual void StreamState(bool saving) override
{
if(!saving) {
_previousCycle = 0;
}
Stream(_lastOutput, _timer, _period, _nesModel);
}
void SetNesModel(NesModel model)
{
_nesModel = model;
}
NesModel GetNesModel()
{
if(_nesModel == NesModel::NTSC || _nesModel == NesModel::Dendy) {
//Dendy APU works with NTSC timings
return NesModel::NTSC;
} else {
return _nesModel;
}
}
void Run(uint32_t targetCycle)
{
int32_t cyclesToRun = targetCycle - _previousCycle;
while(cyclesToRun > _timer) {
cyclesToRun -= _timer + 1;
_previousCycle += _timer + 1;
Clock();
_timer = _period;
}
_timer -= cyclesToRun;
_previousCycle = targetCycle;
}
uint8_t ReadRAM(uint16_t addr) override
{
return 0;
}
void AddOutput(int8_t output)
{
if(output != _lastOutput) {
_mixer->AddDelta(_channel, _previousCycle, output - _lastOutput);
_lastOutput = output;
}
}
void EndFrame()
{
_previousCycle = 0;
}
};

View file

@ -1,286 +0,0 @@
#include "stdafx.h"
#include "BaseControlDevice.h"
#include "KeyManager.h"
#include "../Utilities/StringUtilities.h"
#include "Console.h"
#include "EmulationSettings.h"
BaseControlDevice::BaseControlDevice(shared_ptr<Console> console, uint8_t port, KeyMappingSet keyMappingSet)
{
_console = console;
_port = port;
_strobe = false;
_keyMappings = keyMappingSet.GetKeyMappingArray();
}
BaseControlDevice::~BaseControlDevice()
{
}
uint8_t BaseControlDevice::GetPort()
{
return _port;
}
void BaseControlDevice::SetStateFromInput()
{
ClearState();
InternalSetStateFromInput();
}
void BaseControlDevice::InternalSetStateFromInput()
{
}
void BaseControlDevice::StreamState(bool saving)
{
auto lock = _stateLock.AcquireSafe();
VectorInfo<uint8_t> state{ &_state.State };
Stream(_strobe, state);
}
bool BaseControlDevice::IsCurrentPort(uint16_t addr)
{
return _port == (addr - 0x4016);
}
bool BaseControlDevice::IsExpansionDevice()
{
return _port == BaseControlDevice::ExpDevicePort;
}
void BaseControlDevice::StrobeProcessRead()
{
if(_strobe) {
RefreshStateBuffer();
}
}
void BaseControlDevice::StrobeProcessWrite(uint8_t value)
{
bool prevStrobe = _strobe;
_strobe = (value & 0x01) == 0x01;
if(prevStrobe && !_strobe) {
RefreshStateBuffer();
}
}
void BaseControlDevice::ClearState()
{
auto lock = _stateLock.AcquireSafe();
_state = ControlDeviceState();
}
ControlDeviceState BaseControlDevice::GetRawState()
{
auto lock = _stateLock.AcquireSafe();
return _state;
}
void BaseControlDevice::SetRawState(ControlDeviceState state)
{
auto lock = _stateLock.AcquireSafe();
_state = state;
}
void BaseControlDevice::SetTextState(string textState)
{
auto lock = _stateLock.AcquireSafe();
ClearState();
if(IsRawString()) {
_state.State.insert(_state.State.end(), textState.begin(), textState.end());
} else {
if(HasCoordinates()) {
vector<string> data = StringUtilities::Split(textState, ' ');
if(data.size() >= 3) {
MousePosition pos;
try {
pos.X = (int16_t)std::stol(data[0]);
pos.Y = (int16_t)std::stol(data[1]);
} catch(std::exception&) {
pos.X = -1;
pos.Y = -1;
}
SetCoordinates(pos);
textState = data[2];
}
}
int i = 0;
for(char c : textState) {
if(c != '.') {
SetBit(i);
}
i++;
}
}
}
string BaseControlDevice::GetTextState()
{
auto lock = _stateLock.AcquireSafe();
if(IsRawString()) {
return string((char*)_state.State.data(), _state.State.size());
} else {
string keyNames = GetKeyNames();
string output = "";
if(HasCoordinates()) {
MousePosition pos = GetCoordinates();
output += std::to_string(pos.X) + " " + std::to_string(pos.Y) + " ";
}
for(size_t i = 0; i < keyNames.size(); i++) {
output += IsPressed((uint8_t)i) ? keyNames[i] : '.';
}
return output;
}
}
void BaseControlDevice::EnsureCapacity(int32_t minBitCount)
{
auto lock = _stateLock.AcquireSafe();
uint32_t minByteCount = minBitCount / 8 + 1 + (HasCoordinates() ? 32 : 0);
int32_t gap = minByteCount - (int32_t)_state.State.size();
if(gap > 0) {
_state.State.insert(_state.State.end(), gap, 0);
}
}
bool BaseControlDevice::IsKeyboard()
{
return false;
}
bool BaseControlDevice::HasCoordinates()
{
return false;
}
bool BaseControlDevice::IsRawString()
{
return false;
}
uint32_t BaseControlDevice::GetByteIndex(uint8_t bit)
{
return bit / 8 + (HasCoordinates() ? 4 : 0);
}
bool BaseControlDevice::IsPressed(uint8_t bit)
{
auto lock = _stateLock.AcquireSafe();
EnsureCapacity(bit);
uint8_t bitMask = 1 << (bit % 8);
return (_state.State[GetByteIndex(bit)] & bitMask) != 0;
}
void BaseControlDevice::SetBitValue(uint8_t bit, bool set)
{
if(set) {
SetBit(bit);
} else {
ClearBit(bit);
}
}
void BaseControlDevice::SetBit(uint8_t bit)
{
auto lock = _stateLock.AcquireSafe();
EnsureCapacity(bit);
uint8_t bitMask = 1 << (bit % 8);
_state.State[GetByteIndex(bit)] |= bitMask;
}
void BaseControlDevice::ClearBit(uint8_t bit)
{
auto lock = _stateLock.AcquireSafe();
EnsureCapacity(bit);
uint8_t bitMask = 1 << (bit % 8);
_state.State[GetByteIndex(bit)] &= ~bitMask;
}
void BaseControlDevice::InvertBit(uint8_t bit)
{
if(IsPressed(bit)) {
ClearBit(bit);
} else {
SetBit(bit);
}
}
void BaseControlDevice::SetPressedState(uint8_t bit, uint32_t keyCode)
{
if(IsKeyboard() && keyCode < 0x200 && !_console->GetSettings()->IsKeyboardMode()) {
//Prevent keyboard device input when keyboard mode is off
return;
}
if(_console->GetSettings()->InputEnabled() && (!_console->GetSettings()->IsKeyboardMode() || keyCode >= 0x200 || IsKeyboard()) && KeyManager::IsKeyPressed(keyCode)) {
SetBit(bit);
}
}
void BaseControlDevice::SetPressedState(uint8_t bit, bool enabled)
{
if(enabled) {
SetBit(bit);
}
}
void BaseControlDevice::SetCoordinates(MousePosition pos)
{
auto lock = _stateLock.AcquireSafe();
EnsureCapacity(-1);
_state.State[0] = pos.X & 0xFF;
_state.State[1] = (pos.X >> 8) & 0xFF;
_state.State[2] = pos.Y & 0xFF;
_state.State[3] = (pos.Y >> 8) & 0xFF;
}
MousePosition BaseControlDevice::GetCoordinates()
{
auto lock = _stateLock.AcquireSafe();
EnsureCapacity(-1);
MousePosition pos;
pos.X = _state.State[0] | (_state.State[1] << 8);
pos.Y = _state.State[2] | (_state.State[3] << 8);
return pos;
}
void BaseControlDevice::SetMovement(MouseMovement mov)
{
MouseMovement prev = GetMovement();
mov.dx += prev.dx;
mov.dy += prev.dy;
SetCoordinates({ mov.dx, mov.dy });
}
MouseMovement BaseControlDevice::GetMovement()
{
MousePosition pos = GetCoordinates();
SetCoordinates({ 0, 0 });
return { pos.X, pos.Y };
}
void BaseControlDevice::SwapButtons(shared_ptr<BaseControlDevice> state1, uint8_t button1, shared_ptr<BaseControlDevice> state2, uint8_t button2)
{
bool pressed1 = state1->IsPressed(button1);
bool pressed2 = state2->IsPressed(button2);
state1->ClearBit(button1);
state2->ClearBit(button2);
if(pressed1) {
state2->SetBit(button2);
}
if(pressed2) {
state1->SetBit(button1);
}
}

View file

@ -1,84 +0,0 @@
#pragma once
#include "stdafx.h"
#include "EmulationSettings.h"
#include "Snapshotable.h"
#include "ControlManager.h"
#include "ControlDeviceState.h"
#include "../Utilities/SimpleLock.h"
class Console;
class BaseControlDevice : public Snapshotable
{
private:
ControlDeviceState _state;
protected:
shared_ptr<Console> _console;
vector<KeyMapping> _keyMappings;
bool _strobe;
uint8_t _port;
SimpleLock _stateLock;
virtual void RefreshStateBuffer() { }
virtual void StreamState(bool saving);
void EnsureCapacity(int32_t minBitCount);
uint32_t GetByteIndex(uint8_t bit);
virtual bool HasCoordinates();
virtual bool IsRawString();
bool IsCurrentPort(uint16_t addr);
bool IsExpansionDevice();
void StrobeProcessRead();
void StrobeProcessWrite(uint8_t value);
virtual string GetKeyNames() { return ""; }
void SetPressedState(uint8_t bit, uint32_t keyCode);
void SetPressedState(uint8_t bit, bool enabled);
void SetCoordinates(MousePosition pos);
void SetMovement(MouseMovement mov);
MouseMovement GetMovement();
virtual void InternalSetStateFromInput();
public:
static constexpr uint8_t ExpDevicePort = 4;
static constexpr uint8_t ConsoleInputPort = 5;
static constexpr uint8_t MapperInputPort = 6;
static constexpr uint8_t ExpDevicePort2 = 7;
static constexpr uint8_t PortCount = ExpDevicePort2 + 1;
BaseControlDevice(shared_ptr<Console> console, uint8_t port, KeyMappingSet keyMappingSet = KeyMappingSet());
virtual ~BaseControlDevice();
uint8_t GetPort();
bool IsPressed(uint8_t bit);
MousePosition GetCoordinates();
virtual bool IsKeyboard();
void ClearState();
void SetBit(uint8_t bit);
void ClearBit(uint8_t bit);
void InvertBit(uint8_t bit);
void SetBitValue(uint8_t bit, bool set);
void SetTextState(string state);
string GetTextState();
void SetStateFromInput();
virtual void OnAfterSetState() { }
void SetRawState(ControlDeviceState state);
ControlDeviceState GetRawState();
virtual uint8_t ReadRAM(uint16_t addr) = 0;
virtual void WriteRAM(uint16_t addr, uint8_t value) = 0;
void static SwapButtons(shared_ptr<BaseControlDevice> state1, uint8_t button1, shared_ptr<BaseControlDevice> state2, uint8_t button2);
};

View file

@ -1,57 +0,0 @@
#pragma once
#include "stdafx.h"
#include "Snapshotable.h"
class BaseEeprom24C0X : public Snapshotable
{
protected:
enum class Mode
{
Idle = 0,
Address = 1,
Read = 2,
Write = 3,
SendAck = 4,
WaitAck = 5,
ChipAddress = 6
};
shared_ptr<Console> _console;
Mode _mode = Mode::Idle;
Mode _nextMode = Mode::Idle;
uint8_t _chipAddress = 0;
uint8_t _address = 0;
uint8_t _data = 0;
uint8_t _counter = 0;
uint8_t _output = 0;
uint8_t _prevScl = 0;
uint8_t _prevSda = 0;
uint8_t _romData[256];
void StreamState(bool saving) override
{
ArrayInfo<uint8_t> romData { _romData, 256 };
Stream(_mode, _nextMode, _chipAddress, _address, _data, _counter, _output, _prevScl, _prevSda, romData);
}
public:
virtual void Write(uint8_t scl, uint8_t sda) = 0;
virtual void SaveBattery() = 0;
uint8_t Read()
{
return _output;
}
void WriteScl(uint8_t scl)
{
Write(scl, _prevSda);
}
void WriteSda(uint8_t sda)
{
Write(_prevScl, sda);
}
};

View file

@ -1,20 +0,0 @@
#include "stdafx.h"
#include "BaseExpansionAudio.h"
#include "Console.h"
#include "APU.h"
BaseExpansionAudio::BaseExpansionAudio(shared_ptr<Console> console)
{
_console = console;
}
void BaseExpansionAudio::StreamState(bool saving)
{
}
void BaseExpansionAudio::Clock()
{
if(_console->GetApu()->IsApuEnabled()) {
ClockAudio();
}
}

View file

@ -1,20 +0,0 @@
#pragma once
#include "stdafx.h"
#include "Snapshotable.h"
#include "EmulationSettings.h"
class MemoryManager;
class BaseExpansionAudio : public Snapshotable
{
protected:
shared_ptr<Console> _console = nullptr;
virtual void ClockAudio() = 0;
void StreamState(bool saving) override;
public:
BaseExpansionAudio(shared_ptr<Console> console);
void Clock();
};

View file

@ -1,89 +0,0 @@
#pragma once
#include "stdafx.h"
#include "Snapshotable.h"
class BaseFdsChannel : public Snapshotable
{
protected:
uint8_t _speed = 0;
uint8_t _gain = 0;
bool _envelopeOff = false;
bool _volumeIncrease = false;
uint16_t _frequency = 0;
uint32_t _timer = 0;
//"Few FDS NSFs write to this register. The BIOS initializes this to $FF."
uint8_t _masterSpeed = 0xFF;
void StreamState(bool saving) override
{
Stream(_speed, _gain, _envelopeOff, _volumeIncrease, _frequency, _timer, _masterSpeed);
}
public:
void SetMasterEnvelopeSpeed(uint8_t masterSpeed)
{
_masterSpeed = masterSpeed;
}
virtual void WriteReg(uint16_t addr, uint8_t value)
{
switch(addr & 0x03) {
case 0:
_speed = value & 0x3F;
_volumeIncrease = (value & 0x40) == 0x40;
_envelopeOff = (value & 0x80) == 0x80;
//"Writing to this register immediately resets the clock timer that ticks the volume envelope (delaying the next tick slightly)."
ResetTimer();
if(_envelopeOff) {
//Envelope is off, gain = speed
_gain = _speed;
}
break;
case 2:
_frequency = (_frequency & 0x0F00) | value;
break;
case 3:
_frequency = (_frequency & 0xFF) | ((value & 0x0F) << 8);
break;
}
}
bool TickEnvelope()
{
if(!_envelopeOff && _masterSpeed > 0) {
_timer--;
if(_timer == 0) {
ResetTimer();
if(_volumeIncrease && _gain < 32) {
_gain++;
} else if(!_volumeIncrease && _gain > 0) {
_gain--;
}
return true;
}
}
return false;
}
uint8_t GetGain()
{
return _gain;
}
uint16_t GetFrequency()
{
return _frequency;
}
void ResetTimer()
{
_timer = 8 * (_speed + 1) * _masterSpeed;
}
};

View file

@ -1,22 +0,0 @@
#pragma once
#include "stdafx.h"
#include "MessageManager.h"
class BaseLoader
{
protected:
bool _checkOnly;
void Log(string message)
{
if(!_checkOnly) {
MessageManager::Log(message);
}
}
public:
BaseLoader(bool checkOnly = false)
{
_checkOnly = checkOnly;
}
};

File diff suppressed because it is too large Load diff

View file

@ -1,246 +0,0 @@
#pragma once
#include "stdafx.h"
#include "Snapshotable.h"
#include "IMemoryHandler.h"
#include "DebuggerTypes.h"
#include "Debugger.h"
#include "Types.h"
#include "IBattery.h"
#include "RomData.h"
#include "Console.h"
#include "CPU.h"
#include "EPSMAudio.h"
class BaseControlDevice;
class BaseMapper : public IMemoryHandler, public Snapshotable, public IBattery
{
private:
MirroringType _mirroringType;
string _batteryFilename;
uint16_t InternalGetPrgPageSize();
uint16_t InternalGetSaveRamPageSize();
uint16_t InternalGetWorkRamPageSize();
uint16_t InternalGetChrPageSize();
uint16_t InternalGetChrRamPageSize();
bool ValidateAddressRange(uint16_t startAddr, uint16_t endAddr);
uint8_t *_nametableRam = nullptr;
uint8_t _nametableCount = 2;
bool _onlyChrRam = false;
bool _hasBusConflicts = false;
bool _allowRegisterRead = false;
bool _isReadRegisterAddr[0x10000];
bool _isWriteRegisterAddr[0x10000];
MemoryAccessType _prgMemoryAccess[0x100];
uint8_t* _prgPages[0x100];
MemoryAccessType _chrMemoryAccess[0x100];
uint8_t* _chrPages[0x100];
int32_t _prgMemoryOffset[0x100];
PrgMemoryType _prgMemoryType[0x100];
int32_t _chrMemoryOffset[0x100];
ChrMemoryType _chrMemoryType[0x100];
vector<uint8_t> _originalPrgRom;
vector<uint8_t> _originalChrRom;
protected:
RomInfo _romInfo;
shared_ptr<BaseControlDevice> _mapperControlDevice;
shared_ptr<Console> _console;
uint8_t* _prgRom = nullptr;
uint8_t* _chrRom = nullptr;
uint8_t* _chrRam = nullptr;
uint32_t _prgSize = 0;
uint32_t _chrRomSize = 0;
uint32_t _chrRamSize = 0;
uint8_t* _saveRam = nullptr;
uint32_t _saveRamSize = 0;
uint32_t _workRamSize = 0;
uint8_t* _workRam = nullptr;
bool _hasChrBattery = false;
int16_t _vramOpenBusValue = -1;
virtual void InitMapper() = 0;
virtual void InitMapper(RomData &romData);
virtual uint16_t GetPRGPageSize() = 0;
virtual uint16_t GetCHRPageSize() = 0;
bool IsNes20();
virtual uint16_t GetChrRamPageSize() { return 0x2000; }
//Save ram is battery backed and saved to disk
virtual uint32_t GetSaveRamSize() { return HasBattery() ? 0x2000 : 0; }
virtual uint32_t GetSaveRamPageSize() { return 0x2000; }
virtual bool ForceChrBattery() { return false; }
virtual bool ForceSaveRamSize() { return false; }
virtual bool ForceWorkRamSize() { return false; }
virtual uint32_t GetChrRamSize() { return 0x0000; }
//Work ram is NOT saved - aka Expansion ram, etc.
virtual uint32_t GetWorkRamSize() { return HasBattery() ? 0 : 0x2000; }
virtual uint32_t GetWorkRamPageSize() { return 0x2000; }
virtual uint16_t RegisterStartAddress() { return 0x8000; }
virtual uint16_t RegisterEndAddress() { return 0xFFFF; }
virtual bool AllowRegisterRead() { return false; }
virtual uint32_t GetDipSwitchCount() { return 0; }
virtual bool HasBusConflicts() { return false; }
uint8_t InternalReadRam(uint16_t addr);
virtual void WriteRegister(uint16_t addr, uint8_t value);
virtual void WriteEPSM(uint16_t addr, uint8_t value);
virtual uint8_t ReadRegister(uint16_t addr);
void SelectPrgPage4x(uint16_t slot, uint16_t page, PrgMemoryType memoryType = PrgMemoryType::PrgRom);
void SelectPrgPage2x(uint16_t slot, uint16_t page, PrgMemoryType memoryType = PrgMemoryType::PrgRom);
virtual void SelectPRGPage(uint16_t slot, uint16_t page, PrgMemoryType memoryType = PrgMemoryType::PrgRom);
void SetCpuMemoryMapping(uint16_t startAddr, uint16_t endAddr, int16_t pageNumber, PrgMemoryType type, int8_t accessType = -1);
void SetCpuMemoryMapping(uint16_t startAddr, uint16_t endAddr, PrgMemoryType type, uint32_t sourceOffset, int8_t accessType);
void SetCpuMemoryMapping(uint16_t startAddr, uint16_t endAddr, uint8_t *source, int8_t accessType = -1);
void RemoveCpuMemoryMapping(uint16_t startAddr, uint16_t endAddr);
virtual void SelectChrPage8x(uint16_t slot, uint16_t page, ChrMemoryType memoryType = ChrMemoryType::Default);
virtual void SelectChrPage4x(uint16_t slot, uint16_t page, ChrMemoryType memoryType = ChrMemoryType::Default);
virtual void SelectChrPage2x(uint16_t slot, uint16_t page, ChrMemoryType memoryType = ChrMemoryType::Default);
virtual void SelectCHRPage(uint16_t slot, uint16_t page, ChrMemoryType memoryType = ChrMemoryType::Default);
void SetPpuMemoryMapping(uint16_t startAddr, uint16_t endAddr, uint16_t pageNumber, ChrMemoryType type = ChrMemoryType::Default, int8_t accessType = -1);
void SetPpuMemoryMapping(uint16_t startAddr, uint16_t endAddr, ChrMemoryType type, uint32_t sourceOffset, int8_t accessType);
void SetPpuMemoryMapping(uint16_t startAddr, uint16_t endAddr, uint8_t* sourceMemory, int8_t accessType = -1);
void RemovePpuMemoryMapping(uint16_t startAddr, uint16_t endAddr);
bool HasBattery();
virtual void LoadBattery();
string GetBatteryFilename();
uint32_t GetPRGPageCount();
uint32_t GetCHRPageCount();
uint8_t GetPowerOnByte(uint8_t defaultValue = 0);
uint32_t GetDipSwitches();
void SetupDefaultWorkRam();
void InitializeChrRam(int32_t chrRamSize = -1);
void AddRegisterRange(uint16_t startAddr, uint16_t endAddr, MemoryOperation operation = MemoryOperation::Any);
void RemoveRegisterRange(uint16_t startAddr, uint16_t endAddr, MemoryOperation operation = MemoryOperation::Any);
virtual void StreamState(bool saving) override;
void RestorePrgChrState();
uint8_t* GetNametable(uint8_t nametableIndex);
void SetNametable(uint8_t index, uint8_t nametableIndex);
void SetNametables(uint8_t nametable1Index, uint8_t nametable2Index, uint8_t nametable3Index, uint8_t nametable4Index);
void SetMirroringType(MirroringType type);
MirroringType GetMirroringType();
uint8_t InternalReadVRAM(uint16_t addr);
public:
static constexpr uint32_t NametableCount = 0x10;
static constexpr uint32_t NametableSize = 0x400;
unique_ptr<EPSMAudio> _epsmaudio;
void Initialize(RomData &romData);
virtual ~BaseMapper();
virtual void Reset(bool softReset);
virtual ConsoleFeatures GetAvailableFeatures();
virtual void SetNesModel(NesModel model) { }
virtual void ProcessCpuClock() { }
virtual void ProcessEPSMClock() { _epsmaudio->Clock(); }
virtual void NotifyVRAMAddressChange(uint16_t addr);
virtual void GetMemoryRanges(MemoryRanges &ranges) override;
virtual void SaveBattery() override;
void SetConsole(shared_ptr<Console> console);
shared_ptr<BaseControlDevice> GetMapperControlDevice();
RomInfo GetRomInfo();
uint32_t GetMapperDipSwitchCount();
virtual void ApplySamples(int16_t* buffer, size_t sampleCount, double volume) {}
uint8_t ReadRAM(uint16_t addr) override;
uint8_t PeekRAM(uint16_t addr) override;
uint8_t DebugReadRAM(uint16_t addr);
void WriteRAM(uint16_t addr, uint8_t value) override;
void DebugWriteRAM(uint16_t addr, uint8_t value);
void WritePrgRam(uint16_t addr, uint8_t value);
virtual uint8_t MapperReadVRAM(uint16_t addr, MemoryOperationType operationType);
__forceinline uint8_t ReadVRAM(uint16_t addr, MemoryOperationType type = MemoryOperationType::PpuRenderingRead)
{
uint8_t value = MapperReadVRAM(addr, type);
_console->DebugProcessVramReadOperation(type, addr, value);
return value;
}
void DebugWriteVRAM(uint16_t addr, uint8_t value, bool disableSideEffects = true);
void WriteVRAM(uint16_t addr, uint8_t value);
uint8_t DebugReadVRAM(uint16_t addr, bool disableSideEffects = true);
void CopyChrTile(uint32_t address, uint8_t *dest);
//Debugger Helper Functions
bool HasChrRam();
bool HasChrRom();
CartridgeState GetState();
uint8_t* GetPrgRom();
uint8_t* GetWorkRam();
uint8_t* GetSaveRam();
uint8_t GetMemoryValue(DebugMemoryType memoryType, uint32_t address);
void SetMemoryValue(DebugMemoryType memoryType, uint32_t address, uint8_t value);
uint32_t GetMemorySize(DebugMemoryType type);
uint32_t CopyMemory(DebugMemoryType type, uint8_t* buffer);
void WriteMemory(DebugMemoryType type, uint8_t* buffer, int32_t length);
void GetAbsoluteAddressAndType(uint32_t relativeAddr, AddressTypeInfo *info);
void GetPpuAbsoluteAddressAndType(uint32_t relativeAddr, PpuAddressTypeInfo *info);
int32_t ToAbsoluteAddress(uint16_t addr);
int32_t ToAbsoluteSaveRamAddress(uint16_t addr);
int32_t ToAbsoluteWorkRamAddress(uint16_t addr);
int32_t ToAbsoluteChrAddress(uint16_t addr);
int32_t ToAbsoluteChrRamAddress(uint16_t addr);
int32_t ToAbsoluteChrRomAddress(uint16_t addr);
int32_t FromAbsoluteChrAddress(uint32_t addr);
int32_t FromAbsoluteAddress(uint32_t addr, AddressType type = AddressType::PrgRom);
int32_t FromAbsolutePpuAddress(uint32_t addr, PpuAddressType type);
bool IsWriteRegister(uint16_t addr);
bool IsReadRegister(uint16_t addr);
void GetRomFileData(vector<uint8_t> &out, bool asIpsFile, uint8_t* header);
vector<uint8_t> GetPrgChrCopy();
void RestorePrgChrBackup(vector<uint8_t>& backupData);
void RevertPrgChrChanges();
bool HasPrgChrChanges();
void CopyPrgChrRom(shared_ptr<BaseMapper> mapper);
};

View file

@ -1,199 +0,0 @@
#include "stdafx.h"
#include <cmath>
#include "BaseRenderer.h"
#include "Console.h"
#include "EmulationSettings.h"
#include "VideoDecoder.h"
#include "PPU.h"
BaseRenderer::BaseRenderer(shared_ptr<Console> console, bool registerAsMessageManager)
{
_console = console;
if(registerAsMessageManager) {
//Only display messages on the master CPU's screen
MessageManager::RegisterMessageManager(this);
}
}
BaseRenderer::~BaseRenderer()
{
MessageManager::UnregisterMessageManager(this);
}
void BaseRenderer::DisplayMessage(string title, string message)
{
shared_ptr<ToastInfo> toast(new ToastInfo(title, message, 4000));
_toasts.push_front(toast);
}
void BaseRenderer::RemoveOldToasts()
{
_toasts.remove_if([](shared_ptr<ToastInfo> toast) { return toast->IsToastExpired(); });
}
void BaseRenderer::DrawToasts()
{
RemoveOldToasts();
int counter = 0;
int lastHeight = 5;
for(shared_ptr<ToastInfo> toast : _toasts) {
if(counter < 6) {
DrawToast(toast, lastHeight);
} else {
break;
}
counter++;
}
}
std::wstring BaseRenderer::WrapText(string utf8Text, float maxLineWidth, uint32_t &lineCount)
{
using std::wstring;
wstring text = utf8::utf8::decode(utf8Text);
wstring wrappedText;
list<wstring> words;
wstring currentWord;
for(size_t i = 0, len = text.length(); i < len; i++) {
if(text[i] == L' ' || text[i] == L'\n') {
if(currentWord.length() > 0) {
words.push_back(currentWord);
currentWord.clear();
}
} else {
currentWord += text[i];
}
}
if(currentWord.length() > 0) {
words.push_back(currentWord);
}
lineCount = 1;
float spaceWidth = MeasureString(L" ");
float lineWidth = 0.0f;
for(wstring word : words) {
for(unsigned int i = 0; i < word.size(); i++) {
if(!ContainsCharacter(word[i])) {
word[i] = L'?';
}
}
float wordWidth = MeasureString(word.c_str());
if(lineWidth + wordWidth < maxLineWidth) {
wrappedText += word + L" ";
lineWidth += wordWidth + spaceWidth;
} else {
wrappedText += L"\n" + word + L" ";
lineWidth = wordWidth + spaceWidth;
lineCount++;
}
}
return wrappedText;
}
void BaseRenderer::DrawToast(shared_ptr<ToastInfo> toast, int &lastHeight)
{
//Get opacity for fade in/out effect
uint8_t opacity = (uint8_t)(toast->GetOpacity()*255);
int textLeftMargin = 4;
int lineHeight = 25;
string text = "[" + toast->GetToastTitle() + "] " + toast->GetToastMessage();
uint32_t lineCount = 0;
std::wstring wrappedText = WrapText(text, (float)(_screenWidth - textLeftMargin * 2 - 20), lineCount);
lastHeight += lineCount * lineHeight;
DrawString(wrappedText, textLeftMargin, _screenHeight - lastHeight, opacity, opacity, opacity, opacity);
}
void BaseRenderer::DrawString(std::string message, int x, int y, uint8_t r, uint8_t g, uint8_t b, uint8_t opacity)
{
std::wstring textStr = utf8::utf8::decode(message);
DrawString(textStr, x, y, r, g, b, opacity);
}
void BaseRenderer::ShowFpsCounter(int lineNumber)
{
int yPos = 13 + 24 * lineNumber;
if(_fpsTimer.GetElapsedMS() > 1000) {
//Update fps every sec
uint32_t frameCount = _console->GetFrameCount();
if(_lastFrameCount > frameCount) {
_currentFPS = 0;
} else {
_currentFPS = (int)(std::round((double)(frameCount - _lastFrameCount) / (_fpsTimer.GetElapsedMS() / 1000)));
_currentRenderedFPS = (int)(std::round((double)(_renderedFrameCount - _lastRenderedFrameCount) / (_fpsTimer.GetElapsedMS() / 1000)));
}
_lastFrameCount = frameCount;
_lastRenderedFrameCount = _renderedFrameCount;
_fpsTimer.Reset();
}
if(_currentFPS > 5000) {
_currentFPS = 0;
}
if(_currentRenderedFPS > 5000) {
_currentRenderedFPS = 0;
}
string fpsString = string("FPS: ") + std::to_string(_currentFPS) + " / " + std::to_string(_currentRenderedFPS);
DrawString(fpsString, _screenWidth - 125, yPos, 250, 235, 215);
}
void BaseRenderer::ShowGameTimer(int lineNumber)
{
int yPos = 13 + 24 * lineNumber;
double frameCount = _console->GetFrameCount();
double frameRate = _console->GetModel() == NesModel::NTSC ? 60.098811862348404716732985230828 : 50.006977968268290848936010226333;
//uint32_t milliseconds = (uint32_t)(frameCount / 60.1 * 1000) % 1000;
uint32_t seconds = (uint32_t)(frameCount / frameRate) % 60;
uint32_t minutes = (uint32_t)(frameCount / frameRate / 60) % 60;
uint32_t hours = (uint32_t)(frameCount / frameRate / 3600);
std::stringstream ss;
ss << std::setw(2) << std::setfill('0') << hours << ":";
ss << std::setw(2) << std::setfill('0') << minutes << ":";
ss << std::setw(2) << std::setfill('0') << seconds;
//ss << "." << std::setw(3) << std::setfill('0') << milliseconds;
DrawString(ss.str(), _screenWidth - 95, yPos, 250, 235, 215);
}
void BaseRenderer::ShowLagCounter(int lineNumber)
{
int yPos = 13 + 24 * lineNumber;
string lagCounter = MessageManager::Localize("Lag") + ": " + std::to_string(_console->GetLagCounter());
DrawString(lagCounter, _screenWidth - 123, yPos, 250, 235, 215);
}
void BaseRenderer::ShowFrameCounter(int lineNumber)
{
int yPos = 13 + 24 * lineNumber;
string lagCounter = MessageManager::Localize("Frame") + ": " + std::to_string(_console->GetFrameCount());
DrawString(lagCounter, _screenWidth - 146, yPos, 250, 235, 215);
}
void BaseRenderer::DrawCounters()
{
int lineNumber = 0;
EmulationSettings* settings = _console->GetSettings();
if(settings->CheckFlag(EmulationFlags::ShowGameTimer)) {
ShowGameTimer(lineNumber++);
}
if(settings->CheckFlag(EmulationFlags::ShowFPS)) {
ShowFpsCounter(lineNumber++);
}
if(settings->CheckFlag(EmulationFlags::ShowLagCounter)) {
ShowLagCounter(lineNumber++);
}
if(settings->CheckFlag(EmulationFlags::ShowFrameCounter)) {
ShowFrameCounter(lineNumber++);
}
}
bool BaseRenderer::IsMessageShown()
{
return !_toasts.empty();
}

View file

@ -1,47 +0,0 @@
#pragma once
#include "../Core/IMessageManager.h"
#include "../Utilities/Timer.h"
class Console;
class BaseRenderer : public IMessageManager
{
private:
list<shared_ptr<ToastInfo>> _toasts;
Timer _fpsTimer;
uint32_t _lastFrameCount = 0;
uint32_t _lastRenderedFrameCount = 0;
uint32_t _currentFPS = 0;
uint32_t _currentRenderedFPS = 0;
void RemoveOldToasts();
std::wstring WrapText(string utf8Text, float maxLineWidth, uint32_t &lineCount);
virtual float MeasureString(std::wstring text) = 0;
virtual bool ContainsCharacter(wchar_t character) = 0;
protected:
shared_ptr<Console> _console;
uint32_t _screenWidth = 0;
uint32_t _screenHeight = 0;
uint32_t _renderedFrameCount = 0;
BaseRenderer(shared_ptr<Console> console, bool registerAsMessageManager);
virtual ~BaseRenderer();
bool IsMessageShown();
void DisplayMessage(string title, string message);
void DrawToasts();
void DrawToast(shared_ptr<ToastInfo> toast, int &lastHeight);
void DrawString(std::string message, int x, int y, uint8_t r, uint8_t g, uint8_t b, uint8_t opacity = 255);
virtual void DrawString(std::wstring message, int x, int y, uint8_t r = 255, uint8_t g = 255, uint8_t b = 255, uint8_t opacity = 255) = 0;
void ShowFpsCounter(int lineNumber);
void ShowLagCounter(int lineNumber);
void ShowFrameCounter(int lineNumber);
void ShowGameTimer(int lineNumber);
void DrawCounters();
};

View file

@ -1,50 +0,0 @@
#include "stdafx.h"
#include "BaseSoundManager.h"
void BaseSoundManager::ProcessLatency(uint32_t readPosition, uint32_t writePosition)
{
//Record latency between read & write cursors once per frame
int32_t cursorGap;
if(writePosition < readPosition) {
cursorGap = writePosition - readPosition + _bufferSize;
} else {
cursorGap = writePosition - readPosition;
}
_cursorGaps[_cursorGapIndex] = cursorGap;
_cursorGapIndex = (_cursorGapIndex + 1) % 60;
if(_cursorGapIndex == 0) {
_cursorGapFilled = true;
}
if(_cursorGapFilled) {
//Once we have 60+ frames worth of data to work with, adjust playback frequency by +/- 0.5%
//To speed up or slow down playback in order to reach our latency goal.
uint32_t bytesPerSample = _isStereo ? 4 : 2;
int32_t gapSum = 0;
for(int i = 0; i < 60; i++) {
gapSum += _cursorGaps[i];
}
int32_t gapAverage = gapSum / 60;
_averageLatency = (gapAverage / bytesPerSample) / (double)_sampleRate * 1000;
}
}
AudioStatistics BaseSoundManager::GetStatistics()
{
AudioStatistics stats;
stats.AverageLatency = _averageLatency;
stats.BufferUnderrunEventCount = _bufferUnderrunEventCount;
stats.BufferSize = _bufferSize;
return stats;
}
void BaseSoundManager::ResetStats()
{
_cursorGapIndex = 0;
_cursorGapFilled = false;
_bufferUnderrunEventCount = 0;
_averageLatency = 0;
}

View file

@ -1,23 +0,0 @@
#pragma once
#include "../Core/IAudioDevice.h"
class BaseSoundManager : public IAudioDevice
{
public:
void ProcessLatency(uint32_t readPosition, uint32_t writePosition);
AudioStatistics GetStatistics() override;
protected:
bool _isStereo;
uint32_t _sampleRate = 0;
double _averageLatency = 0;
uint32_t _bufferSize = 0x10000;
uint32_t _bufferUnderrunEventCount = 0;
int32_t _cursorGaps[60];
int32_t _cursorGapIndex = 0;
bool _cursorGapFilled = false;
void ResetStats();
};

View file

@ -1,144 +0,0 @@
#include "stdafx.h"
#include "BaseVideoFilter.h"
#include "MessageManager.h"
#include "../Utilities/PNGHelper.h"
#include "../Utilities/FolderUtilities.h"
#include "StandardController.h"
#include "ScaleFilter.h"
#include "RotateFilter.h"
#include "Console.h"
BaseVideoFilter::BaseVideoFilter(shared_ptr<Console> console)
{
_console = console;
_overscan = _console->GetSettings()->GetOverscanDimensions();
}
BaseVideoFilter::~BaseVideoFilter()
{
auto lock = _frameLock.AcquireSafe();
if(_outputBuffer) {
delete[] _outputBuffer;
_outputBuffer = nullptr;
}
}
void BaseVideoFilter::UpdateBufferSize()
{
uint32_t newBufferSize = GetFrameInfo().Width*GetFrameInfo().Height;
if(_bufferSize != newBufferSize) {
_frameLock.Acquire();
if(_outputBuffer) {
delete[] _outputBuffer;
}
_bufferSize = newBufferSize;
_outputBuffer = new uint32_t[newBufferSize];
_frameLock.Release();
}
}
OverscanDimensions BaseVideoFilter::GetOverscan()
{
return _overscan;
}
void BaseVideoFilter::OnBeforeApplyFilter()
{
}
bool BaseVideoFilter::IsOddFrame()
{
return _isOddFrame;
}
void BaseVideoFilter::SendFrame(uint16_t *ppuOutputBuffer, uint32_t frameNumber)
{
_frameLock.Acquire();
_overscan = _console->GetSettings()->GetOverscanDimensions();
_isOddFrame = frameNumber % 2;
UpdateBufferSize();
OnBeforeApplyFilter();
ApplyFilter(ppuOutputBuffer);
_frameLock.Release();
}
uint32_t* BaseVideoFilter::GetOutputBuffer()
{
return _outputBuffer;
}
void BaseVideoFilter::TakeScreenshot(VideoFilterType filterType, string filename, std::stringstream *stream, bool rawScreenshot)
{
uint32_t* pngBuffer;
FrameInfo frameInfo;
uint32_t* frameBuffer = nullptr;
{
auto lock = _frameLock.AcquireSafe();
if(_bufferSize == 0 || !GetOutputBuffer()) {
return;
}
frameBuffer = new uint32_t[_bufferSize];
memcpy(frameBuffer, GetOutputBuffer(), _bufferSize * sizeof(frameBuffer[0]));
frameInfo = GetFrameInfo();
}
pngBuffer = frameBuffer;
shared_ptr<RotateFilter> rotateFilter;
shared_ptr<ScaleFilter> scaleFilter = ScaleFilter::GetScaleFilter(filterType);
if(!rawScreenshot) {
uint32_t rotationAngle = _console->GetSettings()->GetScreenRotation();
if(rotationAngle > 0) {
rotateFilter.reset(new RotateFilter(rotationAngle));
pngBuffer = rotateFilter->ApplyFilter(pngBuffer, frameInfo.Width, frameInfo.Height);
frameInfo = rotateFilter->GetFrameInfo(frameInfo);
}
if(scaleFilter) {
pngBuffer = scaleFilter->ApplyFilter(pngBuffer, frameInfo.Width, frameInfo.Height, _console->GetSettings()->GetPictureSettings().ScanlineIntensity);
frameInfo = scaleFilter->GetFrameInfo(frameInfo);
}
VideoHud hud;
hud.DrawHud(_console, pngBuffer, frameInfo, _console->GetSettings()->GetOverscanDimensions());
}
if(!filename.empty()) {
PNGHelper::WritePNG(filename, pngBuffer, frameInfo.Width, frameInfo.Height);
} else {
PNGHelper::WritePNG(*stream, pngBuffer, frameInfo.Width, frameInfo.Height);
}
delete[] frameBuffer;
}
void BaseVideoFilter::TakeScreenshot(string romName, VideoFilterType filterType)
{
string romFilename = FolderUtilities::GetFilename(romName, false);
int counter = 0;
string baseFilename = FolderUtilities::CombinePath(FolderUtilities::GetScreenshotFolder(), romFilename);
string ssFilename;
while(true) {
string counterStr = std::to_string(counter);
while(counterStr.length() < 3) {
counterStr = "0" + counterStr;
}
ssFilename = baseFilename + "_" + counterStr + ".png";
ifstream file(ssFilename, ios::in);
if(file) {
file.close();
} else {
break;
}
counter++;
}
TakeScreenshot(filterType, ssFilename);
MessageManager::DisplayMessage("ScreenshotSaved", FolderUtilities::GetFilename(ssFilename, true));
}

View file

@ -1,39 +0,0 @@
#pragma once
#include "stdafx.h"
#include "../Utilities/SimpleLock.h"
#include "EmulationSettings.h"
#include "FrameInfo.h"
#include "VideoHud.h"
class Console;
class BaseVideoFilter
{
private:
uint32_t* _outputBuffer = nullptr;
uint32_t _bufferSize = 0;
SimpleLock _frameLock;
OverscanDimensions _overscan;
bool _isOddFrame;
void UpdateBufferSize();
protected:
shared_ptr<Console> _console;
virtual void ApplyFilter(uint16_t *ppuOutputBuffer) = 0;
virtual void OnBeforeApplyFilter();
bool IsOddFrame();
public:
BaseVideoFilter(shared_ptr<Console> console);
virtual ~BaseVideoFilter();
uint32_t* GetOutputBuffer();
void SendFrame(uint16_t *ppuOutputBuffer, uint32_t frameNumber);
void TakeScreenshot(string romName, VideoFilterType filterType);
void TakeScreenshot(VideoFilterType filterType, string filename, std::stringstream *stream = nullptr, bool rawScreenshot = false);
virtual OverscanDimensions GetOverscan();
virtual FrameInfo GetFrameInfo() = 0;
};

View file

@ -1,80 +0,0 @@
#include "stdafx.h"
#include "BatteryManager.h"
#include "VirtualFile.h"
#include "../Utilities/FolderUtilities.h"
void BatteryManager::Initialize(string romName)
{
_romName = romName;
_saveEnabled = true;
}
string BatteryManager::GetBasePath()
{
return FolderUtilities::CombinePath(FolderUtilities::GetSaveFolder(), _romName);
}
void BatteryManager::SetSaveEnabled(bool enabled)
{
_saveEnabled = enabled;
}
void BatteryManager::SetBatteryProvider(shared_ptr<IBatteryProvider> provider)
{
_provider = provider;
}
void BatteryManager::SetBatteryRecorder(shared_ptr<IBatteryRecorder> recorder)
{
_recorder = recorder;
}
void BatteryManager::SaveBattery(string extension, uint8_t* data, uint32_t length)
{
if(_saveEnabled) {
#ifdef LIBRETRO
if(extension == ".sav") {
//Disable .sav files for libretro
return;
}
#endif
ofstream out(GetBasePath() + extension, ios::binary);
if(out) {
out.write((char*)data, length);
}
}
}
vector<uint8_t> BatteryManager::LoadBattery(string extension)
{
shared_ptr<IBatteryProvider> provider = _provider.lock();
vector<uint8_t> batteryData;
if(provider) {
//Used by movie player to provider initial state of ram at startup
batteryData = provider->LoadBattery(extension);
} else {
VirtualFile file = GetBasePath() + extension;
if(file.IsValid()) {
file.ReadFile(batteryData);
}
}
if(!batteryData.empty()) {
shared_ptr<IBatteryRecorder> recorder = _recorder.lock();
if(recorder) {
//Used by movies to record initial state of battery-backed ram at power on
recorder->OnLoadBattery(extension, batteryData);
}
}
return batteryData;
}
void BatteryManager::LoadBattery(string extension, uint8_t* data, uint32_t length)
{
vector<uint8_t> batteryData = LoadBattery(extension);
memset(data, 0, length);
memcpy(data, batteryData.data(), std::min((uint32_t)batteryData.size(), length));
}

View file

@ -1,38 +0,0 @@
#pragma once
#include "stdafx.h"
class IBatteryProvider
{
public:
virtual vector<uint8_t> LoadBattery(string extension) = 0;
};
class IBatteryRecorder
{
public:
virtual void OnLoadBattery(string extension, vector<uint8_t> batteryData) = 0;
};
class BatteryManager
{
private:
string _romName;
bool _saveEnabled;
string GetBasePath();
std::weak_ptr<IBatteryProvider> _provider;
std::weak_ptr<IBatteryRecorder> _recorder;
public:
void Initialize(string romName);
void SetSaveEnabled(bool enabled);
void SetBatteryProvider(shared_ptr<IBatteryProvider> provider);
void SetBatteryRecorder(shared_ptr<IBatteryRecorder> recorder);
void SaveBattery(string extension, uint8_t* data, uint32_t length);
vector<uint8_t> LoadBattery(string extension);
void LoadBattery(string extension, uint8_t* data, uint32_t length);
};

View file

@ -1,118 +0,0 @@
#pragma once
#include "stdafx.h"
#include "../Utilities/FolderUtilities.h"
#include "Console.h"
#include "BaseControlDevice.h"
#include "IBattery.h"
#include "BatteryManager.h"
class BattleBox : public BaseControlDevice, public IBattery
{
private:
static constexpr int FileSize = 0x200;
uint8_t _lastWrite = 0;
uint8_t _address = 0;
uint8_t _chipSelect = 0;
uint16_t _data[BattleBox::FileSize/2];
uint8_t _output = 0;
bool _writeEnabled = false;
uint8_t _inputBitPosition = 0;
uint16_t _inputData = 0;
bool _isWrite = false;
bool _isRead = false;
protected:
void StreamState(bool saving) override
{
BaseControlDevice::StreamState(saving);
ArrayInfo<uint8_t> data{ (uint8_t*)_data, BattleBox::FileSize };
Stream(_lastWrite, _address, _chipSelect, _output, _writeEnabled, _inputBitPosition, _isWrite, _isRead, _inputData, data);
}
public:
BattleBox(shared_ptr<Console> console) : BaseControlDevice(console, BaseControlDevice::ExpDevicePort)
{
_console->GetBatteryManager()->LoadBattery(".bb", (uint8_t*)_data, BattleBox::FileSize);
}
~BattleBox()
{
SaveBattery();
}
void SaveBattery() override
{
_console->GetBatteryManager()->SaveBattery(".bb", (uint8_t*)_data, BattleBox::FileSize);
}
uint8_t ReadRAM(uint16_t addr) override
{
if(addr == 0x4017) {
if(_lastWrite & 0x01) {
_chipSelect ^= 0x01;
_inputData = 0;
_inputBitPosition = 0;
}
_output ^= 0x01;
uint8_t readBit = 0;
if(_isRead) {
readBit = ((_data[(_chipSelect ? 0x80 : 0) | _address] >> _inputBitPosition) & 0x01) << 3;
}
uint8_t writeBit = (_output << 4);
return readBit | writeBit;
}
return 0;
}
void WriteRAM(uint16_t addr, uint8_t value) override
{
if(value & 0x01 && !(_lastWrite & 0x01)) {
//Clock
_inputData &= ~(1 << _inputBitPosition);
_inputData |= (_output << _inputBitPosition);
_inputBitPosition++;
if(_inputBitPosition > 15) {
if(_isWrite) {
_data[(_chipSelect ? 0x80 : 0) | _address] = _inputData;
_isWrite = false;
} else {
_isRead = false;
//done reading addr/command or write data
uint8_t address = (_inputData & 0x7F);
uint8_t cmd = ((_inputData & 0x7F00) >> 8) ^ 0x7F;
switch(cmd) {
case 0x01:
//read
_address = address;
_isRead = true;
break;
case 0x06:
//program
if(_writeEnabled) {
_address = address;
_isWrite = true;
}
break;
case 0x0C:
//chip erase
if(_writeEnabled) {
memset(_data, 0, BattleBox::FileSize);
}
break;
case 0x0D: break; //busy monitor
case 0x09: _writeEnabled = true; break; //erase/write enable
case 0x0B: _writeEnabled = false; break; //erase/write disable
}
}
_inputBitPosition = 0;
}
}
_lastWrite = value;
}
};

View file

@ -1,50 +0,0 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class Bb : public BaseMapper
{
private:
uint8_t _prgReg;
uint8_t _chrReg;
protected:
uint16_t GetPRGPageSize() override { return 0x2000; }
uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
_prgReg = -1;
_chrReg = 0;
SelectPrgPage4x(0, -4);
UpdateState();
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_prgReg, _chrReg);
if(!saving) {
UpdateState();
}
}
void UpdateState()
{
SetCpuMemoryMapping(0x6000, 0x7FFF, _prgReg, PrgMemoryType::PrgRom);
SelectCHRPage(0, _chrReg);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if((addr & 0x9000) == 0x8000 || addr >= 0xF000){
//A version of Bubble Bobble expects writes to $F000+ to switch the PRG banks
_prgReg = _chrReg = value;
} else {
//For ProWres
_chrReg = value & 0x01;
}
UpdateState();
}
};

View file

@ -1,293 +0,0 @@
//NTSC filter based on Bisqwit's code/algorithm
//As described here:
//http://forums.nesdev.com/viewtopic.php?p=172329
#include "stdafx.h"
#include <cmath>
#include "BisqwitNtscFilter.h"
#include "PPU.h"
#include "EmulationSettings.h"
#include "Console.h"
BisqwitNtscFilter::BisqwitNtscFilter(shared_ptr<Console> console, int resDivider) : BaseVideoFilter(console)
{
_resDivider = resDivider;
_stopThread = false;
_workDone = false;
const int8_t signalLumaLow[4] = { -29, -15, 22, 71 };
const int8_t signalLumaHigh[4] = { 32, 66, 105, 105 };
//Precalculate the low and high signal chosen for each 64 base colors
for(int i = 0; i <= 0x3F; i++) {
int r = (i & 0x0F) >= 0x0E ? 0x1D : i;
int m = signalLumaLow[r / 0x10];
int q = signalLumaHigh[r / 0x10];
if((r & 0x0F) == 13) {
q = m;
} else if((r & 0x0F) == 0) {
m = q;
}
_signalLow[i] = m;
_signalHigh[i] = q;
}
_extraThread = std::thread([=]() {
//Worker thread to improve decode speed
while(!_stopThread) {
_waitWork.Wait();
if(_stopThread) {
break;
}
uint32_t* outputBuffer = GetOutputBuffer();
//Adjust outputbuffer to start at the middle of the picture
if(_keepVerticalRes) {
outputBuffer += GetOverscan().GetScreenWidth() * 8 / _resDivider * (120 - GetOverscan().Top);
} else {
outputBuffer += GetOverscan().GetScreenWidth() * 64 / _resDivider / _resDivider * (120 - GetOverscan().Top);
}
DecodeFrame(120, 239 - GetOverscan().Bottom, _ppuOutputBuffer, outputBuffer, (IsOddFrame() ? 8 : 0) + 327360);
_workDone = true;
}
});
}
BisqwitNtscFilter::~BisqwitNtscFilter()
{
_stopThread = true;
_waitWork.Signal();
_extraThread.join();
}
void BisqwitNtscFilter::ApplyFilter(uint16_t *ppuOutputBuffer)
{
_ppuOutputBuffer = ppuOutputBuffer;
_workDone = false;
_waitWork.Signal();
DecodeFrame(GetOverscan().Top, 120, ppuOutputBuffer, GetOutputBuffer(), (IsOddFrame() ? 8 : 0) + GetOverscan().Top*341*8);
while(!_workDone) {}
}
FrameInfo BisqwitNtscFilter::GetFrameInfo()
{
OverscanDimensions overscan = GetOverscan();
if(_keepVerticalRes) {
return { overscan.GetScreenWidth() * 8 / _resDivider, overscan.GetScreenHeight(), PPU::ScreenWidth, PPU::ScreenHeight, 4 };
} else {
return { overscan.GetScreenWidth() * 8 / _resDivider, overscan.GetScreenHeight() * 8 / _resDivider, PPU::ScreenWidth, PPU::ScreenHeight, 4 };
}
}
void BisqwitNtscFilter::OnBeforeApplyFilter()
{
PictureSettings pictureSettings = _console->GetSettings()->GetPictureSettings();
NtscFilterSettings ntscSettings = _console->GetSettings()->GetNtscFilterSettings();
_keepVerticalRes = ntscSettings.KeepVerticalResolution;
const double pi = std::atan(1.0) * 4;
int contrast = (int)((pictureSettings.Contrast + 1.0) * (pictureSettings.Contrast + 1.0) * 167941);
int saturation = (int)((pictureSettings.Saturation + 1.0) * (pictureSettings.Saturation + 1.0) * 144044);
for(int i = 0; i < 27; i++) {
_sinetable[i] = (int8_t)(8 * std::sin(i * 2 * pi / 12 + pictureSettings.Hue * pi));
}
_yWidth = (int)(12 + ntscSettings.YFilterLength * 22);
_iWidth = (int)(12 + ntscSettings.IFilterLength * 22);
_qWidth = (int)(12 + ntscSettings.QFilterLength * 22);
_y = contrast / _yWidth;
_ir = (int)(contrast * 1.994681e-6 * saturation / _iWidth);
_qr = (int)(contrast * 9.915742e-7 * saturation / _qWidth);
_ig = (int)(contrast * 9.151351e-8 * saturation / _iWidth);
_qg = (int)(contrast * -6.334805e-7 * saturation / _qWidth);
_ib = (int)(contrast * -1.012984e-6 * saturation / _iWidth);
_qb = (int)(contrast * 1.667217e-6 * saturation / _qWidth);
}
void BisqwitNtscFilter::RecursiveBlend(int iterationCount, uint64_t *output, uint64_t *currentLine, uint64_t *nextLine, int pixelsPerCycle, bool verticalBlend)
{
//Blend 2 pixels at once
uint32_t width = GetOverscan().GetScreenWidth() * pixelsPerCycle / 2;
double scanlineIntensity = 1.0 - _console->GetSettings()->GetPictureSettings().ScanlineIntensity;
if(scanlineIntensity < 1.0 && (iterationCount == 2 || _resDivider == 4)) {
//Most likely extremely inefficient scanlines, but works
for(uint32_t x = 0; x < width; x++) {
uint64_t mixed;
if(verticalBlend) {
mixed = ((((currentLine[x] ^ nextLine[x]) & 0xfefefefefefefefeL) >> 1) + (currentLine[x] & nextLine[x]));
} else {
mixed = currentLine[x];
}
uint8_t r = (mixed >> 16) & 0xFF, g = (mixed >> 8) & 0xFF, b = mixed & 0xFF;
uint8_t r2 = (mixed >> 48) & 0xFF, g2 = (mixed >> 40) & 0xFF, b2 = (mixed >> 32) & 0xFF;
r = (uint8_t)(r * scanlineIntensity);
g = (uint8_t)(g * scanlineIntensity);
b = (uint8_t)(b * scanlineIntensity);
r2 = (uint8_t)(r2 * scanlineIntensity);
g2 = (uint8_t)(g2 * scanlineIntensity);
b2 = (uint8_t)(b2 * scanlineIntensity);
output[x] = ((uint64_t)r2 << 48) | ((uint64_t)g2 << 40) | ((uint64_t)b2 << 32) | (r << 16) | (g << 8) | b;
}
} else {
if(verticalBlend) {
for(uint32_t x = 0; x < width; x++) {
output[x] = ((((currentLine[x] ^ nextLine[x]) & 0xfefefefefefefefeL) >> 1) + (currentLine[x] & nextLine[x]));
}
} else {
memcpy(output, currentLine, width * sizeof(uint64_t));
}
}
iterationCount /= 2;
if(iterationCount > 0) {
RecursiveBlend(iterationCount, output - width * iterationCount, currentLine, output, pixelsPerCycle, verticalBlend);
RecursiveBlend(iterationCount, output + width * iterationCount, output, nextLine, pixelsPerCycle, verticalBlend);
}
}
void BisqwitNtscFilter::GenerateNtscSignal(int8_t *ntscSignal, int &phase, int rowNumber)
{
for(int x = -_paddingSize; x < 256 + _paddingSize; x++) {
uint16_t color = _ppuOutputBuffer[(rowNumber << 8) | (x < 0 ? 0 : (x >= 256 ? 255 : x))];
int8_t low = _signalLow[color & 0x3F];
int8_t high = _signalHigh[color & 0x3F];
int8_t emphasis = color >> 6;
uint16_t phaseBitmask = _bitmaskLut[std::abs(phase - (color & 0x0F)) % 12];
uint8_t voltage;
for(int j = 0; j < 8; j++) {
phaseBitmask <<= 1;
voltage = high;
if(phaseBitmask >= 0x40) {
if(phaseBitmask == 0x1000) {
phaseBitmask = 1;
} else {
voltage = low;
}
}
if(phaseBitmask & emphasis) {
voltage -= voltage / 4;
}
ntscSignal[((x + _paddingSize) << 3) | j] = voltage;
}
phase += _signalsPerPixel;
}
phase += (341 - 256 - _paddingSize * 2) * _signalsPerPixel;
}
void BisqwitNtscFilter::DecodeFrame(int startRow, int endRow, uint16_t *ppuOutputBuffer, uint32_t* outputBuffer, int startPhase)
{
int pixelsPerCycle = 8 / _resDivider;
int phase = startPhase;
constexpr int lineWidth = 256 + _paddingSize * 2;
int8_t rowSignal[lineWidth * _signalsPerPixel];
uint32_t rowPixelGap = GetOverscan().GetScreenWidth() * pixelsPerCycle;
if(!_keepVerticalRes) {
rowPixelGap *= pixelsPerCycle;
}
uint32_t* orgBuffer = outputBuffer;
for(int y = startRow; y <= endRow; y++) {
int startCycle = phase % 12;
//Convert the PPU's output to an NTSC signal
GenerateNtscSignal(rowSignal, phase, y);
//Convert the NTSC signal to RGB
NtscDecodeLine(lineWidth * _signalsPerPixel, rowSignal, outputBuffer, (startCycle + 7) % 12);
outputBuffer += rowPixelGap;
}
if(!_keepVerticalRes) {
//Generate the missing vertical lines
outputBuffer = orgBuffer;
int lastRow = 239 - GetOverscan().Bottom;
bool verticalBlend = _console->GetSettings()->GetNtscFilterSettings().VerticalBlend;
for(int y = startRow; y <= endRow; y++) {
uint64_t* currentLine = (uint64_t*)outputBuffer;
uint64_t* nextLine = y == lastRow ? currentLine : (uint64_t*)(outputBuffer + rowPixelGap);
uint64_t* buffer = (uint64_t*)(outputBuffer + rowPixelGap / 2);
RecursiveBlend(4 / _resDivider, buffer, currentLine, nextLine, pixelsPerCycle, verticalBlend);
outputBuffer += rowPixelGap;
}
}
}
/**
* NTSC_DecodeLine(Width, Signal, Target, Phase0)
*
* Convert NES NTSC graphics signal into RGB using integer arithmetics only.
*
* Width: Number of NTSC signal samples.
* For a 256 pixels wide screen, this would be 256*8. 283*8 if you include borders.
*
* Signal: An array of Width samples.
* The following sample values are recognized:
* -29 = Luma 0 low 32 = Luma 0 high (-38 and 6 when attenuated)
* -15 = Luma 1 low 66 = Luma 1 high (-28 and 31 when attenuated)
* 22 = Luma 2 low 105 = Luma 2 high ( -1 and 58 when attenuated)
* 71 = Luma 3 low 105 = Luma 3 high ( 34 and 58 when attenuated)
* In this scale, sync signal would be -59 and colorburst would be -40 and 19,
* but these are not interpreted specially in this function.
* The value is calculated from the relative voltage with:
* floor((voltage-0.518)*1000/12)-15
*
* Target: Pointer to a storage for Width RGB32 samples (00rrggbb).
* Note that the function will produce a RGB32 value for _every_ half-clock-cycle.
* This means 2264 RGB samples if you render 283 pixels per scanline (incl. borders).
* The caller can pick and choose those columns they want from the signal
* to render the picture at their desired resolution.
*
* Phase0: An integer in range 0-11 that describes the phase offset into colors on this scanline.
* Would be generated from the PPU clock cycle counter at the start of the scanline.
* In essence it conveys in one integer the same information that real NTSC signal
* would convey in the colorburst period in the beginning of each scanline.
*/
void BisqwitNtscFilter::NtscDecodeLine(int width, const int8_t* signal, uint32_t* target, int phase0)
{
auto Read = [=](int pos) -> char { return pos >= 0 ? signal[pos] : 0; };
auto Cos = [=](int pos) -> char { return _sinetable[(pos + 36) % 12 + phase0]; };
auto Sin = [=](int pos) -> char { return _sinetable[(pos + 36) % 12 + 3 + phase0]; };
int brightness = (int)(_console->GetSettings()->GetPictureSettings().Brightness * 750);
int ysum = brightness, isum = 0, qsum = 0;
int offset = _resDivider + 4;
int leftOverscan = (GetOverscan().Left + _paddingSize) * 8 + offset;
int rightOverscan = width - (GetOverscan().Right + _paddingSize) * 8 + offset;
for(int s = 0; s < rightOverscan; s++) {
ysum += Read(s) - Read(s - _yWidth);
isum += Read(s) * Cos(s) - Read(s - _iWidth) * Cos(s - _iWidth);
qsum += Read(s) * Sin(s) - Read(s - _qWidth) * Sin(s - _qWidth);
if(!(s % _resDivider) && s >= leftOverscan) {
int r = std::min(255, std::max(0, (ysum*_y + isum*_ir + qsum*_qr) / 65536));
int g = std::min(255, std::max(0, (ysum*_y + isum*_ig + qsum*_qg) / 65536));
int b = std::min(255, std::max(0, (ysum*_y + isum*_ib + qsum*_qb) / 65536));
*target = 0xFF000000 | (r << 16) | (g << 8) | b;
target++;
}
}
}

View file

@ -1,58 +0,0 @@
//NTSC filter based on Bisqwit's code/algorithm
//As described here:
//http://forums.nesdev.com/viewtopic.php?p=172329
#pragma once
#include "stdafx.h"
#include "BaseVideoFilter.h"
#include "../Utilities/AutoResetEvent.h"
class BisqwitNtscFilter : public BaseVideoFilter
{
private:
const uint16_t _bitmaskLut[12] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x100, 0x200, 0x400, 0x800 };
static constexpr int _paddingSize = 6;
static constexpr int _signalsPerPixel = 8;
static constexpr int _signalWidth = 258;
std::thread _extraThread;
AutoResetEvent _waitWork;
atomic<bool> _stopThread;
atomic<bool> _workDone;
bool _keepVerticalRes = false;
int _resDivider = 1;
uint16_t *_ppuOutputBuffer = nullptr;
/* Ywidth, Iwidth and Qwidth are the filter widths for Y,I,Q respectively.
* All widths at 12 produce the best signal quality.
* 12,24,24 would be the closest values matching the NTSC spec.
* But off-spec values 12,22,26 are used here, to bring forth mild
* "chroma dots", an artifacting common with badly tuned TVs.
* Larger values = more horizontal blurring.
*/
int _yWidth, _iWidth, _qWidth;
int _y;
int _ir, _ig, _ib;
int _qr, _qg, _qb;
//To finetune hue, you would have to recalculate sinetable[]. (Coarse changes can be made with Phase0.)
int8_t _sinetable[27]; // 8*sin(x*2pi/12)
int8_t _signalLow[0x40];
int8_t _signalHigh[0x40];
void RecursiveBlend(int iterationCount, uint64_t *output, uint64_t *currentLine, uint64_t *nextLine, int pixelsPerCycle, bool verticalBlend);
void NtscDecodeLine(int width, const int8_t* signal, uint32_t* target, int phase0);
void GenerateNtscSignal(int8_t *ntscSignal, int &phase, int rowNumber);
void DecodeFrame(int startRow, int endRow, uint16_t *ppuOutputBuffer, uint32_t* outputBuffer, int startPhase);
void OnBeforeApplyFilter();
public:
BisqwitNtscFilter(shared_ptr<Console> console, int resDivider);
virtual ~BisqwitNtscFilter();
virtual void ApplyFilter(uint16_t *ppuOutputBuffer);
virtual FrameInfo GetFrameInfo();
};

View file

@ -1,225 +0,0 @@
#include "stdafx.h"
#include "ControlManager.h"
#include "SystemActionManager.h"
#include "FdsSystemActionManager.h"
#include "VsSystemActionManager.h"
#include "BizhawkMovie.h"
#include "VsControlManager.h"
#include "Console.h"
#include "BatteryManager.h"
#include "NotificationManager.h"
BizhawkMovie::BizhawkMovie(shared_ptr<Console> console)
{
_console = console;
_originalPowerOnState = _console->GetSettings()->GetRamPowerOnState();
}
BizhawkMovie::~BizhawkMovie()
{
Stop();
}
void BizhawkMovie::Stop()
{
if(_isPlaying) {
MessageManager::DisplayMessage("Movies", "MovieEnded");
_console->GetNotificationManager()->SendNotification(ConsoleNotificationType::MovieEnded);
if(_console->GetSettings()->CheckFlag(EmulationFlags::PauseOnMovieEnd)) {
_console->GetSettings()->SetFlags(EmulationFlags::Paused);
}
_console->GetSettings()->SetRamPowerOnState(_originalPowerOnState);
_isPlaying = false;
}
_console->GetControlManager()->UnregisterInputProvider(this);
}
bool BizhawkMovie::SetInput(BaseControlDevice *device)
{
SystemActionManager* actionManager = dynamic_cast<SystemActionManager*>(device);
int32_t pollCounter = _console->GetControlManager()->GetPollCounter();
if(actionManager) {
if(pollCounter < (int32_t)_systemActionByFrame.size()) {
uint32_t systemAction = _systemActionByFrame[pollCounter];
if(systemAction & 0x01) {
actionManager->SetBit(SystemActionManager::Buttons::PowerButton);
}
if(systemAction & 0x02) {
actionManager->SetBit(SystemActionManager::Buttons::ResetButton);
}
VsSystemActionManager* vsActionManager = dynamic_cast<VsSystemActionManager*>(device);
if(vsActionManager) {
if(systemAction & 0x04) {
actionManager->SetBit(VsSystemActionManager::VsButtons::InsertCoin1);
}
if(systemAction & 0x08) {
actionManager->SetBit(VsSystemActionManager::VsButtons::InsertCoin2);
}
if(systemAction & 0x10) {
actionManager->SetBit(VsSystemActionManager::VsButtons::ServiceButton);
}
}
FdsSystemActionManager* fdsActionManager = dynamic_cast<FdsSystemActionManager*>(device);
if(fdsActionManager) {
//FDS timings between NesHawk & Mesen are currently significantly different
//So FDS games will always go out of sync
if(systemAction & 0x04) {
fdsActionManager->SetBit(FdsSystemActionManager::FdsButtons::EjectDiskButton);
}
if(systemAction >= 8) {
systemAction >>= 3;
uint32_t diskNumber = 0;
while(!(systemAction & 0x01)) {
systemAction >>= 1;
diskNumber++;
}
fdsActionManager->SetBit(FdsSystemActionManager::FdsButtons::InsertDisk1 + diskNumber);
}
}
}
} else {
int port = device->GetPort();
StandardController* controller = dynamic_cast<StandardController*>(device);
if(controller) {
if(pollCounter < (int32_t)_dataByFrame[port].size()) {
controller->SetTextState(_dataByFrame[port][pollCounter]);
} else {
Stop();
}
}
}
return true;
}
bool BizhawkMovie::InitializeGameData(ZipReader &reader)
{
stringstream fileData;
if(!reader.GetStream("Header.txt", fileData)) {
return false;
}
_console->GetControlManager()->SetPollCounter(0);
while(!fileData.eof()) {
string line;
std::getline(fileData, line);
if(line.compare(0, 4, "SHA1", 4) == 0) {
if(line.size() >= 45) {
HashInfo hashInfo;
hashInfo.Sha1 = line.substr(5, 40);
if(_console->LoadMatchingRom("", hashInfo)) {
return true;
}
}
} else if(line.compare(0, 3, "MD5", 3) == 0) {
if(line.size() >= 36) {
HashInfo hashInfo;
hashInfo.PrgChrMd5 = line.substr(4, 32);
std::transform(hashInfo.PrgChrMd5.begin(), hashInfo.PrgChrMd5.end(), hashInfo.PrgChrMd5.begin(), ::toupper);
if(_console->LoadMatchingRom("", hashInfo)) {
return true;
}
}
}
}
return false;
}
bool BizhawkMovie::InitializeInputData(ZipReader &reader)
{
stringstream inputData;
if(!reader.GetStream("Input Log.txt", inputData)) {
return false;
}
int systemActionCount = 2;
shared_ptr<FdsSystemActionManager> fdsActionManager = _console->GetSystemActionManager<FdsSystemActionManager>();
if(fdsActionManager) {
//Eject disk + Insert Disk #XX
systemActionCount += fdsActionManager->GetSideCount() + 1;
} else {
shared_ptr<VsSystemActionManager> vsActionManager = _console->GetSystemActionManager<VsSystemActionManager>();
if(vsActionManager) {
//Insert coin 1, 2 + service button
systemActionCount += 3;
}
}
while(!inputData.eof()) {
string line;
std::getline(inputData, line);
if(line.size() > 0 && line[0] == '|') {
line.erase(std::remove(line.begin(), line.end(), '|'), line.end());
line = line.substr(0, line.size() - 1);
//Read power/reset/FDS/VS/etc. commands
uint32_t systemAction = 0;
for(int i = 0; i < systemActionCount; i++) {
if(line[i] != '.') {
systemAction |= (1 << i);
}
}
_systemActionByFrame.push_back(systemAction);
line = line.substr(systemActionCount);
int port = 0;
while(line.size() >= 8) {
_dataByFrame[port].push_back(line.substr(0, 8));
line = line.substr(8);
port++;
}
while(port < 4) {
_dataByFrame[port].push_back("........");
port++;
}
}
}
return _dataByFrame[0].size() > 0;
}
bool BizhawkMovie::Play(VirtualFile &file)
{
_console->Pause();
ZipReader reader;
std::stringstream ss;
file.ReadFile(ss);
reader.LoadArchive(ss);
_console->GetNotificationManager()->RegisterNotificationListener(shared_from_this());
_console->GetSettings()->SetRamPowerOnState(RamPowerOnState::AllOnes);
_console->GetBatteryManager()->SetBatteryProvider(shared_from_this());
if(InitializeInputData(reader) && InitializeGameData(reader)) {
//NesHawk initializes memory to 1s
_isPlaying = true;
}
_console->Resume();
return _isPlaying;
}
bool BizhawkMovie::IsPlaying()
{
return _isPlaying;
}
void BizhawkMovie::ProcessNotification(ConsoleNotificationType type, void* parameter)
{
if(type == ConsoleNotificationType::GameLoaded) {
_console->GetControlManager()->RegisterInputProvider(this);
}
}
vector<uint8_t> BizhawkMovie::LoadBattery(string extension)
{
return vector<uint8_t>();
}

View file

@ -1,37 +0,0 @@
#pragma once
#include "stdafx.h"
#include "MovieManager.h"
#include "../Utilities/ZipReader.h"
#include "INotificationListener.h"
#include "BatteryManager.h"
class VirtualFile;
class Console;
class BizhawkMovie : public IMovie, public INotificationListener, public IBatteryProvider, public std::enable_shared_from_this<BizhawkMovie>
{
private:
bool InitializeGameData(ZipReader &reader);
bool InitializeInputData(ZipReader &reader);
void Stop();
protected:
shared_ptr<Console> _console;
vector<uint32_t> _systemActionByFrame;
vector<string> _dataByFrame[4];
bool _isPlaying = false;
RamPowerOnState _originalPowerOnState;
public:
BizhawkMovie(shared_ptr<Console>);
virtual ~BizhawkMovie();
bool SetInput(BaseControlDevice *device) override;
bool Play(VirtualFile &file) override;
bool IsPlaying() override;
// Inherited via INotificationListener
virtual void ProcessNotification(ConsoleNotificationType type, void * parameter) override;
virtual vector<uint8_t> LoadBattery(string extension) override;
};

View file

@ -1,28 +0,0 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class Bmc11160 : public BaseMapper
{
protected:
uint16_t GetPRGPageSize() override { return 0x8000; }
uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
}
void Reset(bool softReset) override
{
BaseMapper::Reset(softReset);
WriteRegister(0x8000, 0);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
uint8_t bank = (value >> 4) & 0x07;
SelectPRGPage(0, bank);
SelectCHRPage(0, (bank << 2) | (value & 0x03));
SetMirroringType(value & 0x80 ? MirroringType::Vertical : MirroringType::Horizontal);
}
};

View file

@ -1,50 +0,0 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class Bmc12in1 : public BaseMapper
{
private:
uint8_t _regs[2];
uint8_t _mode;
protected:
uint16_t GetPRGPageSize() override { return 0x4000; }
uint16_t GetCHRPageSize() override { return 0x1000; }
void InitMapper() override
{
_regs[0] = _regs[1] = 0;
_mode = 0;
UpdateState();
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_regs[0], _regs[1], _mode);
}
void UpdateState()
{
uint8_t bank = (_mode & 0x03) << 3;
SelectCHRPage(0, (_regs[0] >> 3) | (bank << 2));
SelectCHRPage(1, (_regs[1] >> 3) | (bank << 2));
if(_mode & 0x08) {
SelectPrgPage2x(0, bank | (_regs[0] & 0x06));
} else {
SelectPRGPage(0, bank | (_regs[0] & 0x07));
SelectPRGPage(1, bank | 0x07);
}
SetMirroringType(_mode & 0x04 ? MirroringType::Horizontal : MirroringType::Vertical);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
switch(addr & 0xE000) {
case 0xA000: _regs[0] = value; UpdateState(); break;
case 0xC000: _regs[1] = value; UpdateState(); break;
case 0xE000: _mode = value & 0x0F; UpdateState(); break;
}
}
};

View file

@ -1,23 +0,0 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class Bmc190in1 : public BaseMapper
{
protected:
uint16_t GetPRGPageSize() override { return 0x4000; }
uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
WriteRegister(0x8000, 0);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
SelectPRGPage(0, (value >> 2) & 0x07);
SelectPRGPage(1, (value >> 2) & 0x07);
SelectCHRPage(0, (value >> 2) & 0x07);
SetMirroringType(value & 0x01 ? MirroringType::Horizontal : MirroringType::Vertical);
}
};

View file

@ -1,68 +0,0 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class Bmc235 : public BaseMapper
{
private:
bool _openBus = false;
protected:
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
SelectPrgPage2x(0, 0);
SelectCHRPage(0, 0);
}
void Reset(bool softReset) override
{
SelectPrgPage2x(0, 0);
_openBus = false;
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_openBus);
if(!saving && _openBus) {
RemoveCpuMemoryMapping(0x8000, 0xFFFF);
}
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
SetMirroringType((addr & 0x0400) ? MirroringType::ScreenAOnly : (addr & 0x2000) ? MirroringType::Horizontal : MirroringType::Vertical);
const uint8_t config[4][4][2] = {
{ { 0x00, 0 }, { 0x00, 1 }, { 0x00, 1 }, { 0x00, 1 } },
{ { 0x00, 0 }, { 0x00, 1 }, { 0x20, 0 }, { 0x00, 1 } },
{ { 0x00, 0 }, { 0x00, 1 }, { 0x20, 0 }, { 0x40, 0 } },
{ { 0x00, 0 }, { 0x20, 0 }, { 0x40, 0 }, { 0x60, 0 } }
};
uint8_t mode;
switch(GetPRGPageCount()) {
case 64: mode = 0; break;
case 128: mode = 1; break;
case 256: mode = 2; break;
default: mode = 3; break;
};
uint8_t bank = config[mode][addr >> 8 & 0x03][0] | (addr & 0x1F);
_openBus = false;
if(config[mode][addr >> 8 & 0x03][1]) {
//Open bus
_openBus = true;
RemoveCpuMemoryMapping(0x8000, 0xFFFF);
} else if(addr & 0x800) {
bank = (bank << 1) | (addr >> 12 & 0x01);
SelectPRGPage(0, bank);
SelectPRGPage(1, bank);
} else {
SelectPrgPage2x(0, bank << 1);
}
}
};

View file

@ -1,26 +0,0 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class Bmc255 : public BaseMapper
{
protected:
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
WriteRegister(0x8000, 0);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
uint8_t prgBit = (addr & 0x1000) ? 0 : 1;
uint8_t bank = ((addr >> 8) & 0x40) | ((addr >> 6) & 0x3F);
SelectPRGPage(0, bank & ~prgBit);
SelectPRGPage(1, bank | prgBit);
SelectCHRPage(0, ((addr >> 8) & 0x40) | (addr & 0x3F));
SetMirroringType(addr & 0x2000 ? MirroringType::Horizontal : MirroringType::Vertical);
}
};

View file

@ -1,59 +0,0 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class Bmc51 : public BaseMapper
{
private:
uint8_t _bank;
uint8_t _mode;
protected:
virtual uint16_t GetPRGPageSize() override { return 0x2000; }
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
uint16_t RegisterStartAddress() override { return 0x6000; }
uint16_t RegisterEndAddress() override { return 0xFFFF; }
void InitMapper() override
{
_bank = 0;
_mode = 1;
UpdateState();
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_bank, _mode);
if(!saving) {
UpdateState();
}
}
void UpdateState()
{
if(_mode & 0x01) {
SelectPrgPage4x(0, _bank << 2);
SetCpuMemoryMapping(0x6000, 0x7FFF, (0x23 | (_bank << 2)), PrgMemoryType::PrgRom);
} else {
SelectPrgPage2x(0, (_bank << 2) | _mode);
SelectPrgPage2x(1, _bank << 2 | 0x0E);
SetCpuMemoryMapping(0x6000, 0x7FFF, (0x2F | (_bank << 2)), PrgMemoryType::PrgRom);
}
SetMirroringType(_mode == 0x03 ? MirroringType::Horizontal : MirroringType::Vertical);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if(addr <= 0x7FFF) {
_mode = ((value >> 3) & 0x02) | ((value >> 1) & 0x01);
} else if(addr >= 0xC000 && addr <= 0xDFFF) {
_bank = value & 0x0F;
_mode = ((value >> 3) & 0x02) | (_mode & 0x01);
} else {
_bank = value & 0x0F;
}
UpdateState();
}
};

View file

@ -1,83 +0,0 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class Bmc60311C : public BaseMapper
{
private:
uint8_t _innerPrg;
uint8_t _outerPrg;
uint8_t _mode;
protected:
uint16_t GetPRGPageSize() override { return 0x4000; }
uint16_t GetCHRPageSize() override { return 0x2000; }
uint16_t RegisterStartAddress() override { return 0x6000; }
uint16_t RegisterEndAddress() override { return 0xFFFF; }
void InitMapper() override
{
_innerPrg = 0;
_outerPrg = 0;
_mode = 0;
UpdateState();
SelectCHRPage(0, 0);
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_innerPrg, _outerPrg, _mode);
}
void UpdateState()
{
uint8_t page = _outerPrg | ((_mode & 0x04) ? 0 : _innerPrg);
switch(_mode & 0x03) {
case 0:
//0: NROM-128: Same inner/outer 16 KiB bank at CPU $8000-$BFFF and $C000-$FFFF
SelectPRGPage(0, page);
SelectPRGPage(1, page);
break;
case 1:
//1: NROM-256: 32 kiB bank at CPU $8000-$FFFF (Selected inner/outer bank SHR 1)
SelectPrgPage2x(0, page & 0xFE);
break;
case 2:
//2: UNROM: Inner/outer bank at CPU $8000-BFFF, fixed inner bank 7 within outer bank at $C000-$FFFF
SelectPRGPage(0, page);
SelectPRGPage(1, _outerPrg | 7);
break;
case 3:
//Unknown
break;
}
SetMirroringType(_mode & 0x08 ? MirroringType::Horizontal : MirroringType::Vertical);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if(addr >= 0x8000) {
_innerPrg = value & 0x07;
UpdateState();
} else {
switch(addr & 0xE001) {
case 0x6000:
_mode = value & 0x0F;
UpdateState();
break;
case 0x6001:
_outerPrg = value;
UpdateState();
break;
}
}
}
};

View file

@ -1,48 +0,0 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class Bmc63 : public BaseMapper
{
private:
bool _openBus;
protected:
virtual uint16_t GetPRGPageSize() override { return 0x2000; }
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
WriteRegister(0x8000, 0);
}
void Reset(bool softReset) override
{
_openBus = false;
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_openBus);
if(!saving && _openBus) {
RemoveCpuMemoryMapping(0x8000, 0xBFFF);
}
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
_openBus = ((addr & 0x0300) == 0x0300);
if(_openBus) {
RemoveCpuMemoryMapping(0x8000, 0xBFFF);
} else {
SelectPRGPage(0, (addr >> 1 & 0x1FC) | ((addr & 0x2) ? 0x0 : (addr >> 1 & 0x2) | 0x0));
SelectPRGPage(1, (addr >> 1 & 0x1FC) | ((addr & 0x2) ? 0x1 : (addr >> 1 & 0x2) | 0x1));
}
SelectPRGPage(2, (addr >> 1 & 0x1FC) | ((addr & 0x2) ? 0x2 : (addr >> 1 & 0x2) | 0x0));
SelectPRGPage(3, (addr & 0x800) ? ((addr & 0x07C) | ((addr & 0x06) ? 0x03 : 0x01)) : ((addr >> 1 & 0x01FC) | ((addr & 0x02) ? 0x03 : ((addr >> 1 & 0x02) | 0x01))));
SetMirroringType(addr & 0x01 ? MirroringType::Horizontal : MirroringType::Vertical);
}
};

View file

@ -1,63 +0,0 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class Bmc64in1NoRepeat : public BaseMapper
{
private:
uint8_t _regs[4];
protected:
uint16_t GetPRGPageSize() override { return 0x4000; }
uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
AddRegisterRange(0x5000, 0x5003, MemoryOperation::Write);
}
void Reset(bool softReset) override
{
BaseMapper::Reset(softReset);
_regs[0] = 0x80;
_regs[1] = 0x43;
_regs[2] = _regs[3] = 0;
UpdateState();
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_regs[0], _regs[1], _regs[2], _regs[3]);
}
void UpdateState()
{
if(_regs[0] & 0x80) {
if(_regs[1] & 0x80) {
SelectPrgPage2x(0, (_regs[1] & 0x1F) << 1);
} else {
int bank = ((_regs[1] & 0x1F) << 1) | ((_regs[1] >> 6) & 0x01);
SelectPRGPage(0, bank);
SelectPRGPage(1, bank);
}
} else {
SelectPRGPage(1, ((_regs[1] & 0x1F) << 1) | ((_regs[1] >> 6) & 0x01));
}
SetMirroringType(_regs[0] & 0x20 ? MirroringType::Horizontal : MirroringType::Vertical);
SelectCHRPage(0, (_regs[2] << 2) | ((_regs[0] >> 1) & 0x03));
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if(addr < 0x8000) {
_regs[addr & 0x03] = value;
} else {
_regs[3] = value;
}
UpdateState();
}
};

View file

@ -1,97 +0,0 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class Bmc70in1 : public BaseMapper
{
private:
uint8_t _bankMode;
uint8_t _outerBank;
uint8_t _prgReg;
uint8_t _chrReg;
bool _useOuterBank;
protected:
uint32_t GetDipSwitchCount() override { return 4; }
uint16_t GetPRGPageSize() override { return 0x4000; }
uint16_t GetCHRPageSize() override { return 0x2000; }
bool AllowRegisterRead() override { return true; }
void InitMapper() override
{
_prgReg = 0;
_chrReg = 0;
if(HasChrRom()) {
_useOuterBank = false;
} else {
_useOuterBank = true;
}
SelectCHRPage(0, 0);
UpdateState();
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_bankMode, _outerBank, _prgReg, _chrReg);
}
void Reset(bool softReset) override
{
BaseMapper::Reset(softReset);
_bankMode = 0;
_outerBank = 0;
}
void UpdateState()
{
switch(_bankMode) {
case 0x00: case 0x10:
SelectPRGPage(0, _outerBank | _prgReg);
SelectPRGPage(1, _outerBank | 7);
break;
case 0x20:
SelectPrgPage2x(0, (_outerBank | _prgReg) & 0xFE);
break;
case 0x30:
SelectPRGPage(0, _outerBank | _prgReg);
SelectPRGPage(1, _outerBank | _prgReg);
break;
}
if(!_useOuterBank) {
SelectCHRPage(0, _chrReg);
}
}
uint8_t ReadRegister(uint16_t addr) override
{
if(_bankMode == 0x10) {
return InternalReadRam((addr & 0xFFF0) | GetDipSwitches());
} else {
return InternalReadRam(addr);
}
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if(addr & 0x4000) {
_bankMode = addr & 0x30;
_prgReg = addr & 0x07;
} else {
SetMirroringType(addr & 0x20 ? MirroringType::Horizontal : MirroringType::Vertical);
if(_useOuterBank) {
_outerBank = (addr & 0x03) << 3;
} else {
_chrReg = addr & 0x07;
}
}
UpdateState();
}
};

View file

@ -1,54 +0,0 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class Bmc80013B : public BaseMapper
{
private:
uint8_t _regs[2];
uint8_t _mode;
protected:
uint16_t GetPRGPageSize() override { return 0x4000; }
uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
SelectCHRPage(0, 0);
}
void Reset(bool softReset) override
{
_regs[0] = _regs[1] = _mode = 0;
UpdateState();
}
void StreamState(bool saving) override
{
Stream(_regs[0], _regs[1], _mode);
}
void UpdateState()
{
if(_mode & 0x02) {
SelectPRGPage(0, (_regs[0] & 0x0F) | (_regs[1] & 0x70));
} else {
SelectPRGPage(0, _regs[0] & 0x03);
}
SelectPRGPage(1, _regs[1] & 0x7F);
SetMirroringType(_regs[0] & 0x10 ? MirroringType::Vertical : MirroringType::Horizontal);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
uint8_t reg = (addr >> 13) & 0x03;
if(reg == 0) {
_regs[0] = value;
} else {
_regs[1] = value;
_mode = reg;
}
UpdateState();
}
};

View file

@ -1,33 +0,0 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class Bmc810544CA1 : public BaseMapper
{
protected:
uint16_t GetPRGPageSize() override { return 0x4000; }
uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
}
void Reset(bool softReset) override
{
BaseMapper::Reset(softReset);
WriteRegister(0x8000, 0);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
uint16_t bank = (addr >> 6) & 0xFFFE;
if(addr & 0x40) {
SelectPrgPage2x(0, bank);
} else {
SelectPRGPage(0, bank | ((addr >> 5) & 0x01));
SelectPRGPage(1, bank | ((addr >> 5) & 0x01));
}
SelectCHRPage(0, addr & 0x0F);
SetMirroringType(addr & 0x10 ? MirroringType::Horizontal : MirroringType::Vertical);
}
};

View file

@ -1,63 +0,0 @@
#pragma once
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class Bmc8157 : public BaseMapper
{
private:
uint16_t _lastAddr;
protected:
uint32_t GetDipSwitchCount() override { return 1; }
uint16_t GetPRGPageSize() override { return 0x4000; }
uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
_lastAddr = 0;
UpdateState();
SelectCHRPage(0, 0);
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_lastAddr);
if(!saving) {
UpdateState();
}
}
void UpdateState()
{
uint8_t innerPrg0 = (_lastAddr >> 2) & 0x07;
uint8_t innerPrg1 = ((_lastAddr >> 7) & 0x01) | ((_lastAddr >> 8) & 0x02);
uint8_t outer128Prg = (_lastAddr >> 5) & 0x03;
uint8_t outer512Prg = (_lastAddr >> 8) & 0x01;
int baseBank;
if(innerPrg1 == 0) {
baseBank = 0;
} else if(innerPrg1 == 1) {
baseBank = innerPrg0;
} else {
baseBank = 7;
}
if(outer512Prg && _prgSize <= 1024 * 512 && GetDipSwitches() != 0) {
RemoveCpuMemoryMapping(0x8000, 0xFFFF);
} else {
SelectPRGPage(0, (outer512Prg << 6) | (outer128Prg << 3) | innerPrg0);
SelectPRGPage(1, (outer512Prg << 6) | (outer128Prg << 3) | baseBank);
SetMirroringType(_lastAddr & 0x02 ? MirroringType::Horizontal : MirroringType::Vertical);
}
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
_lastAddr = addr;
UpdateState();
}
};

View file

@ -1,59 +0,0 @@
#pragma once
#include "stdafx.h"
#include "MMC3.h"
class Bmc830118C : public MMC3
{
private:
uint8_t _reg;
protected:
void InitMapper() override
{
_reg = 0;
MMC3::InitMapper();
AddRegisterRange(0x6800, 0x68FF, MemoryOperation::Write);
}
void Reset(bool softReset) override
{
_reg = 0;
MMC3::Reset(softReset);
}
void StreamState(bool saving) override
{
MMC3::StreamState(saving);
Stream(_reg);
}
void SelectCHRPage(uint16_t slot, uint16_t page, ChrMemoryType memoryType = ChrMemoryType::Default) override
{
MMC3::SelectCHRPage(slot, ((_reg & 0x0C) << 5) | (page & 0x7F), memoryType);
}
void SelectPRGPage(uint16_t slot, uint16_t page, PrgMemoryType memoryType = PrgMemoryType::PrgRom) override
{
if((_reg & 0x0C) == 0x0C) {
if(slot == 0) {
MMC3::SelectPRGPage(0, ((_reg & 0x0C) << 2) | (page & 0x0F));
MMC3::SelectPRGPage(2, 0x32 | (page & 0x0F));
} else if(slot == 1) {
MMC3::SelectPRGPage(1, ((_reg & 0x0C) << 2) | (page & 0x0F));
MMC3::SelectPRGPage(3, 0x32 | (page & 0x0F));
}
} else {
MMC3::SelectPRGPage(slot, ((_reg & 0x0C) << 2) | (page & 0x0F));
}
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if(addr < 0x8000) {
_reg = value;
UpdateState();
} else {
MMC3::WriteRegister(addr, value);
}
}
};

View file

@ -1,54 +0,0 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class Bmc830425C4391T : public BaseMapper
{
private:
uint8_t _innerReg;
uint8_t _outerReg;
uint8_t _prgMode;
protected:
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
_innerReg = 0;
_outerReg = 0;
_prgMode = 0;
SelectCHRPage(0, 0);
UpdateState();
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_innerReg, _outerReg, _prgMode);
}
void UpdateState()
{
if(_prgMode) {
//UNROM mode
SelectPRGPage(0, (_innerReg & 0x07) | (_outerReg << 3));
SelectPRGPage(1, 0x07 | (_outerReg << 3));
} else {
//UOROM mode
SelectPRGPage(0, _innerReg | (_outerReg << 3));
SelectPRGPage(1, 0x0F | (_outerReg << 3));
}
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
_innerReg = value & 0x0F;
if((addr & 0xFFE0) == 0xF0E0) {
_outerReg = addr & 0x0F;
_prgMode = (addr >> 4) & 0x01;
}
UpdateState();
}
};

View file

@ -1,46 +0,0 @@
#pragma once
#include "stdafx.h"
#include "MMC3.h"
class Bmc8in1 : public MMC3
{
private:
uint8_t _reg;
protected:
void InitMapper() override
{
_reg = 0;
MMC3::InitMapper();
}
void StreamState(bool saving) override
{
MMC3::StreamState(saving);
Stream(_reg);
}
void SelectCHRPage(uint16_t slot, uint16_t page, ChrMemoryType memoryType = ChrMemoryType::Default) override
{
MMC3::SelectCHRPage(slot, ((_reg & 0x0C) << 5) | (page & 0x7F), memoryType);
}
void SelectPRGPage(uint16_t slot, uint16_t page, PrgMemoryType memoryType = PrgMemoryType::PrgRom) override
{
if(_reg & 0x10) {
MMC3::SelectPRGPage(slot, ((_reg & 0x0C) << 2) | (page & 0x0F));
} else {
SelectPrgPage4x(0, (_reg & 0x0F) << 2);
}
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if(addr & 0x1000) {
_reg = value;
UpdateState();
} else {
MMC3::WriteRegister(addr, value);
}
}
};

View file

@ -1,38 +0,0 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class BmcG146 : public BaseMapper
{
protected:
uint16_t GetPRGPageSize() override { return 0x4000; }
uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
}
void Reset(bool softReset) override
{
BaseMapper::Reset(softReset);
WriteRegister(0x8000, 0);
SelectCHRPage(0, 0);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if(addr & 0x800) {
SelectPRGPage(0, (addr & 0x1F) | (addr & ((addr & 0x40) >> 6)));
SelectPRGPage(1, (addr & 0x18) | 0x07);
} else {
if(addr & 0x40) {
SelectPRGPage(0, addr & 0x1F);
SelectPRGPage(1, addr & 0x1F);
} else {
SelectPrgPage2x(0, addr & 0x1E);
}
}
SetMirroringType(addr & 0x80 ? MirroringType::Horizontal : MirroringType::Vertical);
}
};

View file

@ -1,64 +0,0 @@
#pragma once
#include "stdafx.h"
#include "MMC3.h"
class BmcGn45 : public MMC3
{
private:
uint8_t _selectedBlock = 0;
bool _wramEnabled = false;
protected:
uint16_t RegisterStartAddress() override { return 0x6000; }
uint16_t RegisterEndAddress() override { return 0xFFFF; }
void StreamState(bool saving) override
{
MMC3::StreamState(saving);
Stream(_selectedBlock, _wramEnabled);
}
void Reset(bool softReset) override
{
MMC3::Reset(softReset);
if(softReset) {
_selectedBlock = 0;
_wramEnabled = false;
ResetMmc3();
UpdateState();
}
}
void SelectCHRPage(uint16_t slot, uint16_t page, ChrMemoryType memoryType = ChrMemoryType::Default) override
{
MMC3::SelectCHRPage(slot, (page & 0x7F) | (_selectedBlock << 3), memoryType);
}
void SelectPRGPage(uint16_t slot, uint16_t page, PrgMemoryType memoryType = PrgMemoryType::PrgRom) override
{
MMC3::SelectPRGPage(slot, (page & 0x0F) | _selectedBlock, memoryType);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if(addr < 0x7000) {
if(!_wramEnabled) {
_selectedBlock = addr & 0x30;
_wramEnabled = (addr & 0x80) != 0;
UpdateState();
} else {
WritePrgRam(addr, value);
}
} else if(addr < 0x8000) {
if(!_wramEnabled) {
_selectedBlock = value & 0x30;
UpdateState();
} else {
WritePrgRam(addr, value);
}
} else {
MMC3::WriteRegister(addr, value);
}
}
};

View file

@ -1,116 +0,0 @@
#pragma once
#include "stdafx.h"
#include "MMC3.h"
class BmcHpxx : public MMC3
{
private:
uint8_t _exRegs[5];
bool _locked;
protected:
uint32_t GetDipSwitchCount() override { return 4; }
bool AllowRegisterRead() override { return true; }
void InitMapper() override
{
memset(_exRegs, 0, sizeof(_exRegs));
_locked = false;
MMC3::InitMapper();
AddRegisterRange(0x5000, 0x5FFF, MemoryOperation::Any);
RemoveRegisterRange(0x8000, 0xFFFF, MemoryOperation::Read);
}
void Reset(bool softReset) override
{
MMC3::Reset(softReset);
memset(_exRegs, 0, sizeof(_exRegs));
_locked = false;
MMC3::ResetMmc3();
UpdateState();
}
void StreamState(bool saving) override
{
MMC3::StreamState(saving);
Stream(_exRegs[0], _exRegs[1], _exRegs[2], _exRegs[3], _exRegs[4], _locked);
}
void SelectCHRPage(uint16_t slot, uint16_t page, ChrMemoryType memoryType = ChrMemoryType::Default) override
{
if(_exRegs[0] & 0x04) {
switch(_exRegs[0] & 0x03) {
case 0:
case 1: SelectChrPage8x(0, (_exRegs[2] & 0x3F) << 3); break;
case 2: SelectChrPage8x(0, ((_exRegs[2] & 0x3E) | (_exRegs[4] & 0x01)) << 3); break;
case 3: SelectChrPage8x(0, ((_exRegs[2] & 0x3C) | (_exRegs[4] & 0x03)) << 3); break;
}
} else {
uint8_t base, mask;
if(_exRegs[0] & 0x01) {
base = _exRegs[2] & 0x30;
mask = 0x7F;
} else {
base = _exRegs[2] & 0x20;
mask = 0xFF;
}
MMC3::SelectCHRPage(slot, (page & mask) | (base << 3));
}
}
void SelectPRGPage(uint16_t slot, uint16_t page, PrgMemoryType memoryType = PrgMemoryType::PrgRom) override
{
if(_exRegs[0] & 0x04) {
if((_exRegs[0] & 0x0F) == 0x04) {
SelectPrgPage2x(0, (_exRegs[1] & 0x1F) << 1);
SelectPrgPage2x(1, (_exRegs[1] & 0x1F) << 1);
} else {
SelectPrgPage4x(0, (_exRegs[1] & 0x1E) << 1);
}
} else {
uint8_t base, mask;
if(_exRegs[0] & 0x02) {
base = _exRegs[1] & 0x18;
mask = 0x0F;
} else {
base = _exRegs[1] & 0x10;
mask = 0x1F;
}
MMC3::SelectPRGPage(slot, (page & mask) | (base << 1));
}
}
void UpdateMirroring() override
{
if(_exRegs[0] & 0x04) {
SetMirroringType(_exRegs[4] & 0x04 ? MirroringType::Vertical : MirroringType::Horizontal);
} else {
MMC3::UpdateMirroring();
}
}
uint8_t ReadRegister(uint16_t addr) override
{
return GetDipSwitches();
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if(addr < 0x8000) {
if(!_locked) {
_exRegs[addr & 0x03] = value;
_locked = (value & 0x80) != 0;
UpdatePrgMapping();
UpdateChrMapping();
}
} else {
if(_exRegs[0] & 0x04) {
_exRegs[4] = value;
UpdateChrMapping();
} else {
MMC3::WriteRegister(addr, value);
}
}
}
};

View file

@ -1,26 +0,0 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class BmcK3046 : public BaseMapper
{
protected:
virtual uint16_t GetPRGPageSize() override { return 0x4000; }
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
SelectPRGPage(0, 0);
SelectPRGPage(1, 7);
SelectCHRPage(0, 0);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
uint8_t inner = value & 0x07;
uint8_t outer = value & 0x38;
SelectPRGPage(0, outer | inner);
SelectPRGPage(1, outer | 7);
}
};

View file

@ -1,35 +0,0 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class BmcNtd03 : public BaseMapper
{
protected:
uint16_t GetPRGPageSize() override { return 0x4000; }
uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
}
void Reset(bool softReset) override
{
BaseMapper::Reset(softReset);
WriteRegister(0x8000, 0);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
uint8_t prg = ((addr >> 10) & 0x1E);
uint8_t chr= ((addr & 0x0300) >> 5) | (addr & 0x07);
if(addr & 0x80) {
SelectPRGPage(0, prg | ((addr >> 6) & 1));
SelectPRGPage(1, prg | ((addr >> 6) & 1));
} else {
SelectPrgPage2x(0, prg & 0xFE);
}
SelectCHRPage(0, chr);
SetMirroringType(addr & 0x400 ? MirroringType::Horizontal : MirroringType::Vertical);
}
};

View file

@ -1,22 +0,0 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class BnRom : public BaseMapper
{
protected:
virtual uint16_t GetPRGPageSize() override { return 0x8000; }
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
SelectPRGPage(0, GetPowerOnByte());
SelectCHRPage(0, 0);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
//"While the original BNROM board connects only 2 bits, it is recommended that emulators implement this as an 8-bit register allowing selection of up to 8 MB PRG ROM if present."
SelectPRGPage(0, value);
}
};

View file

@ -1,111 +0,0 @@
#include "stdafx.h"
#include "Breakpoint.h"
Breakpoint::Breakpoint()
{
}
Breakpoint::~Breakpoint()
{
}
bool Breakpoint::Matches(uint32_t memoryAddr, AddressTypeInfo &info)
{
if(_memoryType == DebugMemoryType::CpuMemory) {
if(_startAddr == -1) {
return true;
} else if(_endAddr == -1) {
return (int32_t)memoryAddr == _startAddr;
} else {
return (int32_t)memoryAddr >= _startAddr && (int32_t)memoryAddr <= _endAddr;
}
} else if(
(_memoryType == DebugMemoryType::PrgRom && info.Type == AddressType::PrgRom) ||
(_memoryType == DebugMemoryType::WorkRam && info.Type == AddressType::WorkRam) ||
(_memoryType == DebugMemoryType::SaveRam && info.Type == AddressType::SaveRam)
) {
if(_startAddr == -1) {
return true;
} else if(_endAddr == -1) {
return info.Address == _startAddr;
} else {
return info.Address >= _startAddr && info.Address <= _endAddr;
}
}
return false;
}
bool Breakpoint::Matches(uint32_t memoryAddr, PpuAddressTypeInfo &info)
{
if(_memoryType == DebugMemoryType::PpuMemory) {
if(_startAddr == -1) {
return true;
} else if(_endAddr == -1) {
return (int32_t)memoryAddr == _startAddr;
} else {
return (int32_t)memoryAddr >= _startAddr && (int32_t)memoryAddr <= _endAddr;
}
} else if(
(_memoryType == DebugMemoryType::ChrRam && info.Type == PpuAddressType::ChrRam) ||
(_memoryType == DebugMemoryType::ChrRom && info.Type == PpuAddressType::ChrRom) ||
(_memoryType == DebugMemoryType::PaletteMemory && info.Type == PpuAddressType::PaletteRam) ||
(_memoryType == DebugMemoryType::NametableRam && info.Type == PpuAddressType::NametableRam)
) {
if(_startAddr == -1) {
return true;
} else if(_endAddr == -1) {
return info.Address == _startAddr;
} else {
return info.Address >= _startAddr && info.Address <= _endAddr;
}
}
return false;
}
bool Breakpoint::HasBreakpointType(BreakpointType type)
{
switch(type) {
case BreakpointType::Global: return (_type == BreakpointTypeFlags::Global);
case BreakpointType::Execute: return (_type & BreakpointTypeFlags::Execute) != 0;
case BreakpointType::ReadRam: return (_type & BreakpointTypeFlags::ReadRam) != 0;
case BreakpointType::WriteRam: return (_type & BreakpointTypeFlags::WriteRam) != 0;
case BreakpointType::ReadVram: return (_type & BreakpointTypeFlags::ReadVram) != 0;
case BreakpointType::WriteVram: return (_type & BreakpointTypeFlags::WriteVram) != 0;
case BreakpointType::DummyReadRam: return (_type & BreakpointTypeFlags::ReadRam) != 0 && _processDummyReadWrites;
case BreakpointType::DummyWriteRam: return (_type & BreakpointTypeFlags::WriteRam) != 0 && _processDummyReadWrites;
}
return false;
}
string Breakpoint::GetCondition()
{
return _condition;
}
bool Breakpoint::HasCondition()
{
return _condition[0] != 0;
}
void Breakpoint::ClearCondition()
{
memset(_condition, 0, sizeof(_condition));
}
uint32_t Breakpoint::GetId()
{
return _id;
}
bool Breakpoint::IsEnabled()
{
return _enabled;
}
bool Breakpoint::IsMarked()
{
return _markEvent;
}

View file

@ -1,43 +0,0 @@
#pragma once
#include "stdafx.h"
#include "DebuggerTypes.h"
class Breakpoint
{
private:
enum BreakpointTypeFlags
{
Global = 0,
Execute = 1,
ReadRam = 2,
WriteRam = 4,
ReadVram = 8,
WriteVram = 16,
};
public:
Breakpoint();
~Breakpoint();
bool Matches(uint32_t memoryAddr, AddressTypeInfo &info);
bool Matches(uint32_t memoryAddr, PpuAddressTypeInfo &info);
bool HasBreakpointType(BreakpointType type);
string GetCondition();
bool HasCondition();
void ClearCondition();
uint32_t GetId();
bool IsEnabled();
bool IsMarked();
private:
uint32_t _id;
DebugMemoryType _memoryType;
BreakpointTypeFlags _type;
int32_t _startAddr;
int32_t _endAddr;
bool _enabled;
bool _markEvent;
bool _processDummyReadWrites;
char _condition[1000];
};

View file

@ -1,35 +0,0 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class Bs5 : public BaseMapper
{
protected:
uint32_t GetDipSwitchCount() override { return 2; }
uint16_t GetPRGPageSize() override { return 0x2000; }
uint16_t GetCHRPageSize() override { return 0x800; }
void InitMapper() override
{
for(int i = 0; i < 4; i++) {
SelectPRGPage(i, -1);
SelectCHRPage(i, -1);
}
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
int bank = (addr >> 10) & 0x03;
switch(addr & 0xF000) {
case 0x8000:
SelectCHRPage(bank, addr & 0x1F);
break;
case 0xA000:
if(addr & (1 << (GetDipSwitches() + 4))) {
SelectPRGPage(bank, addr & 0x0F);
}
break;
}
}
};

View file

@ -1,52 +0,0 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class CNROM : public BaseMapper
{
private:
bool _enableCopyProtection;
protected:
virtual uint16_t GetPRGPageSize() override { return 0x8000; }
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
void InitMapper() override
{
SelectPRGPage(0, 0);
SelectCHRPage(0, GetPowerOnByte());
}
bool HasBusConflicts() override { return (_romInfo.MapperID == 3 && _romInfo.SubMapperID == 2) || _romInfo.MapperID == 185; }
void WriteRegister(uint16_t addr, uint8_t value) override
{
if(_enableCopyProtection) {
//Submapper 0: Use heuristics - "if C AND $0F is nonzero, and if C does not equal $13: CHR is enabled"
//Submapper 4: Enable CHR-ROM if bits 0..1 of the latch hold the value 0, otherwise disable CHR-ROM.
//Submapper 5: Enable CHR-ROM if bits 0..1 of the latch hold the value 1, otherwise disable CHR-ROM.
//Submapper 6: Enable CHR-ROM if bits 0..1 of the latch hold the value 2, otherwise disable CHR-ROM.
//Submapper 7: Enable CHR-ROM if bits 0..1 of the latch hold the value 3, otherwise disable CHR-ROM.
bool validAccess = (
(_romInfo.SubMapperID == 0 && (value & 0x0F) != 0 && value != 0x13) ||
(_romInfo.SubMapperID == 4 && (value & 0x03) == 0) ||
(_romInfo.SubMapperID == 5 && (value & 0x03) == 1) ||
(_romInfo.SubMapperID == 6 && (value & 0x03) == 2) ||
(_romInfo.SubMapperID == 7 && (value & 0x03) == 3)
);
if(validAccess) {
SelectCHRPage(0, 0);
} else {
RemovePpuMemoryMapping(0x0000, 0x1FFF);
}
} else {
SelectCHRPage(0, value);
}
}
public:
CNROM(bool enableCopyProtection) : _enableCopyProtection(enableCopyProtection)
{
}
};

View file

@ -1,487 +0,0 @@
#include "stdafx.h"
#include <random>
#include <assert.h>
#include "CPU.h"
#include "PPU.h"
#include "APU.h"
#include "DeltaModulationChannel.h"
#include "Debugger.h"
#include "NsfMapper.h"
#include "Console.h"
CPU::CPU(shared_ptr<Console> console)
{
_console = console;
_memoryManager = _console->GetMemoryManager();
Func opTable[] = {
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
&CPU::BRK, &CPU::ORA, &CPU::HLT, &CPU::SLO, &CPU::NOP, &CPU::ORA, &CPU::ASL_Memory, &CPU::SLO, &CPU::PHP, &CPU::ORA, &CPU::ASL_Acc, &CPU::AAC, &CPU::NOP, &CPU::ORA, &CPU::ASL_Memory, &CPU::SLO, //0
&CPU::BPL, &CPU::ORA, &CPU::HLT, &CPU::SLO, &CPU::NOP, &CPU::ORA, &CPU::ASL_Memory, &CPU::SLO, &CPU::CLC, &CPU::ORA, &CPU::NOP, &CPU::SLO, &CPU::NOP, &CPU::ORA, &CPU::ASL_Memory, &CPU::SLO, //1
&CPU::JSR, &CPU::AND, &CPU::HLT, &CPU::RLA, &CPU::BIT, &CPU::AND, &CPU::ROL_Memory, &CPU::RLA, &CPU::PLP, &CPU::AND, &CPU::ROL_Acc, &CPU::AAC, &CPU::BIT, &CPU::AND, &CPU::ROL_Memory, &CPU::RLA, //2
&CPU::BMI, &CPU::AND, &CPU::HLT, &CPU::RLA, &CPU::NOP, &CPU::AND, &CPU::ROL_Memory, &CPU::RLA, &CPU::SEC, &CPU::AND, &CPU::NOP, &CPU::RLA, &CPU::NOP, &CPU::AND, &CPU::ROL_Memory, &CPU::RLA, //3
&CPU::RTI, &CPU::EOR, &CPU::HLT, &CPU::SRE, &CPU::NOP, &CPU::EOR, &CPU::LSR_Memory, &CPU::SRE, &CPU::PHA, &CPU::EOR, &CPU::LSR_Acc, &CPU::ASR, &CPU::JMP_Abs, &CPU::EOR, &CPU::LSR_Memory, &CPU::SRE, //4
&CPU::BVC, &CPU::EOR, &CPU::HLT, &CPU::SRE, &CPU::NOP, &CPU::EOR, &CPU::LSR_Memory, &CPU::SRE, &CPU::CLI, &CPU::EOR, &CPU::NOP, &CPU::SRE, &CPU::NOP, &CPU::EOR, &CPU::LSR_Memory, &CPU::SRE, //5
&CPU::RTS, &CPU::ADC, &CPU::HLT, &CPU::RRA, &CPU::NOP, &CPU::ADC, &CPU::ROR_Memory, &CPU::RRA, &CPU::PLA, &CPU::ADC, &CPU::ROR_Acc, &CPU::ARR, &CPU::JMP_Ind, &CPU::ADC, &CPU::ROR_Memory, &CPU::RRA, //6
&CPU::BVS, &CPU::ADC, &CPU::HLT, &CPU::RRA, &CPU::NOP, &CPU::ADC, &CPU::ROR_Memory, &CPU::RRA, &CPU::SEI, &CPU::ADC, &CPU::NOP, &CPU::RRA, &CPU::NOP, &CPU::ADC, &CPU::ROR_Memory, &CPU::RRA, //7
&CPU::NOP, &CPU::STA, &CPU::NOP, &CPU::SAX, &CPU::STY, &CPU::STA, &CPU::STX, &CPU::SAX, &CPU::DEY, &CPU::NOP, &CPU::TXA, &CPU::UNK, &CPU::STY, &CPU::STA, &CPU::STX, &CPU::SAX, //8
&CPU::BCC, &CPU::STA, &CPU::HLT, &CPU::AXA, &CPU::STY, &CPU::STA, &CPU::STX, &CPU::SAX, &CPU::TYA, &CPU::STA, &CPU::TXS, &CPU::TAS, &CPU::SYA, &CPU::STA, &CPU::SXA, &CPU::AXA, //9
&CPU::LDY, &CPU::LDA, &CPU::LDX, &CPU::LAX, &CPU::LDY, &CPU::LDA, &CPU::LDX, &CPU::LAX, &CPU::TAY, &CPU::LDA, &CPU::TAX, &CPU::ATX, &CPU::LDY, &CPU::LDA, &CPU::LDX, &CPU::LAX, //A
&CPU::BCS, &CPU::LDA, &CPU::HLT, &CPU::LAX, &CPU::LDY, &CPU::LDA, &CPU::LDX, &CPU::LAX, &CPU::CLV, &CPU::LDA, &CPU::TSX, &CPU::LAS, &CPU::LDY, &CPU::LDA, &CPU::LDX, &CPU::LAX, //B
&CPU::CPY, &CPU::CPA, &CPU::NOP, &CPU::DCP, &CPU::CPY, &CPU::CPA, &CPU::DEC, &CPU::DCP, &CPU::INY, &CPU::CPA, &CPU::DEX, &CPU::AXS, &CPU::CPY, &CPU::CPA, &CPU::DEC, &CPU::DCP, //C
&CPU::BNE, &CPU::CPA, &CPU::HLT, &CPU::DCP, &CPU::NOP, &CPU::CPA, &CPU::DEC, &CPU::DCP, &CPU::CLD, &CPU::CPA, &CPU::NOP, &CPU::DCP, &CPU::NOP, &CPU::CPA, &CPU::DEC, &CPU::DCP, //D
&CPU::CPX, &CPU::SBC, &CPU::NOP, &CPU::ISB, &CPU::CPX, &CPU::SBC, &CPU::INC, &CPU::ISB, &CPU::INX, &CPU::SBC, &CPU::NOP, &CPU::SBC, &CPU::CPX, &CPU::SBC, &CPU::INC, &CPU::ISB, //E
&CPU::BEQ, &CPU::SBC, &CPU::HLT, &CPU::ISB, &CPU::NOP, &CPU::SBC, &CPU::INC, &CPU::ISB, &CPU::SED, &CPU::SBC, &CPU::NOP, &CPU::ISB, &CPU::NOP, &CPU::SBC, &CPU::INC, &CPU::ISB //F
};
typedef AddrMode M;
AddrMode addrMode[] = {
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
M::Imp, M::IndX, M::None, M::IndX, M::Zero, M::Zero, M::Zero, M::Zero, M::Imp, M::Imm, M::Acc, M::Imm, M::Abs, M::Abs, M::Abs, M::Abs, //0
M::Rel, M::IndY, M::None, M::IndYW, M::ZeroX, M::ZeroX, M::ZeroX, M::ZeroX, M::Imp, M::AbsY, M::Imp, M::AbsYW,M::AbsX, M::AbsX, M::AbsXW,M::AbsXW,//1
M::Abs, M::IndX, M::None, M::IndX, M::Zero, M::Zero, M::Zero, M::Zero, M::Imp, M::Imm, M::Acc, M::Imm, M::Abs, M::Abs, M::Abs, M::Abs, //2
M::Rel, M::IndY, M::None, M::IndYW, M::ZeroX, M::ZeroX, M::ZeroX, M::ZeroX, M::Imp, M::AbsY, M::Imp, M::AbsYW,M::AbsX, M::AbsX, M::AbsXW,M::AbsXW,//3
M::Imp, M::IndX, M::None, M::IndX, M::Zero, M::Zero, M::Zero, M::Zero, M::Imp, M::Imm, M::Acc, M::Imm, M::Abs, M::Abs, M::Abs, M::Abs, //4
M::Rel, M::IndY, M::None, M::IndYW, M::ZeroX, M::ZeroX, M::ZeroX, M::ZeroX, M::Imp, M::AbsY, M::Imp, M::AbsYW,M::AbsX, M::AbsX, M::AbsXW,M::AbsXW,//5
M::Imp, M::IndX, M::None, M::IndX, M::Zero, M::Zero, M::Zero, M::Zero, M::Imp, M::Imm, M::Acc, M::Imm, M::Ind, M::Abs, M::Abs, M::Abs, //6
M::Rel, M::IndY, M::None, M::IndYW, M::ZeroX, M::ZeroX, M::ZeroX, M::ZeroX, M::Imp, M::AbsY, M::Imp, M::AbsYW,M::AbsX, M::AbsX, M::AbsXW,M::AbsXW,//7
M::Imm, M::IndX, M::Imm, M::IndX, M::Zero, M::Zero, M::Zero, M::Zero, M::Imp, M::Imm, M::Imp, M::Imm, M::Abs, M::Abs, M::Abs, M::Abs, //8
M::Rel, M::IndYW, M::None, M::IndYW, M::ZeroX, M::ZeroX, M::ZeroY, M::ZeroY, M::Imp, M::AbsYW,M::Imp, M::AbsYW,M::AbsXW,M::AbsXW,M::AbsYW,M::AbsYW,//9
M::Imm, M::IndX, M::Imm, M::IndX, M::Zero, M::Zero, M::Zero, M::Zero, M::Imp, M::Imm, M::Imp, M::Imm, M::Abs, M::Abs, M::Abs, M::Abs, //A
M::Rel, M::IndY, M::None, M::IndY, M::ZeroX, M::ZeroX, M::ZeroY, M::ZeroY, M::Imp, M::AbsY, M::Imp, M::AbsY, M::AbsX, M::AbsX, M::AbsY, M::AbsY, //B
M::Imm, M::IndX, M::Imm, M::IndX, M::Zero, M::Zero, M::Zero, M::Zero, M::Imp, M::Imm, M::Imp, M::Imm, M::Abs, M::Abs, M::Abs, M::Abs, //C
M::Rel, M::IndY, M::None, M::IndYW, M::ZeroX, M::ZeroX, M::ZeroX, M::ZeroX, M::Imp, M::AbsY, M::Imp, M::AbsYW,M::AbsX, M::AbsX, M::AbsXW,M::AbsXW,//D
M::Imm, M::IndX, M::Imm, M::IndX, M::Zero, M::Zero, M::Zero, M::Zero, M::Imp, M::Imm, M::Imp, M::Imm, M::Abs, M::Abs, M::Abs, M::Abs, //E
M::Rel, M::IndY, M::None, M::IndYW, M::ZeroX, M::ZeroX, M::ZeroX, M::ZeroX, M::Imp, M::AbsY, M::Imp, M::AbsYW,M::AbsX, M::AbsX, M::AbsXW,M::AbsXW,//F
};
memcpy(_opTable, opTable, sizeof(opTable));
memcpy(_addrMode, addrMode, sizeof(addrMode));
_instAddrMode = AddrMode::None;
_state = {};
_cycleCount = 0;
_operand = 0;
_spriteDmaTransfer = false;
_spriteDmaOffset = 0;
_needHalt = false;
_ppuOffset = 0;
_startClockCount = 6;
_endClockCount = 6;
_masterClock = 0;
_dmcDmaRunning = false;
_cpuWrite = false;
_irqMask = 0;
_state = {};
_prevRunIrq = false;
_runIrq = false;
}
void CPU::Reset(bool softReset, NesModel model)
{
_state.NMIFlag = false;
_state.IRQFlag = 0;
_spriteDmaTransfer = false;
_spriteDmaOffset = 0;
_needHalt = false;
_dmcDmaRunning = false;
_lastCrashWarning = 0;
//Used by NSF code to disable Frame Counter & DMC interrupts
_irqMask = 0xFF;
//Use _memoryManager->Read() directly to prevent clocking the PPU/APU when setting PC at reset
_state.PC = _memoryManager->Read(CPU::ResetVector) | _memoryManager->Read(CPU::ResetVector+1) << 8;
_state.DebugPC = _state.PC;
_state.PreviousDebugPC = _state.PC;
if(softReset) {
SetFlags(PSFlags::Interrupt);
_state.SP -= 0x03;
} else {
_state.A = 0;
_state.SP = 0xFD;
_state.X = 0;
_state.Y = 0;
_state.PS = PSFlags::Interrupt;
_runIrq = false;
}
uint8_t ppuDivider;
uint8_t cpuDivider;
switch(model) {
default:
case NesModel::NTSC:
ppuDivider = 4;
cpuDivider = 12;
break;
case NesModel::PAL:
ppuDivider = 5;
cpuDivider = 16;
break;
case NesModel::Dendy:
ppuDivider = 5;
cpuDivider = 15;
break;
}
_cycleCount = -1;
_masterClock = 0;
uint8_t cpuOffset = 0;
if(_console->GetSettings()->CheckFlag(EmulationFlags::RandomizeCpuPpuAlignment)) {
std::random_device rd;
std::mt19937 mt(rd());
std::uniform_int_distribution<> distPpu(0, ppuDivider - 1);
std::uniform_int_distribution<> distCpu(0, cpuDivider - 1);
_ppuOffset = distPpu(mt);
cpuOffset += distCpu(mt);
string ppuAlignment = " PPU: " + std::to_string(_ppuOffset) + "/" + std::to_string(ppuDivider - 1);
string cpuAlignment = " CPU: " + std::to_string(cpuOffset) + "/" + std::to_string(cpuDivider - 1);
MessageManager::Log("CPU/PPU alignment -" + ppuAlignment + cpuAlignment);
} else {
_ppuOffset = 1;
cpuOffset = 0;
}
_masterClock += cpuDivider + cpuOffset;
//The CPU takes 8 cycles before it starts executing the ROM's code after a reset/power up
for(int i = 0; i < 8; i++) {
StartCpuCycle(true);
EndCpuCycle(true);
}
}
void CPU::Exec()
{
uint8_t opCode = GetOPCode();
_instAddrMode = _addrMode[opCode];
_operand = FetchOperand();
(this->*_opTable[opCode])();
if(_prevRunIrq || _prevNeedNmi) {
IRQ();
}
}
void CPU::IRQ()
{
#ifndef DUMMYCPU
uint16_t originalPc = PC();
#endif
DummyRead(); //fetch opcode (and discard it - $00 (BRK) is forced into the opcode register instead)
DummyRead(); //read next instruction byte (actually the same as above, since PC increment is suppressed. Also discarded.)
Push((uint16_t)(PC()));
if(_needNmi) {
_needNmi = false;
Push((uint8_t)(PS() | PSFlags::Reserved));
SetFlags(PSFlags::Interrupt);
SetPC(MemoryReadWord(CPU::NMIVector));
#ifndef DUMMYCPU
_console->DebugAddTrace("NMI");
_console->DebugProcessInterrupt(originalPc, _state.PC, true);
#endif
} else {
Push((uint8_t)(PS() | PSFlags::Reserved));
SetFlags(PSFlags::Interrupt);
SetPC(MemoryReadWord(CPU::IRQVector));
#ifndef DUMMYCPU
_console->DebugAddTrace("IRQ");
_console->DebugProcessInterrupt(originalPc, _state.PC, false);
#endif
}
}
void CPU::BRK() {
Push((uint16_t)(PC() + 1));
uint8_t flags = PS() | PSFlags::Break | PSFlags::Reserved;
if(_needNmi) {
_needNmi = false;
Push((uint8_t)flags);
SetFlags(PSFlags::Interrupt);
SetPC(MemoryReadWord(CPU::NMIVector));
#ifndef DUMMYCPU
_console->DebugAddTrace("NMI");
#endif
} else {
Push((uint8_t)flags);
SetFlags(PSFlags::Interrupt);
SetPC(MemoryReadWord(CPU::IRQVector));
#ifndef DUMMYCPU
_console->DebugAddTrace("IRQ");
#endif
}
//Ensure we don't start an NMI right after running a BRK instruction (first instruction in IRQ handler must run first - needed for nmi_and_brk test)
_prevNeedNmi = false;
}
void CPU::MemoryWrite(uint16_t addr, uint8_t value, MemoryOperationType operationType)
{
#ifdef DUMMYCPU
if(operationType == MemoryOperationType::Write || operationType == MemoryOperationType::DummyWrite) {
_writeAddresses[_writeCounter] = addr;
_isDummyWrite[_writeCounter] = operationType == MemoryOperationType::DummyWrite;
_writeValue[_writeCounter] = value;
_writeCounter++;
}
#else
_cpuWrite = true;
StartCpuCycle(false);
_memoryManager->Write(addr, value, operationType);
EndCpuCycle(false);
_cpuWrite = false;
#endif
}
uint8_t CPU::MemoryRead(uint16_t addr, MemoryOperationType operationType) {
#ifdef DUMMYCPU
uint8_t value = _memoryManager->DebugRead(addr);
if(operationType == MemoryOperationType::Read || operationType == MemoryOperationType::DummyRead) {
_readAddresses[_readCounter] = addr;
_readValue[_readCounter] = value;
_isDummyRead[_readCounter] = operationType == MemoryOperationType::DummyRead;
_readCounter++;
}
return value;
#else
ProcessPendingDma(addr);
StartCpuCycle(true);
uint8_t value = _memoryManager->Read(addr, operationType);
EndCpuCycle(true);
return value;
#endif
}
uint16_t CPU::FetchOperand()
{
switch(_instAddrMode) {
case AddrMode::Acc:
case AddrMode::Imp: DummyRead(); return 0;
case AddrMode::Imm:
case AddrMode::Rel: return GetImmediate();
case AddrMode::Zero: return GetZeroAddr();
case AddrMode::ZeroX: return GetZeroXAddr();
case AddrMode::ZeroY: return GetZeroYAddr();
case AddrMode::Ind: return GetIndAddr();
case AddrMode::IndX: return GetIndXAddr();
case AddrMode::IndY: return GetIndYAddr(false);
case AddrMode::IndYW: return GetIndYAddr(true);
case AddrMode::Abs: return GetAbsAddr();
case AddrMode::AbsX: return GetAbsXAddr(false);
case AddrMode::AbsXW: return GetAbsXAddr(true);
case AddrMode::AbsY: return GetAbsYAddr(false);
case AddrMode::AbsYW: return GetAbsYAddr(true);
default: break;
}
#if !defined(LIBRETRO) && !defined(DUMMYCPU)
if(_lastCrashWarning == 0 || _cycleCount - _lastCrashWarning > 5000000) {
MessageManager::DisplayMessage("Error", "GameCrash", "Invalid OP code - CPU crashed.");
_lastCrashWarning = _cycleCount;
}
if(_console->GetSettings()->CheckFlag(EmulationFlags::BreakOnCrash)) {
//When "Break on Crash" is enabled, open the debugger and break immediately if a crash occurs
_console->GetDebugger(true)->BreakImmediately(BreakSource::BreakOnCpuCrash);
}
if(_console->IsNsf()) {
//Don't stop emulation on CPU crash when playing NSFs, reset cpu instead
_console->Reset(true);
return 0;
} else {
return 0;
}
#else
return 0;
#endif
}
void CPU::EndCpuCycle(bool forRead)
{
_masterClock += forRead ? (_endClockCount + 1) : (_endClockCount - 1);
_console->GetPpu()->Run(_masterClock - _ppuOffset);
//"The internal signal goes high during φ1 of the cycle that follows the one where the edge is detected,
//and stays high until the NMI has been handled. "
_prevNeedNmi = _needNmi;
//"This edge detector polls the status of the NMI line during φ2 of each CPU cycle (i.e., during the
//second half of each cycle) and raises an internal signal if the input goes from being high during
//one cycle to being low during the next"
if(!_prevNmiFlag && _state.NMIFlag) {
_needNmi = true;
}
_prevNmiFlag = _state.NMIFlag;
//"it's really the status of the interrupt lines at the end of the second-to-last cycle that matters."
//Keep the irq lines values from the previous cycle. The before-to-last cycle's values will be used
_prevRunIrq = _runIrq;
_runIrq = ((_state.IRQFlag & _irqMask) > 0 && !CheckFlag(PSFlags::Interrupt));
}
void CPU::StartCpuCycle(bool forRead)
{
_masterClock += forRead ? (_startClockCount - 1) : (_startClockCount + 1);
_cycleCount++;
_console->GetPpu()->Run(_masterClock - _ppuOffset);
_console->ProcessCpuClock();
}
void CPU::ProcessPendingDma(uint16_t readAddress)
{
if(!_needHalt) {
return;
}
//"If this cycle is a read, hijack the read, discard the value, and prevent all other actions that occur on this cycle (PC not incremented, etc)"
StartCpuCycle(true);
_memoryManager->Read(readAddress, MemoryOperationType::DummyRead);
EndCpuCycle(true);
_needHalt = false;
uint16_t spriteDmaCounter = 0;
uint8_t spriteReadAddr = 0;
uint8_t readValue = 0;
bool skipDummyReads = (readAddress == 0x4016 || readAddress == 0x4017);
auto processCycle = [this] {
//Sprite DMA cycles count as halt/dummy cycles for the DMC DMA when both run at the same time
if(_needHalt) {
_needHalt = false;
} else if(_needDummyRead) {
_needDummyRead = false;
}
StartCpuCycle(true);
};
while(_dmcDmaRunning || _spriteDmaTransfer) {
bool getCycle = (_cycleCount & 0x01) == 0;
if(getCycle) {
if(_dmcDmaRunning && !_needHalt && !_needDummyRead) {
//DMC DMA is ready to read a byte (both halt and dummy read cycles were performed before this)
processCycle();
readValue = _memoryManager->Read(_console->GetApu()->GetDmcReadAddress(), MemoryOperationType::DmcRead);
EndCpuCycle(true);
_console->GetApu()->SetDmcReadBuffer(readValue);
_dmcDmaRunning = false;
} else if(_spriteDmaTransfer) {
//DMC DMA is not running, or not ready, run sprite DMA
processCycle();
readValue = _memoryManager->Read(_spriteDmaOffset * 0x100 + spriteReadAddr);
EndCpuCycle(true);
spriteReadAddr++;
spriteDmaCounter++;
} else {
//DMC DMA is running, but not ready (need halt/dummy read) and sprite DMA isn't runnnig, perform a dummy read
assert(_needHalt || _needDummyRead);
processCycle();
if(!skipDummyReads) {
_memoryManager->Read(readAddress, MemoryOperationType::DummyRead);
}
EndCpuCycle(true);
}
} else {
if(_spriteDmaTransfer && (spriteDmaCounter & 0x01)) {
//Sprite DMA write cycle (only do this if a sprite dma read was performed last cycle)
processCycle();
_memoryManager->Write(0x2004, readValue, MemoryOperationType::Write);
EndCpuCycle(true);
spriteDmaCounter++;
if(spriteDmaCounter == 0x200) {
_spriteDmaTransfer = false;
}
} else {
//Align to read cycle before starting sprite DMA (or align to perform DMC read)
processCycle();
if(!skipDummyReads) {
_memoryManager->Read(readAddress, MemoryOperationType::DummyRead);
}
EndCpuCycle(true);
}
}
}
}
void CPU::RunDMATransfer(uint8_t offsetValue)
{
_spriteDmaTransfer = true;
_spriteDmaOffset = offsetValue;
_needHalt = true;
}
void CPU::StartDmcTransfer()
{
_dmcDmaRunning = true;
_needDummyRead = true;
_needHalt = true;
}
uint32_t CPU::GetClockRate(NesModel model)
{
switch(model) {
default:
case NesModel::NTSC: return CPU::ClockRateNtsc; break;
case NesModel::PAL: return CPU::ClockRatePal; break;
case NesModel::Dendy: return CPU::ClockRateDendy; break;
}
}
void CPU::SetMasterClockDivider(NesModel region)
{
switch(region) {
default:
case NesModel::NTSC:
_startClockCount = 6;
_endClockCount = 6;
break;
case NesModel::PAL:
_startClockCount = 8;
_endClockCount = 8;
break;
case NesModel::Dendy:
_startClockCount = 7;
_endClockCount = 8;
break;
}
}
void CPU::StreamState(bool saving)
{
EmulationSettings* settings = _console->GetSettings();
uint32_t extraScanlinesBeforeNmi = settings->GetPpuExtraScanlinesBeforeNmi();
uint32_t extraScanlinesAfterNmi = settings->GetPpuExtraScanlinesAfterNmi();
uint32_t dipSwitches = _console->GetSettings()->GetDipSwitches();
Stream(_state.PC, _state.SP, _state.PS, _state.A, _state.X, _state.Y, _cycleCount, _state.NMIFlag,
_state.IRQFlag, _dmcDmaRunning, _spriteDmaTransfer,
extraScanlinesBeforeNmi, extraScanlinesAfterNmi, dipSwitches,
_needDummyRead, _needHalt, _startClockCount, _endClockCount, _ppuOffset, _masterClock,
_prevNeedNmi, _prevNmiFlag, _needNmi);
if(!saving) {
settings->SetPpuNmiConfig(extraScanlinesBeforeNmi, extraScanlinesAfterNmi);
settings->SetDipSwitches(dipSwitches);
}
}

View file

@ -1,885 +0,0 @@
#if (defined(DUMMYCPU) && !defined(__DUMMYCPU__H)) || (!defined(DUMMYCPU) && !defined(__CPU__H))
#ifdef DUMMYCPU
#define __DUMMYCPU__H
#else
#define __CPU__H
#endif
#include "stdafx.h"
#include "Snapshotable.h"
#include "Types.h"
enum class NesModel;
class Console;
class MemoryManager;
class DummyCpu;
class CPU : public Snapshotable
{
#ifndef DUMMYCPU
friend DummyCpu;
#endif
public:
static constexpr uint16_t NMIVector = 0xFFFA;
static constexpr uint16_t ResetVector = 0xFFFC;
static constexpr uint16_t IRQVector = 0xFFFE;
static constexpr uint32_t ClockRateNtsc = 1789773;
static constexpr uint32_t ClockRatePal = 1662607;
static constexpr uint32_t ClockRateDendy = 1773448;
private:
typedef void(CPU::*Func)();
uint64_t _cycleCount;
uint64_t _masterClock;
uint8_t _ppuOffset;
uint8_t _startClockCount;
uint8_t _endClockCount;
uint16_t _operand;
Func _opTable[256];
AddrMode _addrMode[256];
AddrMode _instAddrMode;
bool _needHalt = false;
bool _spriteDmaTransfer = false;
bool _dmcDmaRunning = false;
bool _needDummyRead = false;
uint8_t _spriteDmaOffset;
bool _cpuWrite = false;
uint8_t _irqMask;
State _state;
shared_ptr<Console> _console;
MemoryManager* _memoryManager;
bool _prevRunIrq = false;
bool _runIrq = false;
bool _prevNmiFlag = false;
bool _prevNeedNmi = false;
bool _needNmi = false;
uint64_t _lastCrashWarning = 0;
#ifdef DUMMYCPU
uint32_t _writeCounter = 0;
uint16_t _writeAddresses[10];
uint8_t _writeValue[10];
bool _isDummyWrite[10];
uint32_t _readCounter = 0;
uint16_t _readAddresses[10];
uint8_t _readValue[10];
bool _isDummyRead[10];
#endif
__forceinline void StartCpuCycle(bool forRead);
__forceinline void ProcessPendingDma(uint16_t readAddress);
__forceinline uint16_t FetchOperand();
__forceinline void EndCpuCycle(bool forRead);
void IRQ();
uint8_t GetOPCode()
{
uint8_t opCode = MemoryRead(_state.PC, MemoryOperationType::ExecOpCode);
_state.PC++;
return opCode;
}
void DummyRead()
{
MemoryRead(_state.PC, MemoryOperationType::DummyRead);
}
uint8_t ReadByte()
{
uint8_t value = MemoryRead(_state.PC, MemoryOperationType::ExecOperand);
_state.PC++;
return value;
}
uint16_t ReadWord()
{
uint16_t value = MemoryReadWord(_state.PC, MemoryOperationType::ExecOperand);
_state.PC += 2;
return value;
}
void ClearFlags(uint8_t flags)
{
_state.PS &= ~flags;
}
void SetFlags(uint8_t flags)
{
_state.PS |= flags;
}
bool CheckFlag(uint8_t flag)
{
return (_state.PS & flag) == flag;
}
void SetZeroNegativeFlags(uint8_t value)
{
if(value == 0) {
SetFlags(PSFlags::Zero);
} else if(value & 0x80) {
SetFlags(PSFlags::Negative);
}
}
bool CheckPageCrossed(uint16_t valA, int8_t valB)
{
return ((valA + valB) & 0xFF00) != (valA & 0xFF00);
}
bool CheckPageCrossed(uint16_t valA, uint8_t valB)
{
return ((valA + valB) & 0xFF00) != (valA & 0xFF00);
}
void MemoryWrite(uint16_t addr, uint8_t value, MemoryOperationType operationType = MemoryOperationType::Write);
uint8_t MemoryRead(uint16_t addr, MemoryOperationType operationType = MemoryOperationType::Read);
uint16_t MemoryReadWord(uint16_t addr, MemoryOperationType operationType = MemoryOperationType::Read) {
uint8_t lo = MemoryRead(addr, operationType);
uint8_t hi = MemoryRead(addr + 1, operationType);
return lo | hi << 8;
}
void SetRegister(uint8_t &reg, uint8_t value) {
ClearFlags(PSFlags::Zero | PSFlags::Negative);
SetZeroNegativeFlags(value);
reg = value;
}
void Push(uint8_t value) {
MemoryWrite(SP() + 0x100, value);
SetSP(SP() - 1);
}
void Push(uint16_t value) {
Push((uint8_t)(value >> 8));
Push((uint8_t)value);
}
uint8_t Pop() {
SetSP(SP() + 1);
return MemoryRead(0x100 + SP());
}
uint16_t PopWord() {
uint8_t lo = Pop();
uint8_t hi = Pop();
return lo | hi << 8;
}
uint8_t A() { return _state.A; }
void SetA(uint8_t value) { SetRegister(_state.A, value); }
uint8_t X() { return _state.X; }
void SetX(uint8_t value) { SetRegister(_state.X, value); }
uint8_t Y() { return _state.Y; }
void SetY(uint8_t value) { SetRegister(_state.Y, value); }
uint8_t SP() { return _state.SP; }
void SetSP(uint8_t value) { _state.SP = value; }
uint8_t PS() { return _state.PS; }
void SetPS(uint8_t value) { _state.PS = value & 0xCF; }
uint16_t PC() { return _state.PC; }
void SetPC(uint16_t value) { _state.PC = value; }
uint16_t GetOperand()
{
return _operand;
}
uint8_t GetOperandValue()
{
if(_instAddrMode >= AddrMode::Zero) {
return MemoryRead(GetOperand());
} else {
return (uint8_t)GetOperand();
}
}
uint16_t GetIndAddr() { return ReadWord(); }
uint8_t GetImmediate() { return ReadByte(); }
uint8_t GetZeroAddr() { return ReadByte(); }
uint8_t GetZeroXAddr() {
uint8_t value = ReadByte();
MemoryRead(value, MemoryOperationType::DummyRead); //Dummy read
return value + X();
}
uint8_t GetZeroYAddr() {
uint8_t value = ReadByte();
MemoryRead(value, MemoryOperationType::DummyRead); //Dummy read
return value + Y();
}
uint16_t GetAbsAddr() { return ReadWord(); }
uint16_t GetAbsXAddr(bool dummyRead = true) {
uint16_t baseAddr = ReadWord();
bool pageCrossed = CheckPageCrossed(baseAddr, X());
if(pageCrossed || dummyRead) {
//Dummy read done by the processor (only when page is crossed for READ instructions)
MemoryRead(baseAddr + X() - (pageCrossed ? 0x100 : 0), MemoryOperationType::DummyRead);
}
return baseAddr + X();
}
uint16_t GetAbsYAddr(bool dummyRead = true) {
uint16_t baseAddr = ReadWord();
bool pageCrossed = CheckPageCrossed(baseAddr, Y());
if(pageCrossed || dummyRead) {
//Dummy read done by the processor (only when page is crossed for READ instructions)
MemoryRead(baseAddr + Y() - (pageCrossed ? 0x100 : 0), MemoryOperationType::DummyRead);
}
return baseAddr + Y();
}
uint16_t GetInd() {
uint16_t addr = GetOperand();
if((addr & 0xFF) == 0xFF) {
auto lo = MemoryRead(addr);
auto hi = MemoryRead(addr - 0xFF);
return (lo | hi << 8);
} else {
return MemoryReadWord(addr);
}
}
uint16_t GetIndXAddr() {
uint8_t zero = ReadByte();
//Dummy read
MemoryRead(zero, MemoryOperationType::DummyRead);
zero += X();
uint16_t addr;
if(zero == 0xFF) {
addr = MemoryRead(0xFF) | MemoryRead(0x00) << 8;
} else {
addr = MemoryReadWord(zero);
}
return addr;
}
uint16_t GetIndYAddr(bool dummyRead = true) {
uint8_t zero = ReadByte();
uint16_t addr;
if(zero == 0xFF) {
addr = MemoryRead(0xFF) | MemoryRead(0x00) << 8;
} else {
addr = MemoryReadWord(zero);
}
bool pageCrossed = CheckPageCrossed(addr, Y());
if(pageCrossed || dummyRead) {
//Dummy read done by the processor (only when page is crossed for READ instructions)
MemoryRead(addr + Y() - (pageCrossed ? 0x100 : 0), MemoryOperationType::DummyRead);
}
return addr + Y();
}
void AND() { SetA(A() & GetOperandValue()); }
void EOR() { SetA(A() ^ GetOperandValue()); }
void ORA() { SetA(A() | GetOperandValue()); }
void ADD(uint8_t value)
{
uint16_t result = (uint16_t)A() + (uint16_t)value + (CheckFlag(PSFlags::Carry) ? PSFlags::Carry : 0x00);
ClearFlags(PSFlags::Carry | PSFlags::Negative | PSFlags::Overflow | PSFlags::Zero);
SetZeroNegativeFlags((uint8_t)result);
if(~(A() ^ value) & (A() ^ result) & 0x80) {
SetFlags(PSFlags::Overflow);
}
if(result > 0xFF) {
SetFlags(PSFlags::Carry);
}
SetA((uint8_t)result);
}
void ADC() { ADD(GetOperandValue()); }
void SBC() { ADD(GetOperandValue() ^ 0xFF); }
void CMP(uint8_t reg, uint8_t value)
{
ClearFlags(PSFlags::Carry | PSFlags::Negative | PSFlags::Zero);
auto result = reg - value;
if(reg >= value) {
SetFlags(PSFlags::Carry);
}
if(reg == value) {
SetFlags(PSFlags::Zero);
}
if((result & 0x80) == 0x80) {
SetFlags(PSFlags::Negative);
}
}
void CPA() { CMP(A(), GetOperandValue()); }
void CPX() { CMP(X(), GetOperandValue()); }
void CPY() { CMP(Y(), GetOperandValue()); }
void INC()
{
uint16_t addr = GetOperand();
ClearFlags(PSFlags::Negative | PSFlags::Zero);
uint8_t value = MemoryRead(addr);
MemoryWrite(addr, value, MemoryOperationType::DummyWrite); //Dummy write
value++;
SetZeroNegativeFlags(value);
MemoryWrite(addr, value);
}
void DEC()
{
uint16_t addr = GetOperand();
ClearFlags(PSFlags::Negative | PSFlags::Zero);
uint8_t value = MemoryRead(addr);
MemoryWrite(addr, value, MemoryOperationType::DummyWrite); //Dummy write
value--;
SetZeroNegativeFlags(value);
MemoryWrite(addr, value);
}
uint8_t ASL(uint8_t value)
{
ClearFlags(PSFlags::Carry | PSFlags::Negative | PSFlags::Zero);
if(value & 0x80) {
SetFlags(PSFlags::Carry);
}
uint8_t result = value << 1;
SetZeroNegativeFlags(result);
return result;
}
uint8_t LSR(uint8_t value) {
ClearFlags(PSFlags::Carry | PSFlags::Negative | PSFlags::Zero);
if(value & 0x01) {
SetFlags(PSFlags::Carry);
}
uint8_t result = value >> 1;
SetZeroNegativeFlags(result);
return result;
}
uint8_t ROL(uint8_t value) {
bool carryFlag = CheckFlag(PSFlags::Carry);
ClearFlags(PSFlags::Carry | PSFlags::Negative | PSFlags::Zero);
if(value & 0x80) {
SetFlags(PSFlags::Carry);
}
uint8_t result = (value << 1 | (carryFlag ? 0x01 : 0x00));
SetZeroNegativeFlags(result);
return result;
}
uint8_t ROR(uint8_t value) {
bool carryFlag = CheckFlag(PSFlags::Carry);
ClearFlags(PSFlags::Carry | PSFlags::Negative | PSFlags::Zero);
if(value & 0x01) {
SetFlags(PSFlags::Carry);
}
uint8_t result = (value >> 1 | (carryFlag ? 0x80 : 0x00));
SetZeroNegativeFlags(result);
return result;
}
void ASLAddr() {
uint16_t addr = GetOperand();
uint8_t value = MemoryRead(addr);
MemoryWrite(addr, value, MemoryOperationType::DummyWrite); //Dummy write
MemoryWrite(addr, ASL(value));
}
void LSRAddr() {
uint16_t addr = GetOperand();
uint8_t value = MemoryRead(addr);
MemoryWrite(addr, value, MemoryOperationType::DummyWrite); //Dummy write
MemoryWrite(addr, LSR(value));
}
void ROLAddr() {
uint16_t addr = GetOperand();
uint8_t value = MemoryRead(addr);
MemoryWrite(addr, value, MemoryOperationType::DummyWrite); //Dummy write
MemoryWrite(addr, ROL(value));
}
void RORAddr() {
uint16_t addr = GetOperand();
uint8_t value = MemoryRead(addr);
MemoryWrite(addr, value, MemoryOperationType::DummyWrite); //Dummy write
MemoryWrite(addr, ROR(value));
}
void JMP(uint16_t addr) {
SetPC(addr);
}
void BranchRelative(bool branch) {
int8_t offset = (int8_t)GetOperand();
if(branch) {
//"a taken non-page-crossing branch ignores IRQ/NMI during its last clock, so that next instruction executes before the IRQ"
//Fixes "branch_delays_irq" test
if(_runIrq && !_prevRunIrq) {
_runIrq = false;
}
DummyRead();
if(CheckPageCrossed(PC(), offset)) {
DummyRead();
}
SetPC(PC() + offset);
}
}
void BIT() {
uint8_t value = GetOperandValue();
ClearFlags(PSFlags::Zero | PSFlags::Overflow | PSFlags::Negative);
if((A() & value) == 0) {
SetFlags(PSFlags::Zero);
}
if(value & 0x40) {
SetFlags(PSFlags::Overflow);
}
if(value & 0x80) {
SetFlags(PSFlags::Negative);
}
}
//OP Codes
void LDA() { SetA(GetOperandValue()); }
void LDX() { SetX(GetOperandValue()); }
void LDY() { SetY(GetOperandValue()); }
void STA() { MemoryWrite(GetOperand(), A()); }
void STX() { MemoryWrite(GetOperand(), X()); }
void STY() { MemoryWrite(GetOperand(), Y()); }
void TAX() { SetX(A()); }
void TAY() { SetY(A()); }
void TSX() { SetX(SP()); }
void TXA() { SetA(X()); }
void TXS() { SetSP(X()); }
void TYA() { SetA(Y()); }
void PHA() { Push(A()); }
void PHP() {
uint8_t flags = PS() | PSFlags::Break | PSFlags::Reserved;
Push((uint8_t)flags);
}
void PLA() {
DummyRead();
SetA(Pop());
}
void PLP() {
DummyRead();
SetPS(Pop());
}
void INX() { SetX(X() + 1); }
void INY() { SetY(Y() + 1); }
void DEX() { SetX(X() - 1); }
void DEY() { SetY(Y() - 1); }
void ASL_Acc() { SetA(ASL(A())); }
void ASL_Memory() { ASLAddr(); }
void LSR_Acc() { SetA(LSR(A())); }
void LSR_Memory() { LSRAddr(); }
void ROL_Acc() { SetA(ROL(A())); }
void ROL_Memory() { ROLAddr(); }
void ROR_Acc() { SetA(ROR(A())); }
void ROR_Memory() { RORAddr(); }
void JMP_Abs() {
JMP(GetOperand());
}
void JMP_Ind() { JMP(GetInd()); }
void JSR() {
uint16_t addr = GetOperand();
DummyRead();
Push((uint16_t)(PC() - 1));
JMP(addr);
}
void RTS() {
uint16_t addr = PopWord();
DummyRead();
DummyRead();
SetPC(addr + 1);
}
void BCC() {
BranchRelative(!CheckFlag(PSFlags::Carry));
}
void BCS() {
BranchRelative(CheckFlag(PSFlags::Carry));
}
void BEQ() {
BranchRelative(CheckFlag(PSFlags::Zero));
}
void BMI() {
BranchRelative(CheckFlag(PSFlags::Negative));
}
void BNE() {
BranchRelative(!CheckFlag(PSFlags::Zero));
}
void BPL() {
BranchRelative(!CheckFlag(PSFlags::Negative));
}
void BVC() {
BranchRelative(!CheckFlag(PSFlags::Overflow));
}
void BVS() {
BranchRelative(CheckFlag(PSFlags::Overflow));
}
void CLC() { ClearFlags(PSFlags::Carry); }
void CLD() { ClearFlags(PSFlags::Decimal); }
void CLI() { ClearFlags(PSFlags::Interrupt); }
void CLV() { ClearFlags(PSFlags::Overflow); }
void SEC() { SetFlags(PSFlags::Carry); }
void SED() { SetFlags(PSFlags::Decimal); }
void SEI() { SetFlags(PSFlags::Interrupt); }
void BRK();
void RTI() {
DummyRead();
SetPS(Pop());
SetPC(PopWord());
}
void NOP() {
//Make sure the nop operation takes as many cycles as meant to
GetOperandValue();
}
//Unofficial OpCodes
void SLO()
{
//ASL & ORA
uint8_t value = GetOperandValue();
MemoryWrite(GetOperand(), value, MemoryOperationType::DummyWrite); //Dummy write
uint8_t shiftedValue = ASL(value);
SetA(A() | shiftedValue);
MemoryWrite(GetOperand(), shiftedValue);
}
void SRE()
{
//ROL & AND
uint8_t value = GetOperandValue();
MemoryWrite(GetOperand(), value, MemoryOperationType::DummyWrite); //Dummy write
uint8_t shiftedValue = LSR(value);
SetA(A() ^ shiftedValue);
MemoryWrite(GetOperand(), shiftedValue);
}
void RLA()
{
//LSR & EOR
uint8_t value = GetOperandValue();
MemoryWrite(GetOperand(), value, MemoryOperationType::DummyWrite); //Dummy write
uint8_t shiftedValue = ROL(value);
SetA(A() & shiftedValue);
MemoryWrite(GetOperand(), shiftedValue);
}
void RRA()
{
//ROR & ADC
uint8_t value = GetOperandValue();
MemoryWrite(GetOperand(), value, MemoryOperationType::DummyWrite); //Dummy write
uint8_t shiftedValue = ROR(value);
ADD(shiftedValue);
MemoryWrite(GetOperand(), shiftedValue);
}
void SAX()
{
//STA & STX
MemoryWrite(GetOperand(), A() & X());
}
void LAX()
{
//LDA & LDX
uint8_t value = GetOperandValue();
SetX(value);
SetA(value);
}
void DCP()
{
//DEC & CMP
uint8_t value = GetOperandValue();
MemoryWrite(GetOperand(), value, MemoryOperationType::DummyWrite); //Dummy write
value--;
CMP(A(), value);
MemoryWrite(GetOperand(), value);
}
void ISB()
{
//INC & SBC
uint8_t value = GetOperandValue();
MemoryWrite(GetOperand(), value, MemoryOperationType::DummyWrite); //Dummy write
value++;
ADD(value ^ 0xFF);
MemoryWrite(GetOperand(), value);
}
void AAC()
{
SetA(A() & GetOperandValue());
ClearFlags(PSFlags::Carry);
if(CheckFlag(PSFlags::Negative)) {
SetFlags(PSFlags::Carry);
}
}
void ASR()
{
ClearFlags(PSFlags::Carry);
SetA(A() & GetOperandValue());
if(A() & 0x01) {
SetFlags(PSFlags::Carry);
}
SetA(A() >> 1);
}
void ARR()
{
SetA(((A() & GetOperandValue()) >> 1) | (CheckFlag(PSFlags::Carry) ? 0x80 : 0x00));
ClearFlags(PSFlags::Carry | PSFlags::Overflow);
if(A() & 0x40) {
SetFlags(PSFlags::Carry);
}
if((CheckFlag(PSFlags::Carry) ? 0x01 : 0x00) ^ ((A() >> 5) & 0x01)) {
SetFlags(PSFlags::Overflow);
}
}
void ATX()
{
//LDA & TAX
uint8_t value = GetOperandValue();
SetA(value); //LDA
SetX(A()); //TAX
SetA(A()); //Update flags based on A
}
void AXS()
{
//CMP & DEX
uint8_t opValue = GetOperandValue();
uint8_t value = (A() & X()) - opValue;
ClearFlags(PSFlags::Carry);
if((A() & X()) >= opValue) {
SetFlags(PSFlags::Carry);
}
SetX(value);
}
void SYA()
{
uint8_t addrHigh = GetOperand() >> 8;
uint8_t addrLow = GetOperand() & 0xFF;
uint8_t value = Y() & (addrHigh + 1);
//From here: http://forums.nesdev.com/viewtopic.php?f=3&t=3831&start=30
//Unsure if this is accurate or not
//"the target address for e.g. SYA becomes ((y & (addr_high + 1)) << 8) | addr_low instead of the normal ((addr_high + 1) << 8) | addr_low"
MemoryWrite(((Y() & (addrHigh + 1)) << 8) | addrLow, value);
}
void SXA()
{
uint8_t addrHigh = GetOperand() >> 8;
uint8_t addrLow = GetOperand() & 0xFF;
uint8_t value = X() & (addrHigh + 1);
MemoryWrite(((X() & (addrHigh + 1)) << 8) | addrLow, value);
}
//Unimplemented/Incorrect Unofficial OP codes
void HLT()
{
//normally freezes the cpu, we can probably assume nothing will ever call this
GetOperandValue();
}
void UNK()
{
//Make sure we take the right amount of cycles (not reliable for operations that write to memory, etc.)
GetOperandValue();
}
void AXA()
{
uint16_t addr = GetOperand();
//"This opcode stores the result of A AND X AND the high byte of the target address of the operand +1 in memory."
//This may not be the actual behavior, but the read/write operations are needed for proper cycle counting
MemoryWrite(GetOperand(), ((addr >> 8) + 1) & A() & X());
}
void TAS()
{
//"AND X register with accumulator and store result in stack
//pointer, then AND stack pointer with the high byte of the
//target address of the argument + 1. Store result in memory."
uint16_t addr = GetOperand();
SetSP(X() & A());
MemoryWrite(addr, SP() & ((addr >> 8) + 1));
}
void LAS()
{
//"AND memory with stack pointer, transfer result to accumulator, X register and stack pointer."
uint8_t value = GetOperandValue();
SetA(value & SP());
SetX(A());
SetSP(A());
}
protected:
void StreamState(bool saving) override;
public:
CPU(shared_ptr<Console> console);
uint64_t GetCycleCount() { return _cycleCount; }
void SetMasterClockDivider(NesModel region);
void SetNmiFlag() { _state.NMIFlag = true; }
void ClearNmiFlag() { _state.NMIFlag = false; }
void SetIrqMask(uint8_t mask) { _irqMask = mask; }
void SetIrqSource(IRQSource source) { _state.IRQFlag |= (int)source; }
bool HasIrqSource(IRQSource source) { return (_state.IRQFlag & (int)source) != 0; }
void ClearIrqSource(IRQSource source) { _state.IRQFlag &= ~(int)source; }
void RunDMATransfer(uint8_t offsetValue);
void StartDmcTransfer();
uint32_t GetClockRate(NesModel model);
bool IsCpuWrite() { return _cpuWrite; }
//Used by debugger for "Set Next Statement"
void SetDebugPC(uint16_t value) {
SetPC(value);
_state.PreviousDebugPC = _state.DebugPC;
_state.DebugPC = value;
}
void Reset(bool softReset, NesModel model);
void Exec();
void GetState(State &state)
{
state = _state;
state.CycleCount = _cycleCount;
}
uint16_t GetDebugPC() { return _state.DebugPC; }
uint16_t GetPC() { return _state.PC; }
void SetState(State state)
{
uint16_t originalPc = state.PC;
uint16_t originalDebugPc = state.DebugPC;
_state = state;
_cycleCount = state.CycleCount;
state.PC = originalPc;
state.DebugPC = originalDebugPc;
}
#ifdef DUMMYCPU
#undef CPU
void SetDummyState(CPU *c)
{
#define CPU DummyCpu
_writeCounter = 0;
_readCounter = 0;
_state = c->_state;
_cycleCount = c->_cycleCount;
_operand = c->_operand;
_spriteDmaTransfer = c->_spriteDmaTransfer;
_needHalt = c->_needHalt;
_dmcDmaRunning = c->_dmcDmaRunning;
_cpuWrite = c->_cpuWrite;
_needDummyRead = c->_needDummyRead;
_needHalt = c->_needHalt;
_spriteDmaOffset = c->_spriteDmaOffset;
_irqMask = c->_irqMask;
_prevRunIrq = c->_prevRunIrq;
_runIrq = c->_runIrq;
_cycleCount = c->_cycleCount;
}
uint32_t GetWriteCount()
{
return _writeCounter;
}
uint32_t GetReadCount()
{
return _readCounter;
}
void GetWriteAddrValue(uint32_t index, uint16_t &addr, uint8_t &value, bool &isDummyWrite)
{
addr = _writeAddresses[index];
value = _writeValue[index];
isDummyWrite = _isDummyWrite[index];
}
void GetReadAddr(uint32_t index, uint16_t &addr, uint8_t &value, bool &isDummyRead)
{
addr = _readAddresses[index];
value = _readValue[index];
isDummyRead = _isDummyRead[index];
}
#endif
};
#endif

View file

@ -1,52 +0,0 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class Caltron41 : public BaseMapper
{
private:
uint8_t _prgBank;
uint8_t _chrBank;
protected:
virtual uint16_t GetPRGPageSize() override { return 0x8000; }
virtual uint16_t GetCHRPageSize() override { return 0x2000; }
virtual uint16_t RegisterStartAddress() override { return 0x8000; }
virtual uint16_t RegisterEndAddress() override { return 0xFFFF; }
void InitMapper() override
{
AddRegisterRange(0x6000, 0x67FF, MemoryOperation::Write);
}
void Reset(bool softReset) override
{
_chrBank = 0;
_prgBank = 0;
WriteRegister(0x6000, 0);
WriteRegister(0x8000, 0);
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
Stream(_prgBank, _chrBank);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if(addr <= 0x67FF) {
_prgBank = addr & 0x07;
_chrBank = (_chrBank & 0x03) | ((addr >> 1) & 0x0C);
SelectPRGPage(0, _prgBank);
SelectCHRPage(0, _chrBank);
SetMirroringType(addr & 0x20 ? MirroringType::Horizontal : MirroringType::Vertical);
} else {
//"Note that the Inner CHR Bank Select only can be written while the PRG ROM bank is 4, 5, 6, or 7"
if(_prgBank >= 4) {
_chrBank = (_chrBank & 0x0C) | (value & 0x03);
SelectCHRPage(0, _chrBank);
}
}
}
};

View file

@ -1,35 +0,0 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
class Cc21 : public BaseMapper
{
protected:
uint16_t GetPRGPageSize() override { return 0x8000; }
uint16_t GetCHRPageSize() override { return 0x1000; }
void InitMapper() override
{
SelectPRGPage(0, 0);
SelectCHRPage(0, 0);
SelectCHRPage(1, 0);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
uint8_t latch = (uint8_t)addr;
if(addr == 0x8000) {
latch = value;
}
if(_chrRomSize == 0x2000) {
SelectCHRPage(0, latch & 0x01);
SelectCHRPage(1, latch & 0x01);
} else {
//Overdumped roms
SelectChrPage2x(0, (latch & 0x01) << 1);
}
SetMirroringType(latch & 0x01 ? MirroringType::ScreenBOnly : MirroringType::ScreenAOnly);
}
};

View file

@ -1,91 +0,0 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
#include "FlashSST39SF040.h"
#include "../Utilities/IpsPatcher.h"
class Cheapocabra : public BaseMapper
{
private:
unique_ptr<FlashSST39SF040> _flash;
uint8_t _prgReg = 0;
vector<uint8_t> _orgPrgRom;
protected:
uint16_t GetPRGPageSize() override { return 0x8000; }
uint16_t GetCHRPageSize() override { return 0x2000; }
uint32_t GetWorkRamSize() override { return 0; }
uint32_t GetSaveRamSize() override { return 0; }
uint16_t RegisterStartAddress() override { return 0x5000; }
uint16_t RegisterEndAddress() override { return 0x5FFF; }
uint32_t GetChrRamSize() override { return 0x4000; }
bool AllowRegisterRead() override { return true; }
void InitMapper() override
{
AddRegisterRange(0x7000, 0x7FFF, MemoryOperation::Write);
_flash.reset(new FlashSST39SF040(_prgRom, _prgSize));
AddRegisterRange(0x8000, 0xFFFF, MemoryOperation::Any);
RemoveRegisterRange(0x5000, 0x5FFF, MemoryOperation::Read);
WriteRegister(0x5000, GetPowerOnByte());
_orgPrgRom = vector<uint8_t>(_prgRom, _prgRom + _prgSize);
ApplySaveData();
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
SnapshotInfo flash { _flash.get() };
Stream(_prgReg, flash);
}
void ApplySaveData()
{
//Apply save data (saved as an IPS file), if found
vector<uint8_t> ipsData = _console->GetBatteryManager()->LoadBattery(".ips");
if(!ipsData.empty()) {
vector<uint8_t> patchedPrgRom;
if(IpsPatcher::PatchBuffer(ipsData, _orgPrgRom, patchedPrgRom)) {
memcpy(_prgRom, patchedPrgRom.data(), _prgSize);
}
}
}
void SaveBattery() override
{
vector<uint8_t> prgRom = vector<uint8_t>(_prgRom, _prgRom + _prgSize);
vector<uint8_t> ipsData = IpsPatcher::CreatePatch(_orgPrgRom, prgRom);
if(ipsData.size() > 8) {
_console->GetBatteryManager()->SaveBattery(".ips", ipsData.data(), (uint32_t)ipsData.size());
}
}
uint8_t ReadRegister(uint16_t addr) override
{
int16_t value = _flash->Read(addr);
if(value >= 0) {
return (uint8_t)value;
}
return BaseMapper::InternalReadRam(addr);
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
if(addr < 0x8000) {
_prgReg = value & 0x0F;
SelectPRGPage(0, _prgReg);
SelectCHRPage(0, (value >> 4) & 0x01);
for(int i = 0; i < 8; i++) {
SetNametable(i, ((value & 0x20) ? 8 : 0) + i);
}
} else {
_flash->Write((_prgReg << 15) | (addr & 0x7FFF), value);
}
}
};

View file

@ -1,218 +0,0 @@
#include "stdafx.h"
#include "CheatManager.h"
#include "Console.h"
#include "BaseMapper.h"
#include "MessageManager.h"
#include "NotificationManager.h"
CheatManager::CheatManager(shared_ptr<Console> console)
{
_console = console;
for(int i = 0; i <= 0xFFFF; i++) {
_relativeCheatCodes.push_back(nullptr);
}
}
uint32_t CheatManager::DecodeValue(uint32_t code, uint32_t* bitIndexes, uint32_t bitCount)
{
uint32_t result = 0;
for(uint32_t i = 0; i < bitCount; i++) {
result <<= 1;
result |= (code >> bitIndexes[i]) & 0x01;
}
return result;
}
CodeInfo CheatManager::GetGGCodeInfo(string ggCode)
{
string ggLetters = "APZLGITYEOXUKSVN";
uint32_t rawCode = 0;
for(size_t i = 0, len = ggCode.size(); i < len; i++) {
rawCode |= ggLetters.find(ggCode[i]) << (i * 4);
}
CodeInfo code = { };
code.IsRelativeAddress = true;
code.CompareValue = -1;
uint32_t addressBits[15] = { 14, 13, 12, 19, 22, 21, 20, 7, 10, 9, 8, 15, 18, 17, 16 };
uint32_t valueBits[8] = { 3, 6, 5, 4, 23, 2, 1, 0 };
if(ggCode.size() == 8) {
//Bit 5 of the value is stored in a different location for 8-character codes
valueBits[4] = 31;
uint32_t compareValueBits[8] = { 27, 30, 29, 28, 23, 26, 25, 24 };
code.CompareValue = DecodeValue(rawCode, compareValueBits, 8);
}
code.Address = DecodeValue(rawCode, addressBits, 15) + 0x8000;
code.Value = DecodeValue(rawCode, valueBits, 8);
return code;
}
CodeInfo CheatManager::GetPARCodeInfo(uint32_t parCode)
{
uint32_t shiftValues[31] = {
3, 13, 14, 1, 6, 9, 5, 0, 12, 7, 2, 8, 10, 11, 4, //address
19, 21, 23, 22, 20, 17, 16, 18, //compare
29, 31, 24, 26, 25, 30, 27, 28 //value
};
uint32_t key = 0x7E5EE93A;
uint32_t xorValue = 0x5C184B91;
//Throw away bit 0, not used.
parCode >>= 1;
uint32_t result = 0;
for(int32_t i = 30; i >= 0; i--) {
if(((key ^ parCode) >> 30) & 0x01) {
result |= 0x01 << shiftValues[i];
key ^= xorValue;
}
parCode <<= 1;
key <<= 1;
}
CodeInfo code = { };
code.IsRelativeAddress = true;
code.Address = (result & 0x7fff) + 0x8000;
code.Value = (result >> 24) & 0xFF;
code.CompareValue = (result >> 16) & 0xFF;
return code;
}
void CheatManager::AddCode(CodeInfo &code)
{
if(code.IsRelativeAddress) {
if(code.Address > 0xFFFF) {
//Invalid cheat, ignore it
return;
}
if(_relativeCheatCodes[code.Address] == nullptr) {
_relativeCheatCodes[code.Address].reset(new vector<CodeInfo>());
}
_relativeCheatCodes[code.Address]->push_back(code);
} else {
_absoluteCheatCodes.push_back(code);
}
_hasCode = true;
_console->GetNotificationManager()->SendNotification(ConsoleNotificationType::CheatAdded);
}
void CheatManager::AddGameGenieCode(string code)
{
CodeInfo info = GetGGCodeInfo(code);
AddCode(info);
}
void CheatManager::AddProActionRockyCode(uint32_t code)
{
CodeInfo info = GetPARCodeInfo(code);
AddCode(info);
}
void CheatManager::AddCustomCode(uint32_t address, uint8_t value, int32_t compareValue, bool isRelativeAddress)
{
CodeInfo code;
code.Address = address;
code.Value = value;
code.CompareValue = compareValue;
code.IsRelativeAddress = isRelativeAddress;
AddCode(code);
}
void CheatManager::ClearCodes()
{
bool cheatRemoved = false;
for(int i = 0; i <= 0xFFFF; i++) {
if(!_relativeCheatCodes[i]) {
cheatRemoved = true;
}
_relativeCheatCodes[i].reset();
}
cheatRemoved |= _absoluteCheatCodes.size() > 0;
_absoluteCheatCodes.clear();
_hasCode = false;
if(cheatRemoved) {
_console->GetNotificationManager()->SendNotification(ConsoleNotificationType::CheatRemoved);
}
}
void CheatManager::ApplyCodes(uint16_t addr, uint8_t &value)
{
if(!_hasCode) {
return;
}
if(_relativeCheatCodes[addr] != nullptr) {
for(uint32_t i = 0, len = i < _relativeCheatCodes[addr]->size(); i < len; i++) {
CodeInfo code = _relativeCheatCodes[addr]->at(i);
if(code.CompareValue == -1 || code.CompareValue == value) {
value = code.Value;
return;
}
}
} else if(!_absoluteCheatCodes.empty()) {
int32_t absAddr = _console->GetMapper()->ToAbsoluteAddress(addr);
if(absAddr >= 0) {
for(CodeInfo &code : _absoluteCheatCodes) {
if(code.Address == (uint32_t)absAddr && (code.CompareValue == -1 || code.CompareValue == value)) {
value = code.Value;
return;
}
}
}
}
}
vector<CodeInfo> CheatManager::GetCheats()
{
//Used by NetPlay
vector<CodeInfo> cheats;
for(unique_ptr<vector<CodeInfo>> &codes : _relativeCheatCodes) {
if(codes) {
std::copy(codes.get()->begin(), codes.get()->end(), std::back_inserter(cheats));
}
}
std::copy(_absoluteCheatCodes.begin(), _absoluteCheatCodes.end(), std::back_inserter(cheats));
return cheats;
}
void CheatManager::SetCheats(CheatInfo cheats[], uint32_t length)
{
_console->Pause();
ClearCodes();
for(uint32_t i = 0; i < length; i++) {
CheatInfo &cheat = cheats[i];
switch(cheat.Type) {
case CheatType::Custom: AddCustomCode(cheat.Address, cheat.Value, cheat.UseCompareValue ? cheat.CompareValue : -1, cheat.IsRelativeAddress); break;
case CheatType::GameGenie: AddGameGenieCode(cheat.GameGenieCode); break;
case CheatType::ProActionRocky: AddProActionRockyCode(cheat.ProActionRockyCode); break;
}
}
_console->Resume();
}
void CheatManager::SetCheats(vector<CodeInfo> &cheats)
{
//Used by NetPlay
ClearCodes();
if(cheats.size() > 0) {
MessageManager::DisplayMessage("Cheats", cheats.size() > 1 ? "CheatsApplied" : "CheatApplied", std::to_string(cheats.size()));
for(CodeInfo &cheat : cheats) {
AddCode(cheat);
}
}
}

View file

@ -1,61 +0,0 @@
#pragma once
#include "stdafx.h"
class Console;
struct CodeInfo
{
uint32_t Address;
uint8_t Value;
int32_t CompareValue;
bool IsRelativeAddress;
};
enum class CheatType
{
GameGenie = 0,
ProActionRocky = 1,
Custom = 2
};
struct CheatInfo
{
CheatType Type;
uint32_t ProActionRockyCode;
uint32_t Address;
char GameGenieCode[9];
uint8_t Value;
uint8_t CompareValue;
bool UseCompareValue;
bool IsRelativeAddress;
};
class CheatManager
{
private:
shared_ptr<Console> _console;
bool _hasCode = false;
vector<unique_ptr<vector<CodeInfo>>> _relativeCheatCodes;
vector<CodeInfo> _absoluteCheatCodes;
uint32_t DecodeValue(uint32_t code, uint32_t* bitIndexes, uint32_t bitCount);
CodeInfo GetGGCodeInfo(string ggCode);
CodeInfo GetPARCodeInfo(uint32_t parCode);
void AddCode(CodeInfo &code);
public:
CheatManager(shared_ptr<Console> console);
void AddGameGenieCode(string code);
void AddProActionRockyCode(uint32_t code);
void AddCustomCode(uint32_t address, uint8_t value, int32_t compareValue = -1, bool isRelativeAddress = true);
void ClearCodes();
vector<CodeInfo> GetCheats();
void SetCheats(vector<CodeInfo> &cheats);
void SetCheats(CheatInfo cheats[], uint32_t length);
void ApplyCodes(uint16_t addr, uint8_t &value);
};

View file

@ -1,115 +0,0 @@
#pragma once
#include "stdafx.h"
#include "BaseMapper.h"
#include "MemoryManager.h"
#include "CPU.h"
class CityFighter : public BaseMapper
{
private:
uint8_t _prgReg;
uint8_t _prgMode;
uint8_t _mirroring;
uint8_t _chrRegs[8];
bool _irqEnabled;
uint16_t _irqCounter;
protected:
uint16_t GetPRGPageSize() override { return 0x2000; }
uint16_t GetCHRPageSize() override { return 0x400; }
void InitMapper() override
{
_prgReg = 0;
_prgMode = 0;
_mirroring = 0;
_irqCounter = 0;
_irqEnabled = false;
memset(_chrRegs, 0, sizeof(_chrRegs));
UpdateState();
}
void StreamState(bool saving) override
{
BaseMapper::StreamState(saving);
ArrayInfo<uint8_t> chrRegs { _chrRegs, 8 };
Stream(_prgReg, _prgMode, _mirroring, _irqEnabled, _irqCounter, chrRegs);
}
void UpdateState()
{
SelectPrgPage4x(0x8000, _prgReg);
if(!_prgMode) {
SelectPRGPage(2, _prgReg);
}
for(int i = 0; i < 8; i++) {
SelectCHRPage(i, _chrRegs[i]);
}
switch(_mirroring) {
case 0: SetMirroringType(MirroringType::Vertical); break;
case 1: SetMirroringType(MirroringType::Horizontal); break;
case 2: SetMirroringType(MirroringType::ScreenAOnly); break;
case 3: SetMirroringType(MirroringType::ScreenBOnly); break;
}
}
void ProcessCpuClock() override
{
if(_irqEnabled) {
_irqCounter--;
if(_irqCounter == 0) {
_console->GetCpu()->SetIrqSource(IRQSource::External);
}
}
}
void WriteRegister(uint16_t addr, uint8_t value) override
{
switch(addr & 0xF00C) {
case 0x9000:
_prgReg = value & 0x0C;
_mirroring = value & 0x03;
break;
case 0x9004: case 0x9008: case 0x900C:
if(addr & 0x800) {
_console->GetMemoryManager()->Write(0x4011, (value & 0x0F) << 3, MemoryOperationType::Write);
} else {
_prgReg = value & 0x0C;
}
break;
case 0xC000: case 0xC004: case 0xC008: case 0xC00C:
_prgMode = value & 0x01;
break;
case 0xD000: _chrRegs[0] = (_chrRegs[0] & 0xF0) | (value & 0x0F); break;
case 0xD004: _chrRegs[0] = (_chrRegs[0] & 0x0F) | (value << 4); break;
case 0xD008: _chrRegs[1] = (_chrRegs[1] & 0xF0) | (value & 0x0F); break;
case 0xD00C: _chrRegs[1] = (_chrRegs[1] & 0x0F) | (value << 4); break;
case 0xA000: _chrRegs[2] = (_chrRegs[2] & 0xF0) | (value & 0x0F); break;
case 0xA004: _chrRegs[2] = (_chrRegs[2] & 0x0F) | (value << 4); break;
case 0xA008: _chrRegs[3] = (_chrRegs[3] & 0xF0) | (value & 0x0F); break;
case 0xA00C: _chrRegs[3] = (_chrRegs[3] & 0x0F) | (value << 4); break;
case 0xB000: _chrRegs[4] = (_chrRegs[4] & 0xF0) | (value & 0x0F); break;
case 0xB004: _chrRegs[4] = (_chrRegs[4] & 0x0F) | (value << 4); break;
case 0xB008: _chrRegs[5] = (_chrRegs[5] & 0xF0) | (value & 0x0F); break;
case 0xB00C: _chrRegs[5] = (_chrRegs[5] & 0x0F) | (value << 4); break;
case 0xE000: _chrRegs[6] = (_chrRegs[6] & 0xF0) | (value & 0x0F); break;
case 0xE004: _chrRegs[6] = (_chrRegs[6] & 0x0F) | (value << 4); break;
case 0xE008: _chrRegs[7] = (_chrRegs[7] & 0xF0) | (value & 0x0F); break;
case 0xE00C: _chrRegs[7] = (_chrRegs[7] & 0x0F) | (value << 4); break;
case 0xF000: _irqCounter = ((_irqCounter & 0x1E0) | ((value & 0x0F) << 1)); break;
case 0xF004: _irqCounter = ((_irqCounter & 0x1E) | ((value & 0x0F) << 5)); break;
case 0xF008:
_irqEnabled = (value & 0x02) != 0;
_console->GetCpu()->ClearIrqSource(IRQSource::External);
break;
}
UpdateState();
}
};

View file

@ -1,24 +0,0 @@
#pragma once
#include "stdafx.h"
class ClientConnectionData
{
public:
string Host;
uint16_t Port;
string Password;
string PlayerName;
bool Spectator;
ClientConnectionData() {}
ClientConnectionData(string host, uint16_t port, string password, string playerName, bool spectator) :
Host(host), Port(port), Password(password), PlayerName(playerName), Spectator(spectator)
{
}
~ClientConnectionData()
{
}
};

View file

@ -1,235 +0,0 @@
#include "stdafx.h"
#include "CodeDataLogger.h"
#include "Debugger.h"
#include "LabelManager.h"
CodeDataLogger::CodeDataLogger(Debugger *debugger, uint32_t prgSize, uint32_t chrSize)
{
_debugger = debugger;
_prgSize = prgSize;
_chrSize = chrSize;
_cdlData = new uint8_t[prgSize+chrSize];
Reset();
}
CodeDataLogger::~CodeDataLogger()
{
delete[] _cdlData;
}
void CodeDataLogger::Reset()
{
_codeSize = 0;
_dataSize = 0;
_usedChrSize = 0;
_drawnChrSize = 0;
_readChrSize = 0;
memset(_cdlData, 0, _prgSize + _chrSize);
}
bool CodeDataLogger::LoadCdlFile(string cdlFilepath)
{
ifstream cdlFile(cdlFilepath, ios::in | ios::binary);
if(cdlFile) {
cdlFile.seekg(0, std::ios::end);
size_t fileSize = (size_t)cdlFile.tellg();
cdlFile.seekg(0, std::ios::beg);
if(fileSize == _prgSize + _chrSize) {
Reset();
cdlFile.read((char*)_cdlData, _prgSize + _chrSize);
cdlFile.close();
CalculateStats();
return true;
}
}
return false;
}
void CodeDataLogger::CalculateStats()
{
_codeSize = 0;
_dataSize = 0;
_usedChrSize = 0;
_drawnChrSize = 0;
_readChrSize = 0;
for(int i = 0, len = _prgSize; i < len; i++) {
if(IsCode(i)) {
_codeSize++;
} else if(IsData(i)) {
_dataSize++;
}
}
for(int i = 0, len = _chrSize; i < len; i++) {
if(IsDrawn(i) || IsRead(i)) {
_usedChrSize++;
if(IsDrawn(i)) {
_drawnChrSize++;
} else if(IsRead(i)) {
_readChrSize++;
}
}
}
}
bool CodeDataLogger::SaveCdlFile(string cdlFilepath)
{
ofstream cdlFile(cdlFilepath, ios::out | ios::binary);
if(cdlFile) {
cdlFile.write((char*)_cdlData, _prgSize+_chrSize);
cdlFile.close();
return true;
}
return false;
}
void CodeDataLogger::SetFlag(int32_t absoluteAddr, CdlPrgFlags flag)
{
if(absoluteAddr >= 0 && absoluteAddr < (int32_t)_prgSize) {
if((_cdlData[absoluteAddr] & (uint8_t)flag) != (uint8_t)flag) {
if(flag == CdlPrgFlags::Code) {
if(IsData(absoluteAddr)) {
//Remove the data flag from bytes that we are flagging as code
_cdlData[absoluteAddr] &= ~(uint8_t)CdlPrgFlags::Data;
_dataSize--;
}
_cdlData[absoluteAddr] |= (uint8_t)flag;
_codeSize++;
} else if(flag == CdlPrgFlags::Data) {
if(!IsCode(absoluteAddr)) {
_cdlData[absoluteAddr] |= (uint8_t)flag;
_dataSize++;
}
} else {
_cdlData[absoluteAddr] |= (uint8_t)flag;
}
}
}
}
void CodeDataLogger::SetFlag(int32_t chrAbsoluteAddr, CdlChrFlags flag)
{
if(chrAbsoluteAddr >= 0 && chrAbsoluteAddr < (int32_t)_chrSize) {
if((_cdlData[_prgSize + chrAbsoluteAddr] & (uint8_t)flag) != (uint8_t)flag) {
_usedChrSize++;
if(flag == CdlChrFlags::Read) {
_readChrSize++;
} else if(flag == CdlChrFlags::Drawn) {
_drawnChrSize++;
}
_cdlData[_prgSize + chrAbsoluteAddr] |= (uint8_t)flag;
}
}
}
CdlRatios CodeDataLogger::GetRatios()
{
CdlRatios ratios;
ratios.CodeRatio = (float)_codeSize / (float)_prgSize;
ratios.DataRatio = (float)_dataSize / (float)_prgSize;
ratios.PrgRatio = (float)(_codeSize + _dataSize) / (float)_prgSize;
if(_chrSize > 0) {
ratios.ChrRatio = (float)(_usedChrSize) / (float)_chrSize;
ratios.ChrReadRatio = (float)(_readChrSize) / (float)_chrSize;
ratios.ChrDrawnRatio = (float)(_drawnChrSize) / (float)_chrSize;
} else {
ratios.ChrRatio = -1;
ratios.ChrReadRatio = -1;
ratios.ChrDrawnRatio = -1;
}
return ratios;
}
bool CodeDataLogger::IsNone(uint32_t absoluteAddr)
{
return _cdlData[absoluteAddr] == (uint8_t)CdlPrgFlags::None;
}
bool CodeDataLogger::IsCode(uint32_t absoluteAddr)
{
return (_cdlData[absoluteAddr] & (uint8_t)CdlPrgFlags::Code) == (uint8_t)CdlPrgFlags::Code;
}
bool CodeDataLogger::IsJumpTarget(uint32_t absoluteAddr)
{
return (_cdlData[absoluteAddr] & (uint8_t)CdlPrgFlags::JumpTarget) == (uint8_t)CdlPrgFlags::JumpTarget;
}
bool CodeDataLogger::IsSubEntryPoint(uint32_t absoluteAddr)
{
return (_cdlData[absoluteAddr] & (uint8_t)CdlPrgFlags::SubEntryPoint) == (uint8_t)CdlPrgFlags::SubEntryPoint;
}
bool CodeDataLogger::IsData(uint32_t absoluteAddr)
{
return (_cdlData[absoluteAddr] & (uint8_t)CdlPrgFlags::Data) == (uint8_t)CdlPrgFlags::Data;
}
bool CodeDataLogger::IsRead(uint32_t absoluteAddr)
{
return (_cdlData[absoluteAddr + _prgSize] & (uint8_t)CdlChrFlags::Read) == (uint8_t)CdlChrFlags::Read;
}
bool CodeDataLogger::IsDrawn(uint32_t absoluteAddr)
{
return (_cdlData[absoluteAddr + _prgSize] & (uint8_t)CdlChrFlags::Drawn) == (uint8_t)CdlChrFlags::Drawn;
}
void CodeDataLogger::SetCdlData(uint8_t *cdlData, uint32_t length)
{
if(length <= _prgSize + _chrSize) {
memcpy(_cdlData, cdlData, length);
CalculateStats();
}
}
void CodeDataLogger::GetCdlData(uint32_t offset, uint32_t length, DebugMemoryType memoryType, uint8_t *cdlData)
{
if(memoryType == DebugMemoryType::PrgRom) {
memcpy(cdlData, _cdlData + offset, length);
} else if(memoryType == DebugMemoryType::ChrRom) {
memcpy(cdlData, _cdlData + _prgSize + offset, length);
} else if(memoryType == DebugMemoryType::CpuMemory) {
for(uint32_t i = 0; i < length; i++) {
int32_t absoluteAddress = _debugger->GetAbsoluteAddress(offset + i);
cdlData[i] = absoluteAddress >= 0 ? _cdlData[absoluteAddress] : 0;
}
} else if(memoryType == DebugMemoryType::PpuMemory) {
for(uint32_t i = 0; i < length; i++) {
int32_t absoluteAddress = _debugger->GetAbsoluteChrAddress(offset + i);
cdlData[i] = absoluteAddress >= 0 ? _cdlData[_prgSize + absoluteAddress] : 0;
}
}
}
void CodeDataLogger::StripData(uint8_t *romBuffer, CdlStripFlag flag)
{
if(flag == CdlStripFlag::StripUnused) {
for(uint32_t i = 0; i < _prgSize + _chrSize; i++) {
if(_cdlData[i] == 0) {
romBuffer[i] = 0;
}
}
} else if(flag == CdlStripFlag::StripUsed) {
for(uint32_t i = 0; i < _prgSize + _chrSize; i++) {
if(_cdlData[i] != 0) {
romBuffer[i] = 0;
}
}
}
}
void CodeDataLogger::MarkPrgBytesAs(uint32_t start, uint32_t end, CdlPrgFlags type)
{
for(uint32_t i = start; i <= end; i++) {
_cdlData[i] = (_cdlData[i] & 0xFC) | (int)type;
}
_debugger->UpdateCdlCache();
}

View file

@ -1,97 +0,0 @@
#pragma once
#include "stdafx.h"
#include "../Utilities/SimpleLock.h"
#include "DebuggerTypes.h"
class Debugger;
enum class CdlPrgFlags
{
None = 0x00,
Code = 0x01,
Data = 0x02,
//Bit 0x10 is used for "indirectly accessed as code" in FCEUX
//Repurposed to mean the address is the target of a jump instruction
JumpTarget = 0x10,
IndirectData = 0x20,
PcmData = 0x40,
//Unused bit in original CDL spec
//Used to denote that the byte is the start of function (sub)
SubEntryPoint = 0x80
};
enum class CdlChrFlags
{
Drawn = 0x01,
Read = 0x02,
};
enum class CdlStripFlag
{
StripNone = 0,
StripUnused,
StripUsed,
};
struct CdlRatios
{
float CodeRatio;
float DataRatio;
float PrgRatio;
float ChrRatio;
float ChrReadRatio;
float ChrDrawnRatio;
};
class CodeDataLogger
{
private:
Debugger* _debugger = nullptr;
uint8_t *_cdlData = nullptr;
uint32_t _prgSize = 0;
uint32_t _chrSize = 0;
uint32_t _codeSize = 0;
uint32_t _dataSize = 0;
uint32_t _usedChrSize = 0;
uint32_t _readChrSize = 0;
uint32_t _drawnChrSize = 0;
SimpleLock _lock;
void CalculateStats();
public:
CodeDataLogger(Debugger *debugger, uint32_t prgSize, uint32_t chrSize);
~CodeDataLogger();
void Reset();
bool LoadCdlFile(string cdlFilepath);
bool SaveCdlFile(string cdlFilepath);
void SetFlag(int32_t absoluteAddr, CdlPrgFlags flag);
void SetFlag(int32_t chrAbsoluteAddr, CdlChrFlags flag);
CdlRatios GetRatios();
bool IsNone(uint32_t absoluteAddr);
bool IsCode(uint32_t absoluteAddr);
bool IsJumpTarget(uint32_t absoluteAddr);
bool IsSubEntryPoint(uint32_t absoluteAddr);
bool IsData(uint32_t absoluteAddr);
bool IsRead(uint32_t absoluteAddr);
bool IsDrawn(uint32_t absoluteAddr);
void SetCdlData(uint8_t *cdlData, uint32_t length);
void GetCdlData(uint32_t offset, uint32_t length, DebugMemoryType memoryType, uint8_t* cdlData);
void StripData(uint8_t* romBuffer, CdlStripFlag flag);
void MarkPrgBytesAs(uint32_t start, uint32_t end, CdlPrgFlags type);
};

View file

@ -1,48 +0,0 @@
#include "stdafx.h"
#include "CodeRunner.h"
#include "Debugger.h"
#include "DisassemblyInfo.h"
CodeRunner::CodeRunner(vector<uint8_t> byteCode, Debugger *debugger)
{
_byteCode = byteCode;
_debugger = debugger;
_running = true;
if(_byteCode.size() < 0x1000) {
//Fill the entire $3000-$3FFF range
_byteCode.insert(_byteCode.end(), 0x1000 - _byteCode.size(), 0xEA); //0xEA = NOP
}
}
bool CodeRunner::IsRunning()
{
return _running;
}
void CodeRunner::GetMemoryRanges(MemoryRanges & ranges)
{
ranges.SetAllowOverride();
ranges.AddHandler(MemoryOperation::Any, CodeRunner::BaseAddress, CodeRunner::BaseAddress + 0xFFF);
}
uint8_t CodeRunner::ReadRAM(uint16_t addr)
{
return _byteCode[addr - CodeRunner::BaseAddress];
}
void CodeRunner::WriteRAM(uint16_t addr, uint8_t value)
{
_byteCode[addr - CodeRunner::BaseAddress] = value;
if(addr == CodeRunner::BaseAddress) {
//Writing to $3000 stops the code runner and resumes normal execution
_debugger->StopCodeRunner();
_running = false;
}
}
DisassemblyInfo CodeRunner::GetDisassemblyInfo(uint16_t cpuAddress)
{
return DisassemblyInfo(_byteCode.data() + cpuAddress - CodeRunner::BaseAddress, false);
}

View file

@ -1,26 +0,0 @@
#pragma once
#include "stdafx.h"
#include "IMemoryHandler.h"
class Debugger;
class DisassemblyInfo;
class CodeRunner : public IMemoryHandler
{
private:
vector<uint8_t> _byteCode;
Debugger *_debugger;
bool _running;
public:
static constexpr uint16_t BaseAddress = 0x3000;
CodeRunner(vector<uint8_t> byteCode, Debugger *debugger);
bool IsRunning();
DisassemblyInfo GetDisassemblyInfo(uint16_t cpuAddress);
void GetMemoryRanges(MemoryRanges &ranges) override;
uint8_t ReadRAM(uint16_t addr) override;
void WriteRAM(uint16_t addr, uint8_t value) override;
};

Some files were not shown because too many files have changed in this diff Show more