libgambatte in tree, configured to build to dll with msvs. compiles with no errors. nothing else done yet.

This commit is contained in:
goyuken 2012-09-08 19:02:33 +00:00
parent e32f2ed67b
commit 3233d31e6c
72 changed files with 15730 additions and 0 deletions

View File

@ -9,6 +9,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BizHawk.MultiClient", "BizH
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DiscoHawk", "DiscoHawk\DiscoHawk.csproj", "{C4366030-6D03-424B-AE53-F4F43BB217C3}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libgambatte", "libgambatte\libgambatte.vcxproj", "{B391EB5D-8C1E-4512-8F8E-2037C41F2BE5}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -67,6 +69,16 @@ Global
{C4366030-6D03-424B-AE53-F4F43BB217C3}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{C4366030-6D03-424B-AE53-F4F43BB217C3}.Release|Win32.ActiveCfg = Release|Any CPU
{C4366030-6D03-424B-AE53-F4F43BB217C3}.Release|Win32.Build.0 = Release|Any CPU
{B391EB5D-8C1E-4512-8F8E-2037C41F2BE5}.Debug|Any CPU.ActiveCfg = Debug|Win32
{B391EB5D-8C1E-4512-8F8E-2037C41F2BE5}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
{B391EB5D-8C1E-4512-8F8E-2037C41F2BE5}.Debug|Mixed Platforms.Build.0 = Debug|Win32
{B391EB5D-8C1E-4512-8F8E-2037C41F2BE5}.Debug|Win32.ActiveCfg = Debug|Win32
{B391EB5D-8C1E-4512-8F8E-2037C41F2BE5}.Debug|Win32.Build.0 = Debug|Win32
{B391EB5D-8C1E-4512-8F8E-2037C41F2BE5}.Release|Any CPU.ActiveCfg = Release|Win32
{B391EB5D-8C1E-4512-8F8E-2037C41F2BE5}.Release|Mixed Platforms.ActiveCfg = Release|Win32
{B391EB5D-8C1E-4512-8F8E-2037C41F2BE5}.Release|Mixed Platforms.Build.0 = Release|Win32
{B391EB5D-8C1E-4512-8F8E-2037C41F2BE5}.Release|Win32.ActiveCfg = Release|Win32
{B391EB5D-8C1E-4512-8F8E-2037C41F2BE5}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -0,0 +1,154 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef GAMBATTE_H
#define GAMBATTE_H
#include "inputgetter.h"
#include "gbint.h"
#include <string>
namespace gambatte {
enum { BG_PALETTE = 0, SP1_PALETTE = 1, SP2_PALETTE = 2 };
class GB {
public:
GB();
~GB();
enum LoadFlag {
FORCE_DMG = 1, /**< Treat the ROM as not having CGB support regardless of what its header advertises. */
GBA_CGB = 2, /**< Use GBA intial CPU register values when in CGB mode. */
MULTICART_COMPAT = 4 /**< Use heuristics to detect and support some multicart MBCs disguised as MBC1. */
};
/** Load ROM image.
*
* @param romfile Path to rom image file. Typically a .gbc, .gb, or .zip-file (if zip-support is compiled in).
* @param flags ORed combination of LoadFlags.
* @return 0 on success, negative value on failure.
*/
int load(const std::string &romfile, unsigned flags = 0);
/** Emulates until at least 'samples' stereo sound samples are produced in the supplied buffer,
* or until a video frame has been drawn.
*
* There are 35112 stereo sound samples in a video frame.
* May run for up to 2064 stereo samples too long.
* A stereo sample consists of two native endian 2s complement 16-bit PCM samples,
* with the left sample preceding the right one. Usually casting soundBuf to/from
* short* is OK and recommended. The reason for not using a short* in the interface
* is to avoid implementation-defined behaviour without compromising performance.
*
* Returns early when a new video frame has finished drawing in the video buffer,
* such that the caller may update the video output before the frame is overwritten.
* The return value indicates whether a new video frame has been drawn, and the
* exact time (in number of samples) at which it was drawn.
*
* @param videoBuf 160x144 RGB32 (native endian) video frame buffer or 0
* @param pitch distance in number of pixels (not bytes) from the start of one line to the next in videoBuf.
* @param soundBuf buffer with space >= samples + 2064
* @param samples in: number of stereo samples to produce, out: actual number of samples produced
* @return sample number at which the video frame was produced. -1 means no frame was produced.
*/
long runFor(gambatte::uint_least32_t *videoBuf, int pitch,
gambatte::uint_least32_t *soundBuf, unsigned &samples);
/** Reset to initial state.
* Equivalent to reloading a ROM image, or turning a Game Boy Color off and on again.
*/
void reset();
/** @param palNum 0 <= palNum < 3. One of BG_PALETTE, SP1_PALETTE and SP2_PALETTE.
* @param colorNum 0 <= colorNum < 4
*/
void setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned rgb32);
/** Sets the callback used for getting input state. */
void setInputGetter(InputGetter *getInput);
/** Sets the directory used for storing save data. The default is the same directory as the ROM Image file. */
void setSaveDir(const std::string &sdir);
/** Returns true if the currently loaded ROM image is treated as having CGB support. */
bool isCgb() const;
/** Returns true if a ROM image is loaded. */
bool isLoaded() const;
/** Writes persistent cartridge data to disk. Done implicitly on ROM close. */
void saveSavedata();
/** Saves emulator state to the state slot selected with selectState().
* The data will be stored in the directory given by setSaveDir().
*
* @param videoBuf 160x144 RGB32 (native endian) video frame buffer or 0. Used for saving a thumbnail.
* @param pitch distance in number of pixels (not bytes) from the start of one line to the next in videoBuf.
* @return success
*/
bool saveState(const gambatte::uint_least32_t *videoBuf, int pitch);
/** Loads emulator state from the state slot selected with selectState().
* @return success
*/
bool loadState();
/** Saves emulator state to the file given by 'filepath'.
*
* @param videoBuf 160x144 RGB32 (native endian) video frame buffer or 0. Used for saving a thumbnail.
* @param pitch distance in number of pixels (not bytes) from the start of one line to the next in videoBuf.
* @return success
*/
bool saveState(const gambatte::uint_least32_t *videoBuf, int pitch, const std::string &filepath);
/** Loads emulator state from the file given by 'filepath'.
* @return success
*/
bool loadState(const std::string &filepath);
/** Selects which state slot to save state to or load state from.
* There are 10 such slots, numbered from 0 to 9 (periodically extended for all n).
*/
void selectState(int n);
/** Current state slot selected with selectState(). Returns a value between 0 and 9 inclusive. */
int currentState() const;
/** ROM header title of currently loaded ROM image. */
const std::string romTitle() const;
/** Set Game Genie codes to apply to currently loaded ROM image. Cleared on ROM load.
* @param codes Game Genie codes in format HHH-HHH-HHH;HHH-HHH-HHH;... where H is [0-9]|[A-F]
*/
void setGameGenie(const std::string &codes);
/** Set Game Shark codes to apply to currently loaded ROM image. Cleared on ROM load.
* @param codes Game Shark codes in format 01HHHHHH;01HHHHHH;... where H is [0-9]|[A-F]
*/
void setGameShark(const std::string &codes);
private:
struct Priv;
Priv *const p_;
GB(const GB &);
GB & operator=(const GB &);
};
}
#endif

View File

@ -0,0 +1,62 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef GAMBATTE_INT_H
#define GAMBATTE_INT_H
#ifdef HAVE_CSTDINT
#include <cstdint>
namespace gambatte {
using std::uint_least32_t;
using std::uint_least16_t;
}
#elif defined(HAVE_STDINT_H)
#include <stdint.h>
namespace gambatte {
using ::uint_least32_t;
using ::uint_least16_t;
}
#else
namespace gambatte {
#ifdef CHAR_LEAST_32
typedef unsigned char uint_least32_t;
#elif defined(SHORT_LEAST_32)
typedef unsigned short uint_least32_t;
#elif defined(INT_LEAST_32)
typedef unsigned uint_least32_t;
#else
typedef unsigned long uint_least32_t;
#endif
#ifdef CHAR_LEAST_16
typedef unsigned char uint_least16_t;
#else
typedef unsigned short uint_least16_t;
#endif
}
#endif
#endif

View File

@ -0,0 +1,33 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef GAMBATTE_INPUTGETTER_H
#define GAMBATTE_INPUTGETTER_H
namespace gambatte {
class InputGetter {
public:
enum { A = 0x01, B = 0x02, SELECT = 0x04, START = 0x08, RIGHT = 0x10, LEFT = 0x20, UP = 0x40, DOWN = 0x80 };
virtual ~InputGetter() {};
/** @return A|B|SELECT|START|RIGHT|LEFT|UP|DOWN if those buttons are pressed. */
virtual unsigned operator()() = 0;
};
}
#endif

View File

@ -0,0 +1,154 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{B391EB5D-8C1E-4512-8F8E-2037C41F2BE5}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>libgambatte</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(ProjectDir)$(Configuration)\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(ProjectDir)$(Configuration)\</OutDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBGAMBATTE_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>include;src;src\common;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBGAMBATTE_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>include;src;src\common;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="src\bitmap_font.cpp" />
<ClCompile Include="src\cpu.cpp" />
<ClCompile Include="src\file\file.cpp" />
<ClCompile Include="src\gambatte.cpp" />
<ClCompile Include="src\initstate.cpp" />
<ClCompile Include="src\interrupter.cpp" />
<ClCompile Include="src\interruptrequester.cpp" />
<ClCompile Include="src\memory.cpp" />
<ClCompile Include="src\mem\cartridge.cpp" />
<ClCompile Include="src\mem\memptrs.cpp" />
<ClCompile Include="src\mem\rtc.cpp" />
<ClCompile Include="src\sound.cpp" />
<ClCompile Include="src\sound\channel1.cpp" />
<ClCompile Include="src\sound\channel2.cpp" />
<ClCompile Include="src\sound\channel3.cpp" />
<ClCompile Include="src\sound\channel4.cpp" />
<ClCompile Include="src\sound\duty_unit.cpp" />
<ClCompile Include="src\sound\envelope_unit.cpp" />
<ClCompile Include="src\sound\length_counter.cpp" />
<ClCompile Include="src\statesaver.cpp" />
<ClCompile Include="src\state_osd_elements.cpp" />
<ClCompile Include="src\tima.cpp" />
<ClCompile Include="src\video.cpp" />
<ClCompile Include="src\video\lyc_irq.cpp" />
<ClCompile Include="src\video\ly_counter.cpp" />
<ClCompile Include="src\video\next_m0_time.cpp" />
<ClCompile Include="src\video\ppu.cpp" />
<ClCompile Include="src\video\sprite_mapper.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\gambatte.h" />
<ClInclude Include="include\gbint.h" />
<ClInclude Include="include\inputgetter.h" />
<ClInclude Include="src\bitmap_font.h" />
<ClInclude Include="src\counterdef.h" />
<ClInclude Include="src\cpu.h" />
<ClInclude Include="src\file\file.h" />
<ClInclude Include="src\file\stdfile.h" />
<ClInclude Include="src\initstate.h" />
<ClInclude Include="src\insertion_sort.h" />
<ClInclude Include="src\interrupter.h" />
<ClInclude Include="src\interruptrequester.h" />
<ClInclude Include="src\memory.h" />
<ClInclude Include="src\mem\cartridge.h" />
<ClInclude Include="src\mem\memptrs.h" />
<ClInclude Include="src\mem\rtc.h" />
<ClInclude Include="src\minkeeper.h" />
<ClInclude Include="src\osd_element.h" />
<ClInclude Include="src\savestate.h" />
<ClInclude Include="src\sound.h" />
<ClInclude Include="src\sound\channel1.h" />
<ClInclude Include="src\sound\channel2.h" />
<ClInclude Include="src\sound\channel3.h" />
<ClInclude Include="src\sound\channel4.h" />
<ClInclude Include="src\sound\duty_unit.h" />
<ClInclude Include="src\sound\envelope_unit.h" />
<ClInclude Include="src\sound\length_counter.h" />
<ClInclude Include="src\sound\master_disabler.h" />
<ClInclude Include="src\sound\sound_unit.h" />
<ClInclude Include="src\sound\static_output_tester.h" />
<ClInclude Include="src\statesaver.h" />
<ClInclude Include="src\state_osd_elements.h" />
<ClInclude Include="src\tima.h" />
<ClInclude Include="src\video.h" />
<ClInclude Include="src\video\lyc_irq.h" />
<ClInclude Include="src\video\ly_counter.h" />
<ClInclude Include="src\video\next_m0_time.h" />
<ClInclude Include="src\video\ppu.h" />
<ClInclude Include="src\video\sprite_mapper.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,222 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\bitmap_font.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\cpu.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\gambatte.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\initstate.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\interrupter.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\interruptrequester.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\memory.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\sound.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\state_osd_elements.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\statesaver.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\tima.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\video.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\file\file.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\mem\cartridge.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\mem\memptrs.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\mem\rtc.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\sound\channel1.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\sound\channel2.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\sound\channel3.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\sound\channel4.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\sound\duty_unit.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\sound\envelope_unit.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\sound\length_counter.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\video\ly_counter.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\video\lyc_irq.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\video\next_m0_time.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\video\ppu.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\video\sprite_mapper.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\statesaver.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\video.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\memory.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\video\ppu.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\minkeeper.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\mem\cartridge.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\mem\memptrs.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cpu.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\mem\rtc.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\savestate.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\interrupter.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\file\file.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\file\stdfile.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\sound\channel1.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\sound\channel2.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\sound\channel3.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\sound\channel4.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\initstate.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\video\ly_counter.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\video\lyc_irq.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\video\next_m0_time.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\tima.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\bitmap_font.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\osd_element.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\sound.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\counterdef.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\sound\duty_unit.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\sound\envelope_unit.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\interruptrequester.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\sound\length_counter.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\sound\master_disabler.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\sound\sound_unit.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\video\sprite_mapper.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\state_osd_elements.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\sound\static_output_tester.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\insertion_sort.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\gambatte.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\gbint.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\inputgetter.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@ -0,0 +1,330 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
/*
The following font bitmaps (static const unsigned char *_bits[]), only used
as data and included in this source file for convenience, are derived from
the Bitstream Vera Sans font, which is distributed under the following
copyright:
Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera
is a trademark of Bitstream, Inc.
Permission is hereby granted, free of charge, to any person obtaining a
copy of the fonts accompanying this license ("Fonts") and associated
documentation files (the "Font Software"), to reproduce and distribute the
Font Software, including without limitation the rights to use, copy, merge,
publish, distribute, and/or sell copies of the Font Software, and to permit
persons to whom the Font Software is furnished to do so, subject to the
following conditions:
The above copyright and trademark notices and this permission notice shall
be included in all copies of one or more of the Font Software typefaces.
The Font Software may be modified, altered, or added to, and in particular
the designs of glyphs or characters in the Fonts may be modified and
additional glyphs or characters may be added to the Fonts, only if the fonts
are renamed to names not containing either the words "Bitstream" or the word
"Vera".
This License becomes null and void to the extent applicable to Fonts or Font
Software that has been modified and is distributed under the "Bitstream Vera"
names.
The Font Software may be sold as part of a larger software package but no
copy of one or more of the Font Software typefaces may be sold by itself.
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM
OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR
CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
Except as contained in this notice, the names of Gnome, the Gnome
Foundation, and Bitstream Inc., shall not be used in advertising or
otherwise to promote the sale, use or other dealings in this Font Software
without prior written authorization from the Gnome Foundation or
Bitstream Inc., respectively. For further information, contact: fonts at
gnome dot org.
*/
#include "bitmap_font.h"
static const unsigned char n0_bits[] = { 0x68,
0x00, 0x1c, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1c };
static const unsigned char n1_bits[] = { 0x68,
0x00, 0x0e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e };
static const unsigned char n2_bits[] = { 0x68,
0x00, 0x1c, 0x22, 0x20, 0x10, 0x08, 0x04, 0x3e };
static const unsigned char n3_bits[] = { 0x68,
0x00, 0x1c, 0x22, 0x20, 0x1c, 0x20, 0x22, 0x1c };
static const unsigned char n4_bits[] = { 0x68,
0x00, 0x18, 0x18, 0x14, 0x12, 0x3e, 0x10, 0x10 };
static const unsigned char n5_bits[] = { 0x68,
0x00, 0x1e, 0x02, 0x1e, 0x20, 0x20, 0x20, 0x1e };
static const unsigned char n6_bits[] = { 0x68,
0x00, 0x3c, 0x06, 0x02, 0x1e, 0x22, 0x22, 0x1c };
static const unsigned char n7_bits[] = { 0x68,
0x00, 0x3e, 0x20, 0x10, 0x10, 0x08, 0x08, 0x04 };
static const unsigned char n8_bits[] = { 0x68,
0x00, 0x1c, 0x22, 0x22, 0x1c, 0x22, 0x22, 0x1c };
static const unsigned char n9_bits[] = { 0x68,
0x00, 0x1c, 0x22, 0x22, 0x3c, 0x20, 0x30, 0x1e };
static const unsigned char A_bits[] = { 0x78,
0x00, 0x08, 0x14, 0x14, 0x22, 0x3e, 0x22, 0x41 };
static const unsigned char a_bits[] = { 0x68,
0x00, 0x00, 0x00, 0x1c, 0x20, 0x3c, 0x22, 0x3e };
static const unsigned char B_bits[] = { 0x78,
0x00, 0x1e, 0x22, 0x22, 0x1e, 0x22, 0x22, 0x1e };
static const unsigned char b_bits[] = { 0x68,
0x02, 0x02, 0x02, 0x1e, 0x22, 0x22, 0x22, 0x1e };
static const unsigned char C_bits[] = { 0x88,
0x00, 0x38, 0x44, 0x02, 0x02, 0x02, 0x44, 0x38 };
static const unsigned char c_bits[] = { 0x58,
0x00, 0x00, 0x00, 0x1c, 0x02, 0x02, 0x02, 0x1c };
static const unsigned char D_bits[] = { 0x88,
0x00, 0x3e, 0x62, 0x42, 0x42, 0x42, 0x62, 0x3e };
static const unsigned char d_bits[] = { 0x68,
0x20, 0x20, 0x20, 0x3c, 0x22, 0x22, 0x22, 0x3c };
static const unsigned char E_bits[] = { 0x78,
0x00, 0x3e, 0x02, 0x02, 0x3e, 0x02, 0x02, 0x3e };
static const unsigned char e_bits[] = { 0x68,
0x00, 0x00, 0x00, 0x1c, 0x22, 0x3e, 0x02, 0x3c };
static const unsigned char F_bits[] = { 0x68,
0x00, 0x1e, 0x02, 0x02, 0x1e, 0x02, 0x02, 0x02 };
static const unsigned char f_bits[] = { 0x48,
0x0e, 0x02, 0x02, 0x07, 0x02, 0x02, 0x02, 0x02 };
static const unsigned char G_bits[] = { 0x88,
0x00, 0x3c, 0x46, 0x02, 0x72, 0x42, 0x46, 0x3c };
static const unsigned char g_bits[] = { 0x6a,
0x00, 0x00, 0x00, 0x3c, 0x22, 0x22, 0x22, 0x3c, 0x20, 0x1c };
static const unsigned char H_bits[] = { 0x88,
0x00, 0x42, 0x42, 0x42, 0x7e, 0x42, 0x42, 0x42 };
static const unsigned char h_bits[] = { 0x68,
0x02, 0x02, 0x02, 0x1e, 0x22, 0x22, 0x22, 0x22 };
static const unsigned char I_bits[] = { 0x38,
0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 };
static const unsigned char i_bits[] = { 0x28,
0x02, 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02 };
static const unsigned char J_bits[] = { 0x4a,
0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03 };
static const unsigned char j_bits[] = { 0x2a,
0x02, 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03 };
static const unsigned char K_bits[] = { 0x78,
0x00, 0x22, 0x12, 0x0a, 0x06, 0x0a, 0x12, 0x22 };
static const unsigned char k_bits[] = { 0x58,
0x02, 0x02, 0x02, 0x12, 0x0a, 0x06, 0x0a, 0x12 };
static const unsigned char L_bits[] = { 0x68,
0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3e };
static const unsigned char l_bits[] = { 0x28,
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 };
static const unsigned char M_bits[] = { 0x98,
0x00, 0x00, 0x82, 0x00, 0xc6, 0x00, 0xc6, 0x00, 0xaa, 0x00, 0xaa, 0x00,
0x92, 0x00, 0x82, 0x00 };
static const unsigned char m_bits[] = { 0xa8,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x01, 0x22, 0x02, 0x22, 0x02,
0x22, 0x02, 0x22, 0x02 };
static const unsigned char N_bits[] = { 0x88,
0x00, 0x42, 0x46, 0x4a, 0x4a, 0x52, 0x62, 0x42 };
static const unsigned char n_bits[] = { 0x68,
0x00, 0x00, 0x00, 0x1e, 0x22, 0x22, 0x22, 0x22 };
static const unsigned char O_bits[] = { 0x88,
0x00, 0x3c, 0x66, 0x42, 0x42, 0x42, 0x66, 0x3c };
static const unsigned char o_bits[] = { 0x68,
0x00, 0x00, 0x00, 0x1c, 0x22, 0x22, 0x22, 0x1c };
static const unsigned char P_bits[] = { 0x78,
0x00, 0x1e, 0x22, 0x22, 0x1e, 0x02, 0x02, 0x02 };
static const unsigned char p_bits[] = { 0x6a,
0x00, 0x00, 0x00, 0x1e, 0x22, 0x22, 0x22, 0x1e, 0x02, 0x02 };
static const unsigned char Q_bits[] = { 0x89,
0x00, 0x3c, 0x66, 0x42, 0x42, 0x42, 0x26, 0x1c, 0x20 };
static const unsigned char q_bits[] = { 0x6a,
0x00, 0x00, 0x00, 0x3c, 0x22, 0x22, 0x22, 0x3c, 0x20, 0x20 };
static const unsigned char R_bits[] = { 0x78,
0x00, 0x1e, 0x22, 0x22, 0x1e, 0x12, 0x22, 0x42 };
static const unsigned char r_bits[] = { 0x48,
0x00, 0x00, 0x00, 0x0e, 0x02, 0x02, 0x02, 0x02 };
static const unsigned char S_bits[] = { 0x78,
0x00, 0x1c, 0x22, 0x02, 0x1c, 0x20, 0x22, 0x1c };
static const unsigned char s_bits[] = { 0x58,
0x00, 0x00, 0x00, 0x1e, 0x02, 0x1c, 0x10, 0x1e };
static const unsigned char T_bits[] = { 0x58,
0x00, 0x1f, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04 };
static const unsigned char t_bits[] = { 0x48,
0x00, 0x02, 0x02, 0x0f, 0x02, 0x02, 0x02, 0x0e };
static const unsigned char U_bits[] = { 0x88,
0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c };
static const unsigned char u_bits[] = { 0x68,
0x00, 0x00, 0x00, 0x22, 0x22, 0x22, 0x22, 0x3c };
static const unsigned char V_bits[] = { 0x78,
0x00, 0x41, 0x41, 0x22, 0x22, 0x14, 0x14, 0x08 };
static const unsigned char v_bits[] = { 0x68,
0x00, 0x00, 0x00, 0x22, 0x22, 0x14, 0x14, 0x08 };
static const unsigned char W_bits[] = { 0x98,
0x00, 0x00, 0x11, 0x01, 0x11, 0x01, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00,
0x44, 0x00, 0x44, 0x00 };
static const unsigned char w_bits[] = { 0x88,
0x00, 0x00, 0x00, 0x92, 0xaa, 0xaa, 0x44, 0x44 };
static const unsigned char X_bits[] = { 0x68,
0x00, 0x21, 0x12, 0x0c, 0x0c, 0x0c, 0x12, 0x21 };
static const unsigned char x_bits[] = { 0x68,
0x00, 0x00, 0x00, 0x22, 0x14, 0x08, 0x14, 0x22 };
static const unsigned char Y_bits[] = { 0x78,
0x00, 0x41, 0x22, 0x14, 0x08, 0x08, 0x08, 0x08 };
static const unsigned char y_bits[] = { 0x6a,
0x00, 0x00, 0x00, 0x22, 0x22, 0x14, 0x14, 0x08, 0x08, 0x06 };
static const unsigned char Z_bits[] = { 0x68,
0x00, 0x3f, 0x10, 0x08, 0x0c, 0x04, 0x02, 0x3f };
static const unsigned char z_bits[] = { 0x58,
0x00, 0x00, 0x00, 0x1e, 0x10, 0x08, 0x04, 0x1e };
static const unsigned char SPC_bits[] = { 0x38,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
namespace bitmapfont {
const unsigned char *const font[] = {
0,
n0_bits, n1_bits, n2_bits, n3_bits, n4_bits, n5_bits, n6_bits, n7_bits, n8_bits, n9_bits,
A_bits, B_bits, C_bits, D_bits, E_bits, F_bits, G_bits, H_bits, I_bits, J_bits, K_bits, L_bits, M_bits,
N_bits, O_bits, P_bits, Q_bits, R_bits, S_bits, T_bits, U_bits, V_bits, W_bits, X_bits, Y_bits, Z_bits,
a_bits, b_bits, c_bits, d_bits, e_bits, f_bits, g_bits, h_bits, i_bits, j_bits, k_bits, l_bits, m_bits,
n_bits, o_bits, p_bits, q_bits, r_bits, s_bits, t_bits, u_bits, v_bits, w_bits, x_bits, y_bits, z_bits,
SPC_bits
};
unsigned getWidth(const char *chars) {
unsigned w = 0;
while (const int character = *chars++) {
w += *font[character] >> 4;
}
return w;
}
namespace {
class Rgb32Fill {
const unsigned long color;
public:
explicit Rgb32Fill(unsigned long color) : color(color) {}
void operator()(gambatte::uint_least32_t *dest, unsigned /*pitch*/) const {
*dest = color;
}
};
}
void print(gambatte::uint_least32_t *dest, const unsigned pitch, const unsigned long color, const char *chars) {
print(dest, pitch, Rgb32Fill(color), chars);
}
static void reverse(char *first, char *last) {
while (first < last) {
const int tmp = *first;
*first = *last;
*last = tmp;
++first;
--last;
}
}
void utoa(unsigned u, char *a) {
char *aa = a;
while (u > 9) {
const unsigned div = u / 10;
const unsigned rem = u % 10;
u = div;
*aa++ = rem + N0;
}
*aa = u + N0;
reverse(a, aa);
}
}

View File

@ -0,0 +1,87 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef BITMAP_FONT_H
#define BITMAP_FONT_H
#include "gbint.h"
namespace bitmapfont {
enum Char {
NUL,
N0, N1, N2, N3, N4, N5, N6, N7, N8, N9,
A, B, C, D, E, F, G, H, I, J, K, L, M,
N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
a, b, c, d, e, f, g, h, i, j, k, l, m,
n, o, p, q, r, s, t, u, v, w, x, y, z,
SPC
};
enum { HEIGHT = 10 };
enum { MAX_WIDTH = 9 };
enum { NUMBER_WIDTH = 6 };
unsigned getWidth(const char *chars);
// struct Fill { void operator()(RandomAccessIterator dest, unsigned pitch) { fill pixels at dest } }
template<class RandomAccessIterator, class Fill>
void print(RandomAccessIterator dest, unsigned pitch, Fill fill, const char *chars);
void print(gambatte::uint_least32_t *dest, unsigned pitch, unsigned long color, const char *chars);
void utoa(unsigned u, char *a);
// --- INTERFACE END ---
extern const unsigned char *const font[];
template<class RandomAccessIterator, class Fill>
void print(RandomAccessIterator dest, const unsigned pitch, Fill fill, const char *chars) {
while (const int character = *chars++) {
RandomAccessIterator dst = dest;
const unsigned char *s = font[character];
const unsigned width = *s >> 4;
unsigned h = *s++ & 0xF;
while (h--) {
RandomAccessIterator d = dst;
unsigned line = *s++;
if (width > 8)
line |= *s++ << 8;
while (line) {
if (line & 1)
fill(d, pitch);
line >>= 1;
++d;
}
dst += pitch;
}
dest += width;
}
}
}
#endif

View File

@ -0,0 +1,52 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef ARRAY_H
#define ARRAY_H
#include <cstddef>
#include "uncopyable.h"
template<typename T>
class Array : Uncopyable {
T *a;
std::size_t sz;
public:
explicit Array(const std::size_t size = 0) : a(size ? new T[size] : 0), sz(size) {}
~Array() { delete []a; }
void reset(const std::size_t size = 0) { delete []a; a = size ? new T[size] : 0; sz = size; }
std::size_t size() const { return sz; }
T * get() const { return a; }
operator T*() const { return a; }
};
template<typename T>
class ScopedArray : Uncopyable {
T *a_;
public:
explicit ScopedArray(T *a = 0) : a_(a) {}
~ScopedArray() { delete []a_; }
void reset(T *a = 0) { delete []a_; a_ = a; }
T * release() { T *a = a_; a_ = 0; return a; }
T * get() const { return a_; }
operator T*() const { return a_; }
};
#endif

View File

@ -0,0 +1,29 @@
/***************************************************************************
* Copyright (C) 2009 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef UNCOPYABLE_H
#define UNCOPYABLE_H
class Uncopyable {
Uncopyable(const Uncopyable&);
Uncopyable& operator=(const Uncopyable&);
public:
Uncopyable() {}
};
#endif

View File

@ -0,0 +1,8 @@
#ifndef COUNTERDEF_H
#define COUNTERDEF_H
namespace gambatte {
enum { DISABLED_TIME = 0xFFFFFFFFul };
}
#endif

2815
libgambatte/src/cpu.cpp Normal file

File diff suppressed because it is too large Load Diff

99
libgambatte/src/cpu.h Normal file
View File

@ -0,0 +1,99 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef CPU_H
#define CPU_H
#include "memory.h"
namespace gambatte {
class CPU {
Memory memory;
unsigned long cycleCounter_;
unsigned short PC_;
unsigned short SP;
unsigned HF1, HF2, ZF, CF;
unsigned char A_, B, C, D, E, /*F,*/ H, L;
bool skip;
void process(unsigned long cycles);
public:
CPU();
// void halt();
// unsigned interrupt(unsigned address, unsigned cycleCounter);
long runFor(unsigned long cycles);
void setStatePtrs(SaveState &state);
void saveState(SaveState &state);
void loadState(const SaveState &state);
void loadSavedata() { memory.loadSavedata(); }
void saveSavedata() { memory.saveSavedata(); }
void setVideoBuffer(uint_least32_t *const videoBuf, const int pitch) {
memory.setVideoBuffer(videoBuf, pitch);
}
void setInputGetter(InputGetter *getInput) {
memory.setInputGetter(getInput);
}
void setSaveDir(const std::string &sdir) {
memory.setSaveDir(sdir);
}
const std::string saveBasePath() const {
return memory.saveBasePath();
}
void setOsdElement(std::auto_ptr<OsdElement> osdElement) {
memory.setOsdElement(osdElement);
}
int load(const std::string &romfile, bool forceDmg, bool multicartCompat) {
return memory.loadROM(romfile, forceDmg, multicartCompat);
}
bool loaded() const { return memory.loaded(); }
const char * romTitle() const { return memory.romTitle(); }
void setSoundBuffer(uint_least32_t *const buf) { memory.setSoundBuffer(buf); }
unsigned fillSoundBuffer() { return memory.fillSoundBuffer(cycleCounter_); }
bool isCgb() const { return memory.isCgb(); }
void setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned rgb32) {
memory.setDmgPaletteColor(palNum, colorNum, rgb32);
}
void setGameGenie(const std::string &codes) { memory.setGameGenie(codes); }
void setGameShark(const std::string &codes) { memory.setGameShark(codes); }
};
}
#endif

View File

@ -0,0 +1,26 @@
/***************************************************************************
Copyright (C) 2007 by Nach
http://nsrt.edgeemu.com
Copyright (C) 2007-2011 by Sindre Aamås
aamas@stud.ntnu.no
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License version 2 for more details.
You should have received a copy of the GNU General Public License
version 2 along with this program; if not, write to the
Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
***************************************************************************/
#include "stdfile.h"
std::auto_ptr<gambatte::File> gambatte::newFileInstance(const std::string &filepath) {
return std::auto_ptr<File>(new StdFile(filepath.c_str()));
}

View File

@ -0,0 +1,43 @@
/***************************************************************************
Copyright (C) 2007 by Nach
http://nsrt.edgeemu.com
Copyright (C) 2007-2011 by Sindre Aamås
aamas@stud.ntnu.no
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License version 2 for more details.
You should have received a copy of the GNU General Public License
version 2 along with this program; if not, write to the
Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
***************************************************************************/
#ifndef GAMBATTE_FILE_H
#define GAMBATTE_FILE_H
#include <memory>
#include <string>
namespace gambatte {
class File {
public:
virtual ~File() {}
virtual void rewind() = 0;
virtual std::size_t size() const = 0;
virtual void read(char *buffer, std::size_t amount) = 0;
virtual bool fail() const = 0;
};
std::auto_ptr<File> newFileInstance(const std::string &filepath);
}
#endif

View File

@ -0,0 +1,53 @@
/***************************************************************************
Copyright (C) 2007 by Nach
http://nsrt.edgeemu.com
Copyright (C) 2007-2011 by Sindre Aamås
aamas@stud.ntnu.no
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License version 2 for more details.
You should have received a copy of the GNU General Public License
version 2 along with this program; if not, write to the
Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
***************************************************************************/
#ifndef GAMBATTE_STD_FILE_H
#define GAMBATTE_STD_FILE_H
#include "file.h"
#include <fstream>
namespace gambatte {
class StdFile : public File {
std::ifstream stream;
std::size_t fsize;
public:
explicit StdFile(const char *filename)
: stream(filename, std::ios::in | std::ios::binary), fsize(0)
{
if (stream) {
stream.seekg(0, std::ios::end);
fsize = stream.tellg();
stream.seekg(0, std::ios::beg);
}
}
virtual void rewind() { stream.seekg(0, std::ios::beg); }
virtual std::size_t size() const { return fsize; };
virtual void read(char *buffer, std::size_t amount) { stream.read(buffer, amount); }
virtual bool fail() const { return stream.fail(); }
};
}
#endif

View File

@ -0,0 +1,202 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "gambatte.h"
#include "cpu.h"
#include "savestate.h"
#include "statesaver.h"
#include "initstate.h"
#include "state_osd_elements.h"
#include <sstream>
#include <cstring>
static const std::string itos(const int i) {
std::stringstream ss;
ss << i;
return ss.str();
}
static const std::string statePath(const std::string &basePath, const int stateNo) {
return basePath + "_" + itos(stateNo) + ".gqs";
}
namespace gambatte {
struct GB::Priv {
CPU cpu;
int stateNo;
bool gbaCgbMode;
Priv() : stateNo(1), gbaCgbMode(false) {}
};
GB::GB() : p_(new Priv) {}
GB::~GB() {
if (p_->cpu.loaded())
p_->cpu.saveSavedata();
delete p_;
}
long GB::runFor(gambatte::uint_least32_t *const videoBuf, const int pitch,
gambatte::uint_least32_t *const soundBuf, unsigned &samples) {
if (!p_->cpu.loaded()) {
samples = 0;
return -1;
}
p_->cpu.setVideoBuffer(videoBuf, pitch);
p_->cpu.setSoundBuffer(soundBuf);
const long cyclesSinceBlit = p_->cpu.runFor(samples * 2);
samples = p_->cpu.fillSoundBuffer();
return cyclesSinceBlit < 0 ? cyclesSinceBlit : static_cast<long>(samples) - (cyclesSinceBlit >> 1);
}
void GB::reset() {
if (p_->cpu.loaded()) {
p_->cpu.saveSavedata();
SaveState state;
p_->cpu.setStatePtrs(state);
setInitState(state, p_->cpu.isCgb(), p_->gbaCgbMode);
p_->cpu.loadState(state);
p_->cpu.loadSavedata();
}
}
void GB::setInputGetter(InputGetter *getInput) {
p_->cpu.setInputGetter(getInput);
}
void GB::setSaveDir(const std::string &sdir) {
p_->cpu.setSaveDir(sdir);
}
int GB::load(const std::string &romfile, const unsigned flags) {
if (p_->cpu.loaded())
p_->cpu.saveSavedata();
const int failed = p_->cpu.load(romfile, flags & FORCE_DMG, flags & MULTICART_COMPAT);
if (!failed) {
SaveState state;
p_->cpu.setStatePtrs(state);
setInitState(state, p_->cpu.isCgb(), p_->gbaCgbMode = flags & GBA_CGB);
p_->cpu.loadState(state);
p_->cpu.loadSavedata();
p_->stateNo = 1;
p_->cpu.setOsdElement(std::auto_ptr<OsdElement>());
}
return failed;
}
bool GB::isCgb() const {
return p_->cpu.isCgb();
}
bool GB::isLoaded() const {
return p_->cpu.loaded();
}
void GB::saveSavedata() {
if (p_->cpu.loaded())
p_->cpu.saveSavedata();
}
void GB::setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned rgb32) {
p_->cpu.setDmgPaletteColor(palNum, colorNum, rgb32);
}
bool GB::loadState(const std::string &filepath) {
if (p_->cpu.loaded()) {
p_->cpu.saveSavedata();
SaveState state;
p_->cpu.setStatePtrs(state);
if (StateSaver::loadState(state, filepath)) {
p_->cpu.loadState(state);
return true;
}
}
return false;
}
bool GB::saveState(const gambatte::uint_least32_t *const videoBuf, const int pitch) {
if (saveState(videoBuf, pitch, statePath(p_->cpu.saveBasePath(), p_->stateNo))) {
p_->cpu.setOsdElement(newStateSavedOsdElement(p_->stateNo));
return true;
}
return false;
}
bool GB::loadState() {
if (loadState(statePath(p_->cpu.saveBasePath(), p_->stateNo))) {
p_->cpu.setOsdElement(newStateLoadedOsdElement(p_->stateNo));
return true;
}
return false;
}
bool GB::saveState(const gambatte::uint_least32_t *const videoBuf, const int pitch, const std::string &filepath) {
if (p_->cpu.loaded()) {
SaveState state;
p_->cpu.setStatePtrs(state);
p_->cpu.saveState(state);
return StateSaver::saveState(state, videoBuf, pitch, filepath);
}
return false;
}
void GB::selectState(int n) {
n -= (n / 10) * 10;
p_->stateNo = n < 0 ? n + 10 : n;
if (p_->cpu.loaded())
p_->cpu.setOsdElement(newSaveStateOsdElement(statePath(p_->cpu.saveBasePath(), p_->stateNo), p_->stateNo));
}
int GB::currentState() const { return p_->stateNo; }
const std::string GB::romTitle() const {
if (p_->cpu.loaded()) {
char title[0x11];
std::memcpy(title, p_->cpu.romTitle(), 0x10);
title[(title[0xF] & 0x80) ? 0xF : 0x10] = '\0';
return std::string(title);
}
return std::string();
}
void GB::setGameGenie(const std::string &codes) {
p_->cpu.setGameGenie(codes);
}
void GB::setGameShark(const std::string &codes) {
p_->cpu.setGameShark(codes);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,26 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef INITSTATE_H
#define INITSTATE_H
namespace gambatte {
void setInitState(struct SaveState &state, bool cgb, bool gbaCgbMode);
}
#endif

View File

@ -0,0 +1,51 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef INSERTION_SORT_H
#define INSERTION_SORT_H
#include <functional>
template<typename T, class Less>
void insertionSort(T *const start, T *const end, Less less) {
if (start >= end)
return;
T *a = start;
while (++a < end) {
const T e = *a;
T *b = a;
while (b != start && less(e, *(b - 1))) {
*b = *(b - 1);
b = b - 1;
}
*b = e;
}
}
template<typename T>
inline void insertionSort(T *const start, T *const end) {
insertionSort(start, end, std::less<T>());
}
#endif /*INSERTION_SORT_H*/

View File

@ -0,0 +1,71 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "interrupter.h"
#include "memory.h"
namespace gambatte {
Interrupter::Interrupter(unsigned short &SP_in, unsigned short &PC_in) :
SP(SP_in),
PC(PC_in)
{}
unsigned long Interrupter::interrupt(const unsigned address, unsigned long cycleCounter, Memory &memory) {
cycleCounter += 8;
SP = (SP - 1) & 0xFFFF;
memory.write(SP, PC >> 8, cycleCounter);
cycleCounter += 4;
SP = (SP - 1) & 0xFFFF;
memory.write(SP, PC & 0xFF, cycleCounter);
PC = address;
cycleCounter += 8;
if (address == 0x40 && !gsCodes.empty())
applyVblankCheats(cycleCounter, memory);
return cycleCounter;
}
static int asHex(const char c) {
return c >= 'A' ? c - 'A' + 0xA : c - '0';
}
void Interrupter::setGameShark(const std::string &codes) {
std::string code;
gsCodes.clear();
for (std::size_t pos = 0; pos < codes.length() && (code = codes.substr(pos, codes.find(';', pos) - pos), true); pos += code.length() + 1) {
if (code.length() >= 8) {
GsCode gs;
gs.type = asHex(code[0]) << 4 | asHex(code[1]);
gs.value = (asHex(code[2]) << 4 | asHex(code[3])) & 0xFF;
gs.address = (asHex(code[4]) << 4 | asHex(code[5]) | asHex(code[6]) << 12 | asHex(code[7]) << 8) & 0xFFFF;
gsCodes.push_back(gs);
}
}
}
void Interrupter::applyVblankCheats(const unsigned long cycleCounter, Memory &memory) {
for (std::size_t i = 0, size = gsCodes.size(); i < size; ++i) {
if (gsCodes[i].type == 0x01)
memory.write(gsCodes[i].address, gsCodes[i].value, cycleCounter);
}
}
}

View File

@ -0,0 +1,47 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef INTERRUPTER_H
#define INTERRUPTER_H
#include <string>
#include <vector>
namespace gambatte {
struct GsCode {
unsigned short address;
unsigned char value;
unsigned char type;
};
class Interrupter {
unsigned short &SP;
unsigned short &PC;
std::vector<GsCode> gsCodes;
void applyVblankCheats(unsigned long cc, class Memory &mem);
public:
Interrupter(unsigned short &SP, unsigned short &PC);
unsigned long interrupt(const unsigned address, unsigned long cycleCounter, class Memory &memory);
void setGameShark(const std::string &codes);
};
}
#endif

View File

@ -0,0 +1,103 @@
/***************************************************************************
* Copyright (C) 2010 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "interruptrequester.h"
#include "savestate.h"
namespace gambatte {
InterruptRequester::InterruptRequester() : minIntTime(0), ifreg_(0), iereg_(0) {}
void InterruptRequester::saveState(SaveState &state) const {
state.mem.minIntTime = minIntTime;
state.mem.IME = ime();
state.mem.halted = halted();
}
void InterruptRequester::loadState(const SaveState &state) {
minIntTime = state.mem.minIntTime;
ifreg_ = state.mem.ioamhram.get()[0x10F];
iereg_ = state.mem.ioamhram.get()[0x1FF] & 0x1F;
intFlags.set(state.mem.IME, state.mem.halted);
eventTimes.setValue<INTERRUPTS>(intFlags.imeOrHalted() && pendingIrqs() ? minIntTime : static_cast<unsigned long>(DISABLED_TIME));
}
void InterruptRequester::resetCc(const unsigned long oldCc, const unsigned long newCc) {
minIntTime = minIntTime < oldCc ? 0 : minIntTime - (oldCc - newCc);
if (eventTimes.value(INTERRUPTS) != DISABLED_TIME)
eventTimes.setValue<INTERRUPTS>(minIntTime);
}
void InterruptRequester::ei(const unsigned long cc) {
intFlags.setIme();
minIntTime = cc + 1;
if (pendingIrqs())
eventTimes.setValue<INTERRUPTS>(minIntTime);
}
void InterruptRequester::di() {
intFlags.unsetIme();
if (!intFlags.imeOrHalted())
eventTimes.setValue<INTERRUPTS>(DISABLED_TIME);
}
void InterruptRequester::halt() {
intFlags.setHalted();
if (pendingIrqs())
eventTimes.setValue<INTERRUPTS>(minIntTime);
}
void InterruptRequester::unhalt() {
intFlags.unsetHalted();
if (!intFlags.imeOrHalted())
eventTimes.setValue<INTERRUPTS>(DISABLED_TIME);
}
void InterruptRequester::flagIrq(const unsigned bit) {
ifreg_ |= bit;
if (intFlags.imeOrHalted() && pendingIrqs())
eventTimes.setValue<INTERRUPTS>(minIntTime);
}
void InterruptRequester::ackIrq(const unsigned bit) {
ifreg_ ^= bit;
di();
}
void InterruptRequester::setIereg(const unsigned iereg) {
iereg_ = iereg & 0x1F;
if (intFlags.imeOrHalted())
eventTimes.setValue<INTERRUPTS>(pendingIrqs() ? minIntTime : static_cast<unsigned long>(DISABLED_TIME));
}
void InterruptRequester::setIfreg(const unsigned ifreg) {
ifreg_ = ifreg;
if (intFlags.imeOrHalted())
eventTimes.setValue<INTERRUPTS>(pendingIrqs() ? minIntTime : static_cast<unsigned long>(DISABLED_TIME));
}
}

View File

@ -0,0 +1,92 @@
/***************************************************************************
* Copyright (C) 2010 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef INTERRUPT_REQUESTER_H
#define INTERRUPT_REQUESTER_H
#include "counterdef.h"
#include "minkeeper.h"
namespace gambatte {
struct SaveState;
enum MemEventId { UNHALT, END, BLIT, SERIAL, OAM, DMA, TIMA, VIDEO, INTERRUPTS };
class InterruptRequester {
MinKeeper<INTERRUPTS + 1> eventTimes;
unsigned long minIntTime;
unsigned ifreg_;
unsigned iereg_;
class IntFlags {
unsigned char flags_;
enum { IME_MASK = 1, HALTED_MASK = 2 };
public:
IntFlags() : flags_(0) {}
bool ime() const { return flags_ & IME_MASK; }
bool halted() const { return flags_ & HALTED_MASK; }
bool imeOrHalted() const { return flags_; }
void setIme() { flags_ |= IME_MASK; }
void unsetIme() { flags_ &= ~IME_MASK; }
void setHalted() { flags_ |= HALTED_MASK; }
void unsetHalted() { flags_ &= ~HALTED_MASK; }
void set(const bool ime, const bool halted) { flags_ = halted * HALTED_MASK + ime * IME_MASK; }
} intFlags;
public:
InterruptRequester();
void saveState(SaveState &) const;
void loadState(const SaveState &);
void resetCc(unsigned long oldCc, unsigned long newCc);
unsigned ifreg() const { return ifreg_; }
unsigned pendingIrqs() const { return ifreg_ & iereg_; }
bool ime() const { return intFlags.ime(); }
bool halted() const { return intFlags.halted(); }
void ei(unsigned long cc);
void di();
void halt();
void unhalt();
void flagIrq(unsigned bit);
void ackIrq(unsigned bit);
void setIereg(unsigned iereg);
void setIfreg(unsigned ifreg);
MemEventId minEventId() const { return static_cast<MemEventId>(eventTimes.min()); }
unsigned long minEventTime() const { return eventTimes.minValue(); }
template<MemEventId id> void setEventTime(unsigned long value) { eventTimes.setValue<id>(value); }
void setEventTime(const MemEventId id, unsigned long value) { eventTimes.setValue(id, value); }
unsigned long eventTime(MemEventId id) const { return eventTimes.value(id); }
};
inline void flagHdmaReq(InterruptRequester *const intreq) { intreq->setEventTime<DMA>(0); }
inline void flagGdmaReq(InterruptRequester *const intreq) { intreq->setEventTime<DMA>(1); }
inline void ackDmaReq(InterruptRequester *const intreq) { intreq->setEventTime<DMA>(DISABLED_TIME); }
inline bool hdmaReqFlagged(const InterruptRequester &intreq) { return intreq.eventTime(DMA) == 0; }
inline bool gdmaReqFlagged(const InterruptRequester &intreq) { return intreq.eventTime(DMA) == 1; }
}
#endif

View File

@ -0,0 +1,741 @@
/***************************************************************************
* Copyright (C) 2007-2010 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "cartridge.h"
#include "file/file.h"
#include "../savestate.h"
#include <cstdio>
#include <cstring>
#include <fstream>
namespace gambatte {
namespace {
static unsigned toMulti64Rombank(const unsigned rombank) {
return (rombank >> 1 & 0x30) | (rombank & 0xF);
}
class DefaultMbc : public Mbc {
public:
virtual bool isAddressWithinAreaRombankCanBeMappedTo(unsigned addr, unsigned bank) const {
return (addr< 0x4000) == (bank == 0);
}
};
class Mbc0 : public DefaultMbc {
MemPtrs &memptrs;
bool enableRam;
public:
explicit Mbc0(MemPtrs &memptrs)
: memptrs(memptrs),
enableRam(false)
{
}
virtual void romWrite(const unsigned P, const unsigned data) {
if (P < 0x2000) {
enableRam = (data & 0xF) == 0xA;
memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, 0);
}
}
virtual void saveState(SaveState::Mem &ss) const {
ss.enableRam = enableRam;
}
virtual void loadState(const SaveState::Mem &ss) {
enableRam = ss.enableRam;
memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, 0);
}
};
static inline unsigned rambanks(const MemPtrs &memptrs) {
return static_cast<std::size_t>(memptrs.rambankdataend() - memptrs.rambankdata()) / 0x2000;
}
static inline unsigned rombanks(const MemPtrs &memptrs) {
return static_cast<std::size_t>(memptrs.romdataend() - memptrs.romdata() ) / 0x4000;
}
class Mbc1 : public DefaultMbc {
MemPtrs &memptrs;
unsigned char rombank;
unsigned char rambank;
bool enableRam;
bool rambankMode;
static unsigned adjustedRombank(unsigned bank) { return bank & 0x1F ? bank : bank | 1; }
void setRambank() const { memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, rambank & (rambanks(memptrs) - 1)); }
void setRombank() const { memptrs.setRombank(adjustedRombank(rombank & (rombanks(memptrs) - 1))); }
public:
explicit Mbc1(MemPtrs &memptrs)
: memptrs(memptrs),
rombank(1),
rambank(0),
enableRam(false),
rambankMode(false)
{
}
virtual void romWrite(const unsigned P, const unsigned data) {
switch (P >> 13 & 3) {
case 0:
enableRam = (data & 0xF) == 0xA;
setRambank();
break;
case 1:
rombank = rambankMode ? data & 0x1F : (rombank & 0x60) | (data & 0x1F);
setRombank();
break;
case 2:
if (rambankMode) {
rambank = data & 3;
setRambank();
} else {
rombank = (data << 5 & 0x60) | (rombank & 0x1F);
setRombank();
}
break;
case 3:
// Pretty sure this should take effect immediately, but I have a policy not to change old behavior
// unless I have something (eg. a verified test or a game) that justifies it.
rambankMode = data & 1;
break;
}
}
virtual void saveState(SaveState::Mem &ss) const {
ss.rombank = rombank;
ss.rambank = rambank;
ss.enableRam = enableRam;
ss.rambankMode = rambankMode;
}
virtual void loadState(const SaveState::Mem &ss) {
rombank = ss.rombank;
rambank = ss.rambank;
enableRam = ss.enableRam;
rambankMode = ss.rambankMode;
setRambank();
setRombank();
}
};
class Mbc1Multi64 : public Mbc {
MemPtrs &memptrs;
unsigned char rombank;
bool enableRam;
bool rombank0Mode;
static unsigned adjustedRombank(unsigned bank) { return bank & 0x1F ? bank : bank | 1; }
void setRombank() const {
if (rombank0Mode) {
const unsigned rb = toMulti64Rombank(rombank);
memptrs.setRombank0(rb & 0x30);
memptrs.setRombank(adjustedRombank(rb));
} else {
memptrs.setRombank0(0);
memptrs.setRombank(adjustedRombank(rombank & (rombanks(memptrs) - 1)));
}
}
public:
explicit Mbc1Multi64(MemPtrs &memptrs)
: memptrs(memptrs),
rombank(1),
enableRam(false),
rombank0Mode(false)
{
}
virtual void romWrite(const unsigned P, const unsigned data) {
switch (P >> 13 & 3) {
case 0:
enableRam = (data & 0xF) == 0xA;
memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, 0);
break;
case 1:
rombank = (rombank & 0x60) | (data & 0x1F);
memptrs.setRombank(adjustedRombank(rombank0Mode ? toMulti64Rombank(rombank) : rombank & (rombanks(memptrs) - 1)));
break;
case 2:
rombank = (data << 5 & 0x60) | (rombank & 0x1F);
setRombank();
break;
case 3:
rombank0Mode = data & 1;
setRombank();
break;
}
}
virtual void saveState(SaveState::Mem &ss) const {
ss.rombank = rombank;
ss.enableRam = enableRam;
ss.rambankMode = rombank0Mode;
}
virtual void loadState(const SaveState::Mem &ss) {
rombank = ss.rombank;
enableRam = ss.enableRam;
rombank0Mode = ss.rambankMode;
memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, 0);
setRombank();
}
virtual bool isAddressWithinAreaRombankCanBeMappedTo(unsigned addr, unsigned bank) const {
return (addr < 0x4000) == ((bank & 0xF) == 0);
}
};
class Mbc2 : public DefaultMbc {
MemPtrs &memptrs;
unsigned char rombank;
bool enableRam;
public:
explicit Mbc2(MemPtrs &memptrs)
: memptrs(memptrs),
rombank(1),
enableRam(false)
{
}
virtual void romWrite(const unsigned P, const unsigned data) {
switch (P & 0x6100) {
case 0x0000:
enableRam = (data & 0xF) == 0xA;
memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, 0);
break;
case 0x2100:
rombank = data & 0xF;
memptrs.setRombank(rombank & (rombanks(memptrs) - 1));
break;
}
}
virtual void saveState(SaveState::Mem &ss) const {
ss.rombank = rombank;
ss.enableRam = enableRam;
}
virtual void loadState(const SaveState::Mem &ss) {
rombank = ss.rombank;
enableRam = ss.enableRam;
memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, 0);
memptrs.setRombank(rombank & (rombanks(memptrs) - 1));
}
};
class Mbc3 : public DefaultMbc {
MemPtrs &memptrs;
Rtc *const rtc;
unsigned char rombank;
unsigned char rambank;
bool enableRam;
void setRambank() const {
unsigned flags = enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0;
if (rtc) {
rtc->set(enableRam, rambank);
if (rtc->getActive())
flags |= MemPtrs::RTC_EN;
}
memptrs.setRambank(flags, rambank & (rambanks(memptrs) - 1));
}
public:
Mbc3(MemPtrs &memptrs, Rtc *const rtc)
: memptrs(memptrs),
rtc(rtc),
rombank(1),
rambank(0),
enableRam(false)
{
}
virtual void romWrite(const unsigned P, const unsigned data) {
switch (P >> 13 & 3) {
case 0:
enableRam = (data & 0xF) == 0xA;
setRambank();
break;
case 1:
rombank = data & 0x7F;
memptrs.setRombank(rombank & (rombanks(memptrs) - 1));
break;
case 2:
rambank = data;
setRambank();
break;
case 3:
if (rtc)
rtc->latch(data);
break;
}
}
virtual void saveState(SaveState::Mem &ss) const {
ss.rombank = rombank;
ss.rambank = rambank;
ss.enableRam = enableRam;
}
virtual void loadState(const SaveState::Mem &ss) {
rombank = ss.rombank;
rambank = ss.rambank;
enableRam = ss.enableRam;
setRambank();
memptrs.setRombank(rombank & (rombanks(memptrs) - 1));
}
};
class HuC1 : public DefaultMbc {
MemPtrs &memptrs;
unsigned char rombank;
unsigned char rambank;
bool enableRam;
bool rambankMode;
void setRambank() const {
memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : MemPtrs::READ_EN,
rambankMode ? rambank & (rambanks(memptrs) - 1) : 0);
}
void setRombank() const { memptrs.setRombank((rambankMode ? rombank : rambank << 6 | rombank) & (rombanks(memptrs) - 1)); }
public:
explicit HuC1(MemPtrs &memptrs)
: memptrs(memptrs),
rombank(1),
rambank(0),
enableRam(false),
rambankMode(false)
{
}
virtual void romWrite(const unsigned P, const unsigned data) {
switch (P >> 13 & 3) {
case 0:
enableRam = (data & 0xF) == 0xA;
setRambank();
break;
case 1:
rombank = data & 0x3F;
setRombank();
break;
case 2:
rambank = data & 3;
rambankMode ? setRambank() : setRombank();
break;
case 3:
rambankMode = data & 1;
setRambank();
setRombank();
break;
}
}
virtual void saveState(SaveState::Mem &ss) const {
ss.rombank = rombank;
ss.rambank = rambank;
ss.enableRam = enableRam;
ss.rambankMode = rambankMode;
}
virtual void loadState(const SaveState::Mem &ss) {
rombank = ss.rombank;
rambank = ss.rambank;
enableRam = ss.enableRam;
rambankMode = ss.rambankMode;
setRambank();
setRombank();
}
};
class Mbc5 : public DefaultMbc {
MemPtrs &memptrs;
unsigned short rombank;
unsigned char rambank;
bool enableRam;
static unsigned adjustedRombank(const unsigned bank) { return bank ? bank : 1; }
void setRambank() const { memptrs.setRambank(enableRam ? MemPtrs::READ_EN | MemPtrs::WRITE_EN : 0, rambank & (rambanks(memptrs) - 1)); }
void setRombank() const { memptrs.setRombank(adjustedRombank(rombank & (rombanks(memptrs) - 1))); }
public:
explicit Mbc5(MemPtrs &memptrs)
: memptrs(memptrs),
rombank(1),
rambank(0),
enableRam(false)
{
}
virtual void romWrite(const unsigned P, const unsigned data) {
switch (P >> 13 & 3) {
case 0:
enableRam = (data & 0xF) == 0xA;
setRambank();
break;
case 1:
rombank = P < 0x3000 ? (rombank & 0x100) | data
: (data << 8 & 0x100) | (rombank & 0xFF);
setRombank();
break;
case 2:
rambank = data & 0xF;
setRambank();
break;
case 3:
break;
}
}
virtual void saveState(SaveState::Mem &ss) const {
ss.rombank = rombank;
ss.rambank = rambank;
ss.enableRam = enableRam;
}
virtual void loadState(const SaveState::Mem &ss) {
rombank = ss.rombank;
rambank = ss.rambank;
enableRam = ss.enableRam;
setRambank();
setRombank();
}
};
static bool hasRtc(const unsigned headerByte0x147) {
switch (headerByte0x147) {
case 0x0F:
case 0x10: return true;
default: return false;
}
}
}
void Cartridge::setStatePtrs(SaveState &state) {
state.mem.vram.set(memptrs.vramdata(), memptrs.vramdataend() - memptrs.vramdata());
state.mem.sram.set(memptrs.rambankdata(), memptrs.rambankdataend() - memptrs.rambankdata());
state.mem.wram.set(memptrs.wramdata(0), memptrs.wramdataend() - memptrs.wramdata(0));
}
void Cartridge::saveState(SaveState &state) const {
mbc->saveState(state.mem);
rtc.saveState(state);
}
void Cartridge::loadState(const SaveState &state) {
rtc.loadState(state);
mbc->loadState(state.mem);
}
static const std::string stripExtension(const std::string &str) {
const std::string::size_type lastDot = str.find_last_of('.');
const std::string::size_type lastSlash = str.find_last_of('/');
if (lastDot != std::string::npos && (lastSlash == std::string::npos || lastSlash < lastDot))
return str.substr(0, lastDot);
return str;
}
static const std::string stripDir(const std::string &str) {
const std::string::size_type lastSlash = str.find_last_of('/');
if (lastSlash != std::string::npos)
return str.substr(lastSlash + 1);
return str;
}
const std::string Cartridge::saveBasePath() const {
return saveDir.empty() ? defaultSaveBasePath : saveDir + stripDir(defaultSaveBasePath);
}
void Cartridge::setSaveDir(const std::string &dir) {
saveDir = dir;
if (!saveDir.empty() && saveDir[saveDir.length() - 1] != '/')
saveDir += '/';
}
static void enforce8bit(unsigned char *data, unsigned long sz) {
if (static_cast<unsigned char>(0x100))
while (sz--)
*data++ &= 0xFF;
}
static unsigned pow2ceil(unsigned n) {
--n;
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
++n;
return n;
}
int Cartridge::loadROM(const std::string &romfile, const bool forceDmg, const bool multicartCompat) {
const std::auto_ptr<File> rom(newFileInstance(romfile));
if (rom->fail())
return -1;
unsigned rambanks = 1;
unsigned rombanks = 2;
bool cgb = false;
enum Cartridgetype { PLAIN, MBC1, MBC2, MBC3, MBC5, HUC1 } type = PLAIN;
{
unsigned char header[0x150];
rom->read(reinterpret_cast<char*>(header), sizeof header);
switch (header[0x0147]) {
case 0x00: std::puts("Plain ROM loaded."); type = PLAIN; break;
case 0x01: std::puts("MBC1 ROM loaded."); type = MBC1; break;
case 0x02: std::puts("MBC1 ROM+RAM loaded."); type = MBC1; break;
case 0x03: std::puts("MBC1 ROM+RAM+BATTERY loaded."); type = MBC1; break;
case 0x05: std::puts("MBC2 ROM loaded."); type = MBC2; break;
case 0x06: std::puts("MBC2 ROM+BATTERY loaded."); type = MBC2; break;
case 0x08: std::puts("Plain ROM with additional RAM loaded."); type = PLAIN; break;
case 0x09: std::puts("Plain ROM with additional RAM and Battery loaded."); type = PLAIN; break;
case 0x0B: std::puts("MM01 ROM not supported."); return -1;
case 0x0C: std::puts("MM01 ROM not supported."); return -1;
case 0x0D: std::puts("MM01 ROM not supported."); return -1;
case 0x0F: std::puts("MBC3 ROM+TIMER+BATTERY loaded."); type = MBC3; break;
case 0x10: std::puts("MBC3 ROM+TIMER+RAM+BATTERY loaded."); type = MBC3; break;
case 0x11: std::puts("MBC3 ROM loaded."); type = MBC3; break;
case 0x12: std::puts("MBC3 ROM+RAM loaded."); type = MBC3; break;
case 0x13: std::puts("MBC3 ROM+RAM+BATTERY loaded."); type = MBC3; break;
case 0x15: std::puts("MBC4 ROM not supported."); return -1;
case 0x16: std::puts("MBC4 ROM not supported."); return -1;
case 0x17: std::puts("MBC4 ROM not supported."); return -1;
case 0x19: std::puts("MBC5 ROM loaded."); type = MBC5; break;
case 0x1A: std::puts("MBC5 ROM+RAM loaded."); type = MBC5; break;
case 0x1B: std::puts("MBC5 ROM+RAM+BATTERY loaded."); type = MBC5; break;
case 0x1C: std::puts("MBC5+RUMBLE ROM not supported."); type = MBC5; break;
case 0x1D: std::puts("MBC5+RUMBLE+RAM ROM not suported."); type = MBC5; break;
case 0x1E: std::puts("MBC5+RUMBLE+RAM+BATTERY ROM not supported."); type = MBC5; break;
case 0xFC: std::puts("Pocket Camera ROM not supported."); return -1;
case 0xFD: std::puts("Bandai TAMA5 ROM not supported."); return -1;
case 0xFE: std::puts("HuC3 ROM not supported."); return -1;
case 0xFF: std::puts("HuC1 ROM+RAM+BATTERY loaded."); type = HUC1; break;
default: std::puts("Wrong data-format, corrupt or unsupported ROM."); return -1;
}
/*switch (header[0x0148]) {
case 0x00: rombanks = 2; break;
case 0x01: rombanks = 4; break;
case 0x02: rombanks = 8; break;
case 0x03: rombanks = 16; break;
case 0x04: rombanks = 32; break;
case 0x05: rombanks = 64; break;
case 0x06: rombanks = 128; break;
case 0x07: rombanks = 256; break;
case 0x08: rombanks = 512; break;
case 0x52: rombanks = 72; break;
case 0x53: rombanks = 80; break;
case 0x54: rombanks = 96; break;
default: return -1;
}
std::printf("rombanks: %u\n", rombanks);*/
switch (header[0x0149]) {
case 0x00: /*std::puts("No RAM");*/ rambanks = type == MBC2; break;
case 0x01: /*std::puts("2kB RAM");*/ /*rambankrom=1; break;*/
case 0x02: /*std::puts("8kB RAM");*/
rambanks = 1;
break;
case 0x03: /*std::puts("32kB RAM");*/
rambanks = 4;
break;
case 0x04: /*std::puts("128kB RAM");*/
rambanks = 16;
break;
case 0x05: /*std::puts("undocumented kB RAM");*/
rambanks = 16;
break;
default: /*std::puts("Wrong data-format, corrupt or unsupported ROM loaded.");*/
rambanks = 16;
break;
}
cgb = header[0x0143] >> 7 & (1 ^ forceDmg);
std::printf("cgb: %d\n", cgb);
}
std::printf("rambanks: %u\n", rambanks);
const std::size_t filesize = rom->size();
rombanks = std::max(pow2ceil(filesize / 0x4000), 2u);
std::printf("rombanks: %u\n", static_cast<unsigned>(filesize / 0x4000));
defaultSaveBasePath.clear();
ggUndoList.clear();
mbc.reset();
memptrs.reset(rombanks, rambanks, cgb ? 8 : 2);
rtc.set(false, 0);
rom->rewind();
rom->read(reinterpret_cast<char*>(memptrs.romdata()), (filesize / 0x4000) * 0x4000ul);
std::memset(memptrs.romdata() + (filesize / 0x4000) * 0x4000ul, 0xFF, (rombanks - filesize / 0x4000) * 0x4000ul);
enforce8bit(memptrs.romdata(), rombanks * 0x4000ul);
if (rom->fail())
return -1;
defaultSaveBasePath = stripExtension(romfile);
switch (type) {
case PLAIN: mbc.reset(new Mbc0(memptrs)); break;
case MBC1:
if (!rambanks && rombanks == 64 && multicartCompat) {
std::puts("Multi-ROM \"MBC1\" presumed");
mbc.reset(new Mbc1Multi64(memptrs));
} else
mbc.reset(new Mbc1(memptrs));
break;
case MBC2: mbc.reset(new Mbc2(memptrs)); break;
case MBC3: mbc.reset(new Mbc3(memptrs, hasRtc(memptrs.romdata()[0x147]) ? &rtc : 0)); break;
case MBC5: mbc.reset(new Mbc5(memptrs)); break;
case HUC1: mbc.reset(new HuC1(memptrs)); break;
}
return 0;
}
static bool hasBattery(const unsigned char headerByte0x147) {
switch (headerByte0x147) {
case 0x03:
case 0x06:
case 0x09:
case 0x0F:
case 0x10:
case 0x13:
case 0x1B:
case 0x1E:
case 0xFF: return true;
default: return false;
}
}
void Cartridge::loadSavedata() {
const std::string &sbp = saveBasePath();
if (hasBattery(memptrs.romdata()[0x147])) {
std::ifstream file((sbp + ".sav").c_str(), std::ios::binary | std::ios::in);
if (file.is_open()) {
file.read(reinterpret_cast<char*>(memptrs.rambankdata()), memptrs.rambankdataend() - memptrs.rambankdata());
enforce8bit(memptrs.rambankdata(), memptrs.rambankdataend() - memptrs.rambankdata());
}
}
if (hasRtc(memptrs.romdata()[0x147])) {
std::ifstream file((sbp + ".rtc").c_str(), std::ios::binary | std::ios::in);
if (file.is_open()) {
unsigned long basetime = file.get() & 0xFF;
basetime = basetime << 8 | (file.get() & 0xFF);
basetime = basetime << 8 | (file.get() & 0xFF);
basetime = basetime << 8 | (file.get() & 0xFF);
rtc.setBaseTime(basetime);
}
}
}
void Cartridge::saveSavedata() {
const std::string &sbp = saveBasePath();
if (hasBattery(memptrs.romdata()[0x147])) {
std::ofstream file((sbp + ".sav").c_str(), std::ios::binary | std::ios::out);
file.write(reinterpret_cast<const char*>(memptrs.rambankdata()), memptrs.rambankdataend() - memptrs.rambankdata());
}
if (hasRtc(memptrs.romdata()[0x147])) {
std::ofstream file((sbp + ".rtc").c_str(), std::ios::binary | std::ios::out);
const unsigned long basetime = rtc.getBaseTime();
file.put(basetime >> 24 & 0xFF);
file.put(basetime >> 16 & 0xFF);
file.put(basetime >> 8 & 0xFF);
file.put(basetime & 0xFF);
}
}
static int asHex(const char c) {
return c >= 'A' ? c - 'A' + 0xA : c - '0';
}
void Cartridge::applyGameGenie(const std::string &code) {
if (6 < code.length()) {
const unsigned val = (asHex(code[0]) << 4 | asHex(code[1])) & 0xFF;
const unsigned addr = (asHex(code[2]) << 8 | asHex(code[4]) << 4 | asHex(code[5]) | (asHex(code[6]) ^ 0xF) << 12) & 0x7FFF;
unsigned cmp = 0xFFFF;
if (10 < code.length()) {
cmp = (asHex(code[8]) << 4 | asHex(code[10])) ^ 0xFF;
cmp = ((cmp >> 2 | cmp << 6) ^ 0x45) & 0xFF;
}
for (unsigned bank = 0; bank < static_cast<std::size_t>(memptrs.romdataend() - memptrs.romdata()) / 0x4000; ++bank) {
if (mbc->isAddressWithinAreaRombankCanBeMappedTo(addr, bank)
&& (cmp > 0xFF || memptrs.romdata()[bank * 0x4000ul + (addr & 0x3FFF)] == cmp)) {
ggUndoList.push_back(AddrData(bank * 0x4000ul + (addr & 0x3FFF), memptrs.romdata()[bank * 0x4000ul + (addr & 0x3FFF)]));
memptrs.romdata()[bank * 0x4000ul + (addr & 0x3FFF)] = val;
}
}
}
}
void Cartridge::setGameGenie(const std::string &codes) {
if (loaded()) {
for (std::vector<AddrData>::reverse_iterator it = ggUndoList.rbegin(), end = ggUndoList.rend(); it != end; ++it) {
if (memptrs.romdata() + it->addr < memptrs.romdataend())
memptrs.romdata()[it->addr] = it->data;
}
ggUndoList.clear();
std::string code;
for (std::size_t pos = 0; pos < codes.length()
&& (code = codes.substr(pos, codes.find(';', pos) - pos), true); pos += code.length() + 1) {
applyGameGenie(code);
}
}
}
}

View File

@ -0,0 +1,96 @@
/***************************************************************************
* Copyright (C) 2007-2010 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef CARTRIDGE_H
#define CARTRIDGE_H
#include "memptrs.h"
#include "rtc.h"
#include "savestate.h"
#include <memory>
#include <string>
#include <vector>
namespace gambatte {
class Mbc {
public:
virtual ~Mbc() {}
virtual void romWrite(unsigned P, unsigned data) = 0;
virtual void saveState(SaveState::Mem &ss) const = 0;
virtual void loadState(const SaveState::Mem &ss) = 0;
virtual bool isAddressWithinAreaRombankCanBeMappedTo(unsigned address, unsigned rombank) const = 0;
};
class Cartridge {
struct AddrData {
unsigned long addr;
unsigned char data;
AddrData(unsigned long addr, unsigned data) : addr(addr), data(data) {}
};
MemPtrs memptrs;
Rtc rtc;
std::auto_ptr<Mbc> mbc;
std::string defaultSaveBasePath;
std::string saveDir;
std::vector<AddrData> ggUndoList;
void applyGameGenie(const std::string &code);
public:
void setStatePtrs(SaveState &);
void saveState(SaveState &) const;
void loadState(const SaveState &);
bool loaded() const { return mbc.get(); }
const unsigned char * rmem(unsigned area) const { return memptrs.rmem(area); }
unsigned char * wmem(unsigned area) const { return memptrs.wmem(area); }
unsigned char * vramdata() const { return memptrs.vramdata(); }
unsigned char * romdata(unsigned area) const { return memptrs.romdata(area); }
unsigned char * wramdata(unsigned area) const { return memptrs.wramdata(area); }
const unsigned char * rdisabledRam() const { return memptrs.rdisabledRam(); }
const unsigned char * rsrambankptr() const { return memptrs.rsrambankptr(); }
unsigned char * wsrambankptr() const { return memptrs.wsrambankptr(); }
unsigned char * vrambankptr() const { return memptrs.vrambankptr(); }
OamDmaSrc oamDmaSrc() const { return memptrs.oamDmaSrc(); }
void setVrambank(unsigned bank) { memptrs.setVrambank(bank); }
void setWrambank(unsigned bank) { memptrs.setWrambank(bank); }
void setOamDmaSrc(OamDmaSrc oamDmaSrc) { memptrs.setOamDmaSrc(oamDmaSrc); }
void mbcWrite(unsigned addr, unsigned data) { mbc->romWrite(addr, data); }
bool isCgb() const { return gambatte::isCgb(memptrs); }
void rtcWrite(unsigned data) { rtc.write(data); }
unsigned char rtcRead() const { return *rtc.getActive(); }
void loadSavedata();
void saveSavedata();
const std::string saveBasePath() const;
void setSaveDir(const std::string &dir);
int loadROM(const std::string &romfile, bool forceDmg, bool multicartCompat);
const char * romTitle() const { return reinterpret_cast<const char *>(memptrs.romdata() + 0x134); }
void setGameGenie(const std::string &codes);
};
}
#endif

View File

@ -0,0 +1,139 @@
/***************************************************************************
* Copyright (C) 2007-2010 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "memptrs.h"
#include <algorithm>
#include <cstring>
namespace gambatte {
MemPtrs::MemPtrs()
: rmem_(), wmem_(), romdata_(), wramdata_(), vrambankptr_(0), rsrambankptr_(0),
wsrambankptr_(0), memchunk_(0), rambankdata_(0), wramdataend_(0), oamDmaSrc_(OAM_DMA_SRC_OFF)
{
}
MemPtrs::~MemPtrs() {
delete []memchunk_;
}
void MemPtrs::reset(const unsigned rombanks, const unsigned rambanks, const unsigned wrambanks) {
delete []memchunk_;
memchunk_ = new unsigned char[0x4000 + rombanks * 0x4000ul + 0x4000 + rambanks * 0x2000ul + wrambanks * 0x1000ul + 0x4000];
romdata_[0] = romdata();
rambankdata_ = romdata_[0] + rombanks * 0x4000ul + 0x4000;
wramdata_[0] = rambankdata_ + rambanks * 0x2000ul;
wramdataend_ = wramdata_[0] + wrambanks * 0x1000ul;
std::memset(rdisabledRamw(), 0xFF, 0x2000);
oamDmaSrc_ = OAM_DMA_SRC_OFF;
rmem_[0x3] = rmem_[0x2] = rmem_[0x1] = rmem_[0x0] = romdata_[0];
rmem_[0xC] = wmem_[0xC] = wramdata_[0] - 0xC000;
rmem_[0xE] = wmem_[0xE] = wramdata_[0] - 0xE000;
setRombank(1);
setRambank(0, 0);
setVrambank(0);
setWrambank(1);
}
void MemPtrs::setRombank0(const unsigned bank) {
romdata_[0] = romdata() + bank * 0x4000ul;
rmem_[0x3] = rmem_[0x2] = rmem_[0x1] = rmem_[0x0] = romdata_[0];
disconnectOamDmaAreas();
}
void MemPtrs::setRombank(const unsigned bank) {
romdata_[1] = romdata() + bank * 0x4000ul - 0x4000;
rmem_[0x7] = rmem_[0x6] = rmem_[0x5] = rmem_[0x4] = romdata_[1];
disconnectOamDmaAreas();
}
void MemPtrs::setRambank(const unsigned flags, const unsigned rambank) {
unsigned char *const srambankptr = flags & RTC_EN
? 0
: (rambankdata() != rambankdataend()
? rambankdata_ + rambank * 0x2000ul - 0xA000 : wdisabledRam() - 0xA000);
rsrambankptr_ = (flags & READ_EN) && srambankptr != wdisabledRam() - 0xA000 ? srambankptr : rdisabledRamw() - 0xA000;
wsrambankptr_ = flags & WRITE_EN ? srambankptr : wdisabledRam() - 0xA000;
rmem_[0xB] = rmem_[0xA] = rsrambankptr_;
wmem_[0xB] = wmem_[0xA] = wsrambankptr_;
disconnectOamDmaAreas();
}
void MemPtrs::setWrambank(const unsigned bank) {
wramdata_[1] = wramdata_[0] + ((bank & 0x07) ? (bank & 0x07) : 1) * 0x1000;
rmem_[0xD] = wmem_[0xD] = wramdata_[1] - 0xD000;
disconnectOamDmaAreas();
}
void MemPtrs::setOamDmaSrc(const OamDmaSrc oamDmaSrc) {
rmem_[0x3] = rmem_[0x2] = rmem_[0x1] = rmem_[0x0] = romdata_[0];
rmem_[0x7] = rmem_[0x6] = rmem_[0x5] = rmem_[0x4] = romdata_[1];
rmem_[0xB] = rmem_[0xA] = rsrambankptr_;
wmem_[0xB] = wmem_[0xA] = wsrambankptr_;
rmem_[0xC] = wmem_[0xC] = wramdata_[0] - 0xC000;
rmem_[0xD] = wmem_[0xD] = wramdata_[1] - 0xD000;
rmem_[0xE] = wmem_[0xE] = wramdata_[0] - 0xE000;
oamDmaSrc_ = oamDmaSrc;
disconnectOamDmaAreas();
}
void MemPtrs::disconnectOamDmaAreas() {
if (isCgb(*this)) {
switch (oamDmaSrc_) {
case OAM_DMA_SRC_ROM: // fall through
case OAM_DMA_SRC_SRAM:
case OAM_DMA_SRC_INVALID:
std::fill(rmem_, rmem_ + 8, static_cast<unsigned char *>(0));
rmem_[0xB] = rmem_[0xA] = 0;
wmem_[0xB] = wmem_[0xA] = 0;
break;
case OAM_DMA_SRC_VRAM:
break;
case OAM_DMA_SRC_WRAM:
rmem_[0xE] = rmem_[0xD] = rmem_[0xC] = 0;
wmem_[0xE] = wmem_[0xD] = wmem_[0xC] = 0;
break;
case OAM_DMA_SRC_OFF:
break;
}
} else {
switch (oamDmaSrc_) {
case OAM_DMA_SRC_ROM: // fall through
case OAM_DMA_SRC_SRAM:
case OAM_DMA_SRC_WRAM:
case OAM_DMA_SRC_INVALID:
std::fill(rmem_, rmem_ + 8, static_cast<unsigned char *>(0));
rmem_[0xB] = rmem_[0xA] = 0;
wmem_[0xB] = wmem_[0xA] = 0;
rmem_[0xE] = rmem_[0xD] = rmem_[0xC] = 0;
wmem_[0xE] = wmem_[0xD] = wmem_[0xC] = 0;
break;
case OAM_DMA_SRC_VRAM:
break;
case OAM_DMA_SRC_OFF:
break;
}
}
}
}

View File

@ -0,0 +1,85 @@
/***************************************************************************
* Copyright (C) 2007-2010 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef MEMPTRS_H
#define MEMPTRS_H
namespace gambatte {
enum OamDmaSrc { OAM_DMA_SRC_ROM, OAM_DMA_SRC_SRAM, OAM_DMA_SRC_VRAM,
OAM_DMA_SRC_WRAM, OAM_DMA_SRC_INVALID, OAM_DMA_SRC_OFF };
class MemPtrs {
const unsigned char *rmem_[0x10];
unsigned char *wmem_[0x10];
unsigned char *romdata_[2];
unsigned char *wramdata_[2];
unsigned char *vrambankptr_;
unsigned char *rsrambankptr_;
unsigned char *wsrambankptr_;
unsigned char *memchunk_;
unsigned char *rambankdata_;
unsigned char *wramdataend_;
OamDmaSrc oamDmaSrc_;
MemPtrs(const MemPtrs &);
MemPtrs & operator=(const MemPtrs &);
void disconnectOamDmaAreas();
unsigned char * rdisabledRamw() const { return wramdataend_ ; }
unsigned char * wdisabledRam() const { return wramdataend_ + 0x2000; }
public:
enum RamFlag { READ_EN = 1, WRITE_EN = 2, RTC_EN = 4 };
MemPtrs();
~MemPtrs();
void reset(unsigned rombanks, unsigned rambanks, unsigned wrambanks);
const unsigned char * rmem(unsigned area) const { return rmem_[area]; }
unsigned char * wmem(unsigned area) const { return wmem_[area]; }
unsigned char * vramdata() const { return rambankdata_ - 0x4000; }
unsigned char * vramdataend() const { return rambankdata_; }
unsigned char * romdata() const { return memchunk_ + 0x4000; }
unsigned char * romdata(unsigned area) const { return romdata_[area]; }
unsigned char * romdataend() const { return rambankdata_ - 0x4000; }
unsigned char * wramdata(unsigned area) const { return wramdata_[area]; }
unsigned char * wramdataend() const { return wramdataend_; }
unsigned char * rambankdata() const { return rambankdata_; }
unsigned char * rambankdataend() const { return wramdata_[0]; }
const unsigned char * rdisabledRam() const { return rdisabledRamw(); }
const unsigned char * rsrambankptr() const { return rsrambankptr_; }
unsigned char * wsrambankptr() const { return wsrambankptr_; }
unsigned char * vrambankptr() const { return vrambankptr_; }
OamDmaSrc oamDmaSrc() const { return oamDmaSrc_; }
void setRombank0(unsigned bank);
void setRombank(unsigned bank);
void setRambank(unsigned ramFlags, unsigned rambank);
void setVrambank(unsigned bank) { vrambankptr_ = vramdata() + bank * 0x2000ul - 0x8000; }
void setWrambank(unsigned bank);
void setOamDmaSrc(OamDmaSrc oamDmaSrc);
};
inline bool isCgb(const MemPtrs &memptrs) {
return memptrs.wramdataend() - memptrs.wramdata(0) == 0x8000;
}
}
#endif

156
libgambatte/src/mem/rtc.cpp Normal file
View File

@ -0,0 +1,156 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "rtc.h"
#include "../savestate.h"
namespace gambatte {
Rtc::Rtc()
: activeData(NULL),
activeSet(NULL),
baseTime(0),
haltTime(0),
index(5),
dataDh(0),
dataDl(0),
dataH(0),
dataM(0),
dataS(0),
enabled(false),
lastLatchData(false)
{
}
void Rtc::doLatch() {
std::time_t tmp = ((dataDh & 0x40) ? haltTime : std::time(0)) - baseTime;
while (tmp > 0x1FF * 86400) {
baseTime += 0x1FF * 86400;
tmp -= 0x1FF * 86400;
dataDh |= 0x80;
}
dataDl = (tmp / 86400) & 0xFF;
dataDh &= 0xFE;
dataDh |= ((tmp / 86400) & 0x100) >> 8;
tmp %= 86400;
dataH = tmp / 3600;
tmp %= 3600;
dataM = tmp / 60;
tmp %= 60;
dataS = tmp;
}
void Rtc::doSwapActive() {
if (!enabled || index > 4) {
activeData = NULL;
activeSet = NULL;
} else switch (index) {
case 0x00:
activeData = &dataS;
activeSet = &Rtc::setS;
break;
case 0x01:
activeData = &dataM;
activeSet = &Rtc::setM;
break;
case 0x02:
activeData = &dataH;
activeSet = &Rtc::setH;
break;
case 0x03:
activeData = &dataDl;
activeSet = &Rtc::setDl;
break;
case 0x04:
activeData = &dataDh;
activeSet = &Rtc::setDh;
break;
}
}
void Rtc::saveState(SaveState &state) const {
state.rtc.baseTime = baseTime;
state.rtc.haltTime = haltTime;
state.rtc.dataDh = dataDh;
state.rtc.dataDl = dataDl;
state.rtc.dataH = dataH;
state.rtc.dataM = dataM;
state.rtc.dataS = dataS;
state.rtc.lastLatchData = lastLatchData;
}
void Rtc::loadState(const SaveState &state) {
baseTime = state.rtc.baseTime;
haltTime = state.rtc.haltTime;
dataDh = state.rtc.dataDh;
dataDl = state.rtc.dataDl;
dataH = state.rtc.dataH;
dataM = state.rtc.dataM;
dataS = state.rtc.dataS;
lastLatchData = state.rtc.lastLatchData;
doSwapActive();
}
void Rtc::setDh(const unsigned new_dh) {
const std::time_t unixtime = (dataDh & 0x40) ? haltTime : std::time(0);
const std::time_t old_highdays = ((unixtime - baseTime) / 86400) & 0x100;
baseTime += old_highdays * 86400;
baseTime -= ((new_dh & 0x1) << 8) * 86400;
if ((dataDh ^ new_dh) & 0x40) {
if (new_dh & 0x40)
haltTime = std::time(0);
else
baseTime += std::time(0) - haltTime;
}
}
void Rtc::setDl(const unsigned new_lowdays) {
const std::time_t unixtime = (dataDh & 0x40) ? haltTime : std::time(0);
const std::time_t old_lowdays = ((unixtime - baseTime) / 86400) & 0xFF;
baseTime += old_lowdays * 86400;
baseTime -= new_lowdays * 86400;
}
void Rtc::setH(const unsigned new_hours) {
const std::time_t unixtime = (dataDh & 0x40) ? haltTime : std::time(0);
const std::time_t old_hours = ((unixtime - baseTime) / 3600) % 24;
baseTime += old_hours * 3600;
baseTime -= new_hours * 3600;
}
void Rtc::setM(const unsigned new_minutes) {
const std::time_t unixtime = (dataDh & 0x40) ? haltTime : std::time(0);
const std::time_t old_minutes = ((unixtime - baseTime) / 60) % 60;
baseTime += old_minutes * 60;
baseTime -= new_minutes * 60;
}
void Rtc::setS(const unsigned new_seconds) {
const std::time_t unixtime = (dataDh & 0x40) ? haltTime : std::time(0);
baseTime += (unixtime - baseTime) % 60;
baseTime -= new_seconds;
}
}

91
libgambatte/src/mem/rtc.h Normal file
View File

@ -0,0 +1,91 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef RTC_H
#define RTC_H
#include <ctime>
namespace gambatte {
struct SaveState;
class Rtc {
private:
unsigned char *activeData;
void (Rtc::*activeSet)(unsigned);
std::time_t baseTime;
std::time_t haltTime;
unsigned char index;
unsigned char dataDh;
unsigned char dataDl;
unsigned char dataH;
unsigned char dataM;
unsigned char dataS;
bool enabled;
bool lastLatchData;
void doLatch();
void doSwapActive();
void setDh(unsigned new_dh);
void setDl(unsigned new_lowdays);
void setH(unsigned new_hours);
void setM(unsigned new_minutes);
void setS(unsigned new_seconds);
public:
Rtc();
const unsigned char* getActive() const { return activeData; }
std::time_t getBaseTime() const { return baseTime; }
void setBaseTime(const std::time_t baseTime) {
this->baseTime = baseTime;
// doLatch();
}
void latch(const unsigned data) {
if (!lastLatchData && data == 1)
doLatch();
lastLatchData = data;
}
void saveState(SaveState &state) const;
void loadState(const SaveState &state);
void set(const bool enabled, unsigned bank) {
bank &= 0xF;
bank -= 8;
this->enabled = enabled;
this->index = bank;
doSwapActive();
}
void write(const unsigned data) {
// if (activeSet)
(this->*activeSet)(data);
*activeData = data;
}
};
}
#endif

1006
libgambatte/src/memory.cpp Normal file

File diff suppressed because it is too large Load Diff

154
libgambatte/src/memory.h Normal file
View File

@ -0,0 +1,154 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef MEMORY_H
#define MEMORY_H
#include "mem/cartridge.h"
#include "video.h"
#include "sound.h"
#include "interrupter.h"
#include "tima.h"
namespace gambatte {
class InputGetter;
class FilterInfo;
class Memory {
Cartridge cart;
unsigned char ioamhram[0x200];
InputGetter *getInput;
unsigned long divLastUpdate;
unsigned long lastOamDmaUpdate;
InterruptRequester intreq;
Tima tima;
LCD display;
PSG sound;
Interrupter interrupter;
unsigned short dmaSource;
unsigned short dmaDestination;
unsigned char oamDmaPos;
unsigned char serialCnt;
bool blanklcd;
void updateInput();
void decEventCycles(MemEventId eventId, unsigned long dec);
void oamDmaInitSetup();
void updateOamDma(unsigned long cycleCounter);
void startOamDma(unsigned long cycleCounter);
void endOamDma(unsigned long cycleCounter);
const unsigned char * oamDmaSrcPtr() const;
unsigned nontrivial_ff_read(unsigned P, unsigned long cycleCounter);
unsigned nontrivial_read(unsigned P, unsigned long cycleCounter);
void nontrivial_ff_write(unsigned P, unsigned data, unsigned long cycleCounter);
void nontrivial_write(unsigned P, unsigned data, unsigned long cycleCounter);
void updateSerial(unsigned long cc);
void updateTimaIrq(unsigned long cc);
void updateIrqs(unsigned long cc);
bool isDoubleSpeed() const { return display.isDoubleSpeed(); }
public:
explicit Memory(const Interrupter &interrupter);
bool loaded() const { return cart.loaded(); }
const char * romTitle() const { return cart.romTitle(); }
void setStatePtrs(SaveState &state);
unsigned long saveState(SaveState &state, unsigned long cc);
void loadState(const SaveState &state/*, unsigned long oldCc*/);
void loadSavedata() { cart.loadSavedata(); }
void saveSavedata() { cart.saveSavedata(); }
const std::string saveBasePath() const { return cart.saveBasePath(); }
void setOsdElement(std::auto_ptr<OsdElement> osdElement) {
display.setOsdElement(osdElement);
}
unsigned long stop(unsigned long cycleCounter);
bool isCgb() const { return display.isCgb(); }
bool ime() const { return intreq.ime(); }
bool halted() const { return intreq.halted(); }
unsigned long nextEventTime() const { return intreq.minEventTime(); }
bool isActive() const { return intreq.eventTime(END) != DISABLED_TIME; }
long cyclesSinceBlit(const unsigned long cc) const {
return cc < intreq.eventTime(BLIT) ? -1 : static_cast<long>((cc - intreq.eventTime(BLIT)) >> isDoubleSpeed());
}
void halt() { intreq.halt(); }
void ei(unsigned long cycleCounter) { if (!ime()) { intreq.ei(cycleCounter); } }
void di() { intreq.di(); }
unsigned ff_read(const unsigned P, const unsigned long cycleCounter) {
return P < 0xFF80 ? nontrivial_ff_read(P, cycleCounter) : ioamhram[P - 0xFE00];
}
unsigned read(const unsigned P, const unsigned long cycleCounter) {
return cart.rmem(P >> 12) ? cart.rmem(P >> 12)[P] : nontrivial_read(P, cycleCounter);
}
void write(const unsigned P, const unsigned data, const unsigned long cycleCounter) {
if (cart.wmem(P >> 12)) {
cart.wmem(P >> 12)[P] = data;
} else
nontrivial_write(P, data, cycleCounter);
}
void ff_write(const unsigned P, const unsigned data, const unsigned long cycleCounter) {
if (P - 0xFF80u < 0x7Fu) {
ioamhram[P - 0xFE00] = data;
} else
nontrivial_ff_write(P, data, cycleCounter);
}
unsigned long event(unsigned long cycleCounter);
unsigned long resetCounters(unsigned long cycleCounter);
int loadROM(const std::string &romfile, bool forceDmg, bool multicartCompat);
void setSaveDir(const std::string &dir) { cart.setSaveDir(dir); }
void setInputGetter(InputGetter *getInput) {
this->getInput = getInput;
}
void setEndtime(unsigned long cc, unsigned long inc);
void setSoundBuffer(uint_least32_t *const buf) { sound.setBuffer(buf); }
unsigned fillSoundBuffer(unsigned long cc);
void setVideoBuffer(uint_least32_t *const videoBuf, const int pitch) {
display.setVideoBuffer(videoBuf, pitch);
}
void setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned long rgb32);
void setGameGenie(const std::string &codes) { cart.setGameGenie(codes); }
void setGameShark(const std::string &codes) { interrupter.setGameShark(codes); }
};
}
#endif

147
libgambatte/src/minkeeper.h Normal file
View File

@ -0,0 +1,147 @@
/***************************************************************************
* Copyright (C) 2009 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef MINKEEPER_H
#define MINKEEPER_H
#include <algorithm>
namespace MinKeeperUtil {
template<int n> struct CeiledLog2 { enum { R = 1 + CeiledLog2<(n + 1) / 2>::R }; };
template<> struct CeiledLog2<1> { enum { R = 0 }; };
template<int v, int n> struct RoundedDiv2n { enum { R = RoundedDiv2n<(v + 1) / 2, n - 1>::R }; };
template<int v> struct RoundedDiv2n<v,1> { enum { R = v }; };
template<template<int> class T, int n> struct Sum { enum { R = T<n-1>::R + Sum<T, n-1>::R }; };
template<template<int> class T> struct Sum<T,0> { enum { R = 0 }; };
}
// Keeps track of minimum value identified by id as values change.
// Higher ids prioritized (as min value) if values are equal. Can easily be reversed by swapping < for <=.
// Higher ids can be faster to change when the number of ids isn't a power of 2.
// Thus the ones that change more frequently should have higher ids if priority allows it.
template<int ids>
class MinKeeper {
enum { LEVELS = MinKeeperUtil::CeiledLog2<ids>::R };
template<int l> struct Num { enum { R = MinKeeperUtil::RoundedDiv2n<ids, LEVELS + 1 - l>::R }; };
template<int l> struct Sum { enum { R = MinKeeperUtil::Sum<Num, l>::R }; };
template<int id, int level>
struct UpdateValue {
enum { P = Sum<level-1>::R + id };
enum { C0 = Sum<level>::R + id * 2 };
static void updateValue(MinKeeper<ids> &m) {
// GCC 4.3 generates better code with the ternary operator on i386.
m.a[P] = (id * 2 + 1 == Num<level>::R || m.values[m.a[C0]] < m.values[m.a[C0 + 1]]) ? m.a[C0] : m.a[C0 + 1];
UpdateValue<id / 2, level - 1>::updateValue(m);
}
};
template<int id>
struct UpdateValue<id,0> {
static void updateValue(MinKeeper<ids> &m) {
m.minValue_ = m.values[m.a[0]];
}
};
class UpdateValueLut {
template<int id, int dummy> struct FillLut {
static void fillLut(UpdateValueLut & l) {
l.lut_[id] = updateValue<id>;
FillLut<id-1,dummy>::fillLut(l);
}
};
template<int dummy> struct FillLut<-1,dummy> {
static void fillLut(UpdateValueLut &) {}
};
void (*lut_[Num<LEVELS-1>::R])(MinKeeper<ids>&);
public:
UpdateValueLut() { FillLut<Num<LEVELS-1>::R-1,0>::fillLut(*this); }
void call(int id, MinKeeper<ids> &mk) const { lut_[id](mk); }
};
static UpdateValueLut updateValueLut;
unsigned long values[ids];
unsigned long minValue_;
int a[Sum<LEVELS>::R];
template<int id> static void updateValue(MinKeeper<ids> &m);
public:
explicit MinKeeper(unsigned long initValue = 0xFFFFFFFF);
int min() const { return a[0]; }
unsigned long minValue() const { return minValue_; }
template<int id>
void setValue(const unsigned long cnt) {
values[id] = cnt;
updateValue<id / 2>(*this);
}
void setValue(const int id, const unsigned long cnt) {
values[id] = cnt;
updateValueLut.call(id >> 1, *this);
}
unsigned long value(const int id) const { return values[id]; }
};
template<int ids> typename MinKeeper<ids>::UpdateValueLut MinKeeper<ids>::updateValueLut;
template<int ids>
MinKeeper<ids>::MinKeeper(const unsigned long initValue) {
std::fill(values, values + ids, initValue);
for (int i = 0; i < Num<LEVELS-1>::R; ++i) {
a[Sum<LEVELS-1>::R + i] = (i * 2 + 1 == ids || values[i * 2] < values[i * 2 + 1]) ? i * 2 : i * 2 + 1;
}
int n = Num<LEVELS-1>::R;
int off = Sum<LEVELS-1>::R;
while (off) {
const int pn = (n + 1) >> 1;
const int poff = off - pn;
for (int i = 0; i < pn; ++i) {
a[poff + i] = (i * 2 + 1 == n ||
values[a[off + i * 2]] < values[a[off + i * 2 + 1]]) ?
a[off + i * 2] : a[off + i * 2 + 1];
}
off = poff;
n = pn;
}
minValue_ = values[a[0]];
}
template<int ids>
template<int id>
void MinKeeper<ids>::updateValue(MinKeeper<ids> &m) {
m.a[Sum<LEVELS-1>::R + id] = (id * 2 + 1 == ids || m.values[id * 2] < m.values[id * 2 + 1]) ? id * 2 : id * 2 + 1;
UpdateValue<id / 2, LEVELS-1>::updateValue(m);
}
#endif

View File

@ -0,0 +1,68 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef OSD_ELEMENT_H
#define OSD_ELEMENT_H
#include "gbint.h"
namespace gambatte {
class OsdElement {
public:
enum Opacity { SEVEN_EIGHTHS, THREE_FOURTHS };
private:
Opacity opacity_;
unsigned x_;
unsigned y_;
unsigned w_;
unsigned h_;
protected:
explicit OsdElement(unsigned x = 0, unsigned y = 0, unsigned w = 0, unsigned h = 0, Opacity opacity = SEVEN_EIGHTHS)
: opacity_(opacity), x_(x), y_(y), w_(w), h_(h)
{
}
void setPos(unsigned x, unsigned y) {
x_ = x;
y_ = y;
}
void setSize(unsigned w, unsigned h) {
w_ = w;
h_ = h;
}
void setOpacity(Opacity opacity) { opacity_ = opacity; }
public:
virtual ~OsdElement() {}
unsigned x() const { return x_; }
unsigned y() const { return y_; }
unsigned w() const { return w_; }
unsigned h() const { return h_; }
Opacity opacity() const { return opacity_; }
virtual const uint_least32_t* update() = 0;
};
}
#endif

195
libgambatte/src/savestate.h Normal file
View File

@ -0,0 +1,195 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef SAVESTATE_H
#define SAVESTATE_H
namespace gambatte {
class SaverList;
struct SaveState {
template<typename T>
class Ptr {
T *ptr;
unsigned long sz;
public:
Ptr() : ptr(0), sz(0) {}
const T* get() const { return ptr; }
unsigned long getSz() const { return sz; }
void set(T *ptr, const unsigned long sz) { this->ptr = ptr; this->sz = sz; }
friend class SaverList;
friend void setInitState(SaveState &, bool, bool);
};
struct CPU {
unsigned long cycleCounter;
unsigned short PC;
unsigned short SP;
unsigned char A;
unsigned char B;
unsigned char C;
unsigned char D;
unsigned char E;
unsigned char F;
unsigned char H;
unsigned char L;
bool skip;
} cpu;
struct Mem {
Ptr<unsigned char> vram;
Ptr<unsigned char> sram;
Ptr<unsigned char> wram;
Ptr<unsigned char> ioamhram;
unsigned long divLastUpdate;
unsigned long timaLastUpdate;
unsigned long tmatime;
unsigned long nextSerialtime;
unsigned long lastOamDmaUpdate;
unsigned long minIntTime;
unsigned long unhaltTime;
unsigned short rombank;
unsigned short dmaSource;
unsigned short dmaDestination;
unsigned char rambank;
unsigned char oamDmaPos;
bool IME;
bool halted;
bool enableRam;
bool rambankMode;
bool hdmaTransfer;
} mem;
struct PPU {
Ptr<unsigned char> bgpData;
Ptr<unsigned char> objpData;
//SpriteMapper::OamReader
Ptr<unsigned char> oamReaderBuf;
Ptr<bool> oamReaderSzbuf;
unsigned long videoCycles;
unsigned long enableDisplayM0Time;
unsigned short lastM0Time;
unsigned short nextM0Irq;
unsigned short tileword;
unsigned short ntileword;
unsigned char spAttribList[10];
unsigned char spByte0List[10];
unsigned char spByte1List[10];
unsigned char winYPos;
unsigned char xpos;
unsigned char endx;
unsigned char reg0;
unsigned char reg1;
unsigned char attrib;
unsigned char nattrib;
unsigned char state;
unsigned char nextSprite;
unsigned char currentSprite;
unsigned char lyc;
unsigned char m0lyc;
unsigned char oldWy;
unsigned char winDrawState;
unsigned char wscx;
bool weMaster;
bool pendingLcdstatIrq;
} ppu;
struct SPU {
struct Duty {
unsigned long nextPosUpdate;
unsigned char nr3;
unsigned char pos;
};
struct Env {
unsigned long counter;
unsigned char volume;
};
struct LCounter {
unsigned long counter;
unsigned short lengthCounter;
};
struct {
struct {
unsigned long counter;
unsigned short shadow;
unsigned char nr0;
bool negging;
} sweep;
Duty duty;
Env env;
LCounter lcounter;
unsigned char nr4;
bool master;
} ch1;
struct {
Duty duty;
Env env;
LCounter lcounter;
unsigned char nr4;
bool master;
} ch2;
struct {
Ptr<unsigned char> waveRam;
LCounter lcounter;
unsigned long waveCounter;
unsigned long lastReadTime;
unsigned char nr3;
unsigned char nr4;
unsigned char wavePos;
unsigned char sampleBuf;
bool master;
} ch3;
struct {
struct {
unsigned long counter;
unsigned short reg;
} lfsr;
Env env;
LCounter lcounter;
unsigned char nr4;
bool master;
} ch4;
unsigned long cycleCounter;
} spu;
struct RTC {
unsigned long baseTime;
unsigned long haltTime;
unsigned char dataDh;
unsigned char dataDl;
unsigned char dataH;
unsigned char dataM;
unsigned char dataS;
bool lastLatchData;
} rtc;
};
}
#endif

182
libgambatte/src/sound.cpp Normal file
View File

@ -0,0 +1,182 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "sound.h"
#include "savestate.h"
#include <cstring>
#include <algorithm>
/*
Frame Sequencer
Step Length Ctr Vol Env Sweep
- - - - - - - - - - - - - - - - - - - -
0 Clock - Clock
S 1 - Clock -
2 Clock - -
3 - - -
4 Clock - Clock
5 - - -
6 Clock - -
7 - - -
- - - - - - - - - - - - - - - - - - - -
Rate 256 Hz 64 Hz 128 Hz
S) start step on sound power on.
*/
namespace gambatte {
PSG::PSG()
: buffer(0),
lastUpdate(0),
soVol(0),
rsum(0x8000), // initialize to 0x8000 to prevent borrows from high word, xor away later
bufferPos(0),
enabled(false)
{
}
void PSG::init(const bool cgb) {
ch1.init(cgb);
ch2.init(cgb);
ch3.init(cgb);
ch4.init(cgb);
}
void PSG::reset() {
ch1.reset();
ch2.reset();
ch3.reset();
ch4.reset();
}
void PSG::setStatePtrs(SaveState &state) {
ch3.setStatePtrs(state);
}
void PSG::saveState(SaveState &state) {
ch1.saveState(state);
ch2.saveState(state);
ch3.saveState(state);
ch4.saveState(state);
}
void PSG::loadState(const SaveState &state) {
ch1.loadState(state);
ch2.loadState(state);
ch3.loadState(state);
ch4.loadState(state);
lastUpdate = state.cpu.cycleCounter;
set_so_volume(state.mem.ioamhram.get()[0x124]);
map_so(state.mem.ioamhram.get()[0x125]);
enabled = state.mem.ioamhram.get()[0x126] >> 7 & 1;
}
void PSG::accumulate_channels(const unsigned long cycles) {
uint_least32_t *const buf = buffer + bufferPos;
std::memset(buf, 0, cycles * sizeof(uint_least32_t));
ch1.update(buf, soVol, cycles);
ch2.update(buf, soVol, cycles);
ch3.update(buf, soVol, cycles);
ch4.update(buf, soVol, cycles);
}
void PSG::generate_samples(const unsigned long cycleCounter, const unsigned doubleSpeed) {
const unsigned long cycles = (cycleCounter - lastUpdate) >> (1 + doubleSpeed);
lastUpdate += cycles << (1 + doubleSpeed);
if (cycles)
accumulate_channels(cycles);
bufferPos += cycles;
}
void PSG::resetCounter(const unsigned long newCc, const unsigned long oldCc, const unsigned doubleSpeed) {
generate_samples(oldCc, doubleSpeed);
lastUpdate = newCc - (oldCc - lastUpdate);
}
unsigned PSG::fillBuffer() {
uint_least32_t sum = rsum;
uint_least32_t *b = buffer;
unsigned n = bufferPos;
if (unsigned n2 = n >> 3) {
n -= n2 << 3;
do {
sum += b[0];
b[0] = sum ^ 0x8000;
sum += b[1];
b[1] = sum ^ 0x8000;
sum += b[2];
b[2] = sum ^ 0x8000;
sum += b[3];
b[3] = sum ^ 0x8000;
sum += b[4];
b[4] = sum ^ 0x8000;
sum += b[5];
b[5] = sum ^ 0x8000;
sum += b[6];
b[6] = sum ^ 0x8000;
sum += b[7];
b[7] = sum ^ 0x8000;
b += 8;
} while (--n2);
}
while (n--) {
sum += *b;
*b++ = sum ^ 0x8000; // xor away the initial rsum value of 0x8000 (which prevents borrows from the high word) from the low word
}
rsum = sum;
return bufferPos;
}
#ifdef WORDS_BIGENDIAN
static const unsigned long so1Mul = 0x00000001;
static const unsigned long so2Mul = 0x00010000;
#else
static const unsigned long so1Mul = 0x00010000;
static const unsigned long so2Mul = 0x00000001;
#endif
void PSG::set_so_volume(const unsigned nr50) {
soVol = (((nr50 & 0x7) + 1) * so1Mul + ((nr50 >> 4 & 0x7) + 1) * so2Mul) * 64;
}
void PSG::map_so(const unsigned nr51) {
const unsigned long tmp = nr51 * so1Mul + (nr51 >> 4) * so2Mul;
ch1.setSo((tmp & 0x00010001) * 0xFFFF);
ch2.setSo((tmp >> 1 & 0x00010001) * 0xFFFF);
ch3.setSo((tmp >> 2 & 0x00010001) * 0xFFFF);
ch4.setSo((tmp >> 3 & 0x00010001) * 0xFFFF);
}
unsigned PSG::getStatus() const {
return ch1.isActive() | ch2.isActive() << 1 | ch3.isActive() << 2 | ch4.isActive() << 3;
}
}

95
libgambatte/src/sound.h Normal file
View File

@ -0,0 +1,95 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef SOUND_H
#define SOUND_H
#include "sound/channel1.h"
#include "sound/channel2.h"
#include "sound/channel3.h"
#include "sound/channel4.h"
namespace gambatte {
class PSG {
Channel1 ch1;
Channel2 ch2;
Channel3 ch3;
Channel4 ch4;
uint_least32_t *buffer;
unsigned long lastUpdate;
unsigned long soVol;
uint_least32_t rsum;
unsigned bufferPos;
bool enabled;
void accumulate_channels(unsigned long cycles);
public:
PSG();
void init(bool cgb);
void reset();
void setStatePtrs(SaveState &state);
void saveState(SaveState &state);
void loadState(const SaveState &state);
void generate_samples(unsigned long cycleCounter, unsigned doubleSpeed);
void resetCounter(unsigned long newCc, unsigned long oldCc, unsigned doubleSpeed);
unsigned fillBuffer();
void setBuffer(uint_least32_t *const buf) { buffer = buf; bufferPos = 0; }
bool isEnabled() const { return enabled; }
void setEnabled(bool value) { enabled = value; }
void set_nr10(unsigned data) { ch1.setNr0(data); }
void set_nr11(unsigned data) { ch1.setNr1(data); }
void set_nr12(unsigned data) { ch1.setNr2(data); }
void set_nr13(unsigned data) { ch1.setNr3(data); }
void set_nr14(unsigned data) { ch1.setNr4(data); }
void set_nr21(unsigned data) { ch2.setNr1(data); }
void set_nr22(unsigned data) { ch2.setNr2(data); }
void set_nr23(unsigned data) { ch2.setNr3(data); }
void set_nr24(unsigned data) { ch2.setNr4(data); }
void set_nr30(unsigned data) { ch3.setNr0(data); }
void set_nr31(unsigned data) { ch3.setNr1(data); }
void set_nr32(unsigned data) { ch3.setNr2(data); }
void set_nr33(unsigned data) { ch3.setNr3(data); }
void set_nr34(unsigned data) { ch3.setNr4(data); }
unsigned waveRamRead(unsigned index) const { return ch3.waveRamRead(index); }
void waveRamWrite(unsigned index, unsigned data) { ch3.waveRamWrite(index, data); }
void set_nr41(unsigned data) { ch4.setNr1(data); }
void set_nr42(unsigned data) { ch4.setNr2(data); }
void set_nr43(unsigned data) { ch4.setNr3(data); }
void set_nr44(unsigned data) { ch4.setNr4(data); }
void set_so_volume(unsigned nr50);
void map_so(unsigned nr51);
unsigned getStatus() const;
};
}
#endif

View File

@ -0,0 +1,262 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "channel1.h"
#include "../savestate.h"
#include <algorithm>
namespace gambatte {
Channel1::SweepUnit::SweepUnit(MasterDisabler &disabler, DutyUnit &dutyUnit) :
disableMaster(disabler),
dutyUnit(dutyUnit),
shadow(0),
nr0(0),
negging(false)
{}
unsigned Channel1::SweepUnit::calcFreq() {
unsigned freq = shadow >> (nr0 & 0x07);
if (nr0 & 0x08) {
freq = shadow - freq;
negging = true;
} else
freq = shadow + freq;
if (freq & 2048)
disableMaster();
return freq;
}
void Channel1::SweepUnit::event() {
const unsigned long period = nr0 >> 4 & 0x07;
if (period) {
const unsigned freq = calcFreq();
if (!(freq & 2048) && (nr0 & 0x07)) {
shadow = freq;
dutyUnit.setFreq(freq, counter);
calcFreq();
}
counter += period << 14;
} else
counter += 8ul << 14;
}
void Channel1::SweepUnit::nr0Change(const unsigned newNr0) {
if (negging && !(newNr0 & 0x08))
disableMaster();
nr0 = newNr0;
}
void Channel1::SweepUnit::nr4Init(const unsigned long cc) {
negging = false;
shadow = dutyUnit.getFreq();
const unsigned period = nr0 >> 4 & 0x07;
const unsigned shift = nr0 & 0x07;
if (period | shift)
counter = ((cc >> 14) + (period ? period : 8)) << 14;
else
counter = COUNTER_DISABLED;
if (shift)
calcFreq();
}
void Channel1::SweepUnit::reset() {
counter = COUNTER_DISABLED;
}
void Channel1::SweepUnit::saveState(SaveState &state) const {
state.spu.ch1.sweep.counter = counter;
state.spu.ch1.sweep.shadow = shadow;
state.spu.ch1.sweep.nr0 = nr0;
state.spu.ch1.sweep.negging = negging;
}
void Channel1::SweepUnit::loadState(const SaveState &state) {
counter = std::max(state.spu.ch1.sweep.counter, state.spu.cycleCounter);
shadow = state.spu.ch1.sweep.shadow;
nr0 = state.spu.ch1.sweep.nr0;
negging = state.spu.ch1.sweep.negging;
}
Channel1::Channel1() :
staticOutputTest(*this, dutyUnit),
disableMaster(master, dutyUnit),
lengthCounter(disableMaster, 0x3F),
envelopeUnit(staticOutputTest),
sweepUnit(disableMaster, dutyUnit),
cycleCounter(0),
soMask(0),
prevOut(0),
nr4(0),
master(false)
{
setEvent();
}
void Channel1::setEvent() {
// nextEventUnit = &dutyUnit;
// if (sweepUnit.getCounter() < nextEventUnit->getCounter())
nextEventUnit = &sweepUnit;
if (envelopeUnit.getCounter() < nextEventUnit->getCounter())
nextEventUnit = &envelopeUnit;
if (lengthCounter.getCounter() < nextEventUnit->getCounter())
nextEventUnit = &lengthCounter;
}
void Channel1::setNr0(const unsigned data) {
sweepUnit.nr0Change(data);
setEvent();
}
void Channel1::setNr1(const unsigned data) {
lengthCounter.nr1Change(data, nr4, cycleCounter);
dutyUnit.nr1Change(data, cycleCounter);
setEvent();
}
void Channel1::setNr2(const unsigned data) {
if (envelopeUnit.nr2Change(data))
disableMaster();
else
staticOutputTest(cycleCounter);
setEvent();
}
void Channel1::setNr3(const unsigned data) {
dutyUnit.nr3Change(data, cycleCounter);
setEvent();
}
void Channel1::setNr4(const unsigned data) {
lengthCounter.nr4Change(nr4, data, cycleCounter);
nr4 = data;
dutyUnit.nr4Change(data, cycleCounter);
if (data & 0x80) { //init-bit
nr4 &= 0x7F;
master = !envelopeUnit.nr4Init(cycleCounter);
sweepUnit.nr4Init(cycleCounter);
staticOutputTest(cycleCounter);
}
setEvent();
}
void Channel1::setSo(const unsigned long soMask) {
this->soMask = soMask;
staticOutputTest(cycleCounter);
setEvent();
}
void Channel1::reset() {
cycleCounter = 0x1000 | (cycleCounter & 0xFFF); // cycleCounter >> 12 & 7 represents the frame sequencer position.
// lengthCounter.reset();
dutyUnit.reset();
envelopeUnit.reset();
sweepUnit.reset();
setEvent();
}
void Channel1::init(const bool cgb) {
lengthCounter.init(cgb);
}
void Channel1::saveState(SaveState &state) {
sweepUnit.saveState(state);
dutyUnit.saveState(state.spu.ch1.duty, cycleCounter);
envelopeUnit.saveState(state.spu.ch1.env);
lengthCounter.saveState(state.spu.ch1.lcounter);
state.spu.cycleCounter = cycleCounter;
state.spu.ch1.nr4 = nr4;
state.spu.ch1.master = master;
}
void Channel1::loadState(const SaveState &state) {
sweepUnit.loadState(state);
dutyUnit.loadState(state.spu.ch1.duty, state.mem.ioamhram.get()[0x111], state.spu.ch1.nr4, state.spu.cycleCounter);
envelopeUnit.loadState(state.spu.ch1.env, state.mem.ioamhram.get()[0x112], state.spu.cycleCounter);
lengthCounter.loadState(state.spu.ch1.lcounter, state.spu.cycleCounter);
cycleCounter = state.spu.cycleCounter;
nr4 = state.spu.ch1.nr4;
master = state.spu.ch1.master;
}
void Channel1::update(uint_least32_t *buf, const unsigned long soBaseVol, unsigned long cycles) {
const unsigned long outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0;
const unsigned long outLow = outBase * (0 - 15ul);
const unsigned long endCycles = cycleCounter + cycles;
for (;;) {
const unsigned long outHigh = master ? outBase * (envelopeUnit.getVolume() * 2 - 15ul) : outLow;
const unsigned long nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles;
unsigned long out = dutyUnit.isHighState() ? outHigh : outLow;
while (dutyUnit.getCounter() <= nextMajorEvent) {
*buf = out - prevOut;
prevOut = out;
buf += dutyUnit.getCounter() - cycleCounter;
cycleCounter = dutyUnit.getCounter();
dutyUnit.event();
out = dutyUnit.isHighState() ? outHigh : outLow;
}
if (cycleCounter < nextMajorEvent) {
*buf = out - prevOut;
prevOut = out;
buf += nextMajorEvent - cycleCounter;
cycleCounter = nextMajorEvent;
}
if (nextEventUnit->getCounter() == nextMajorEvent) {
nextEventUnit->event();
setEvent();
} else
break;
}
if (cycleCounter & SoundUnit::COUNTER_MAX) {
dutyUnit.resetCounters(cycleCounter);
lengthCounter.resetCounters(cycleCounter);
envelopeUnit.resetCounters(cycleCounter);
sweepUnit.resetCounters(cycleCounter);
cycleCounter -= SoundUnit::COUNTER_MAX;
}
}
}

View File

@ -0,0 +1,94 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef SOUND_CHANNEL1_H
#define SOUND_CHANNEL1_H
#include "gbint.h"
#include "master_disabler.h"
#include "length_counter.h"
#include "duty_unit.h"
#include "envelope_unit.h"
#include "static_output_tester.h"
namespace gambatte {
struct SaveState;
class Channel1 {
class SweepUnit : public SoundUnit {
MasterDisabler &disableMaster;
DutyUnit &dutyUnit;
unsigned short shadow;
unsigned char nr0;
bool negging;
unsigned calcFreq();
public:
SweepUnit(MasterDisabler &disabler, DutyUnit &dutyUnit);
void event();
void nr0Change(unsigned newNr0);
void nr4Init(unsigned long cycleCounter);
void reset();
void saveState(SaveState &state) const;
void loadState(const SaveState &state);
};
friend class StaticOutputTester<Channel1,DutyUnit>;
StaticOutputTester<Channel1,DutyUnit> staticOutputTest;
DutyMasterDisabler disableMaster;
LengthCounter lengthCounter;
DutyUnit dutyUnit;
EnvelopeUnit envelopeUnit;
SweepUnit sweepUnit;
SoundUnit *nextEventUnit;
unsigned long cycleCounter;
unsigned long soMask;
unsigned long prevOut;
unsigned char nr4;
bool master;
void setEvent();
public:
Channel1();
void setNr0(unsigned data);
void setNr1(unsigned data);
void setNr2(unsigned data);
void setNr3(unsigned data);
void setNr4(unsigned data);
void setSo(unsigned long soMask);
bool isActive() const { return master; }
void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles);
void reset();
void init(bool cgb);
void saveState(SaveState &state);
void loadState(const SaveState &state);
};
}
#endif

View File

@ -0,0 +1,165 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "channel2.h"
#include "../savestate.h"
namespace gambatte {
Channel2::Channel2() :
staticOutputTest(*this, dutyUnit),
disableMaster(master, dutyUnit),
lengthCounter(disableMaster, 0x3F),
envelopeUnit(staticOutputTest),
cycleCounter(0),
soMask(0),
prevOut(0),
nr4(0),
master(false)
{
setEvent();
}
void Channel2::setEvent() {
// nextEventUnit = &dutyUnit;
// if (envelopeUnit.getCounter() < nextEventUnit->getCounter())
nextEventUnit = &envelopeUnit;
if (lengthCounter.getCounter() < nextEventUnit->getCounter())
nextEventUnit = &lengthCounter;
}
void Channel2::setNr1(const unsigned data) {
lengthCounter.nr1Change(data, nr4, cycleCounter);
dutyUnit.nr1Change(data, cycleCounter);
setEvent();
}
void Channel2::setNr2(const unsigned data) {
if (envelopeUnit.nr2Change(data))
disableMaster();
else
staticOutputTest(cycleCounter);
setEvent();
}
void Channel2::setNr3(const unsigned data) {
dutyUnit.nr3Change(data, cycleCounter);
setEvent();
}
void Channel2::setNr4(const unsigned data) {
lengthCounter.nr4Change(nr4, data, cycleCounter);
nr4 = data;
if (data & 0x80) { //init-bit
nr4 &= 0x7F;
master = !envelopeUnit.nr4Init(cycleCounter);
staticOutputTest(cycleCounter);
}
dutyUnit.nr4Change(data, cycleCounter);
setEvent();
}
void Channel2::setSo(const unsigned long soMask) {
this->soMask = soMask;
staticOutputTest(cycleCounter);
setEvent();
}
void Channel2::reset() {
cycleCounter = 0x1000 | (cycleCounter & 0xFFF); // cycleCounter >> 12 & 7 represents the frame sequencer position.
// lengthCounter.reset();
dutyUnit.reset();
envelopeUnit.reset();
setEvent();
}
void Channel2::init(const bool cgb) {
lengthCounter.init(cgb);
}
void Channel2::saveState(SaveState &state) {
dutyUnit.saveState(state.spu.ch2.duty, cycleCounter);
envelopeUnit.saveState(state.spu.ch2.env);
lengthCounter.saveState(state.spu.ch2.lcounter);
state.spu.ch2.nr4 = nr4;
state.spu.ch2.master = master;
}
void Channel2::loadState(const SaveState &state) {
dutyUnit.loadState(state.spu.ch2.duty, state.mem.ioamhram.get()[0x116], state.spu.ch2.nr4,state.spu.cycleCounter);
envelopeUnit.loadState(state.spu.ch2.env, state.mem.ioamhram.get()[0x117], state.spu.cycleCounter);
lengthCounter.loadState(state.spu.ch2.lcounter, state.spu.cycleCounter);
cycleCounter = state.spu.cycleCounter;
nr4 = state.spu.ch2.nr4;
master = state.spu.ch2.master;
}
void Channel2::update(uint_least32_t *buf, const unsigned long soBaseVol, unsigned long cycles) {
const unsigned long outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0;
const unsigned long outLow = outBase * (0 - 15ul);
const unsigned long endCycles = cycleCounter + cycles;
for (;;) {
const unsigned long outHigh = master ? outBase * (envelopeUnit.getVolume() * 2 - 15ul) : outLow;
const unsigned long nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles;
unsigned long out = dutyUnit.isHighState() ? outHigh : outLow;
while (dutyUnit.getCounter() <= nextMajorEvent) {
*buf += out - prevOut;
prevOut = out;
buf += dutyUnit.getCounter() - cycleCounter;
cycleCounter = dutyUnit.getCounter();
dutyUnit.event();
out = dutyUnit.isHighState() ? outHigh : outLow;
}
if (cycleCounter < nextMajorEvent) {
*buf += out - prevOut;
prevOut = out;
buf += nextMajorEvent - cycleCounter;
cycleCounter = nextMajorEvent;
}
if (nextEventUnit->getCounter() == nextMajorEvent) {
nextEventUnit->event();
setEvent();
} else
break;
}
if (cycleCounter & SoundUnit::COUNTER_MAX) {
dutyUnit.resetCounters(cycleCounter);
lengthCounter.resetCounters(cycleCounter);
envelopeUnit.resetCounters(cycleCounter);
cycleCounter -= SoundUnit::COUNTER_MAX;
}
}
}

View File

@ -0,0 +1,73 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef SOUND_CHANNEL2_H
#define SOUND_CHANNEL2_H
#include "gbint.h"
#include "length_counter.h"
#include "duty_unit.h"
#include "envelope_unit.h"
#include "static_output_tester.h"
namespace gambatte {
struct SaveState;
class Channel2 {
friend class StaticOutputTester<Channel2,DutyUnit>;
StaticOutputTester<Channel2,DutyUnit> staticOutputTest;
DutyMasterDisabler disableMaster;
LengthCounter lengthCounter;
DutyUnit dutyUnit;
EnvelopeUnit envelopeUnit;
SoundUnit *nextEventUnit;
unsigned long cycleCounter;
unsigned long soMask;
unsigned long prevOut;
unsigned char nr4;
bool master;
void setEvent();
public:
Channel2();
void setNr1(unsigned data);
void setNr2(unsigned data);
void setNr3(unsigned data);
void setNr4(unsigned data);
void setSo(unsigned long soMask);
// void deactivate() { disableMaster(); setEvent(); }
bool isActive() const { return master; }
void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles);
void reset();
void init(bool cgb);
void saveState(SaveState &state);
void loadState(const SaveState &state);
};
}
#endif

View File

@ -0,0 +1,211 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "channel3.h"
#include "../savestate.h"
#include <cstring>
#include <algorithm>
static inline unsigned toPeriod(const unsigned nr3, const unsigned nr4) {
return 0x800 - ((nr4 << 8 & 0x700) | nr3);
}
namespace gambatte {
Channel3::Channel3() :
disableMaster(master, waveCounter),
lengthCounter(disableMaster, 0xFF),
cycleCounter(0),
soMask(0),
prevOut(0),
waveCounter(SoundUnit::COUNTER_DISABLED),
lastReadTime(0),
nr0(0),
nr3(0),
nr4(0),
wavePos(0),
rShift(4),
sampleBuf(0),
master(false),
cgb(false)
{}
void Channel3::setNr0(const unsigned data) {
nr0 = data & 0x80;
if (!(data & 0x80))
disableMaster();
}
void Channel3::setNr2(const unsigned data) {
rShift = (data >> 5 & 3U) - 1;
if (rShift > 3)
rShift = 4;
}
void Channel3::setNr4(const unsigned data) {
lengthCounter.nr4Change(nr4, data, cycleCounter);
nr4 = data & 0x7F;
if (data & nr0/* & 0x80*/) {
if (!cgb && waveCounter == cycleCounter + 1) {
const unsigned pos = ((wavePos + 1) & 0x1F) >> 1;
if (pos < 4)
waveRam[0] = waveRam[pos];
else
std::memcpy(waveRam, waveRam + (pos & ~3), 4);
}
master = true;
wavePos = 0;
lastReadTime = waveCounter = cycleCounter + toPeriod(nr3, data) + 3;
}
}
void Channel3::setSo(const unsigned long soMask) {
this->soMask = soMask;
}
void Channel3::reset() {
cycleCounter = 0x1000 | (cycleCounter & 0xFFF); // cycleCounter >> 12 & 7 represents the frame sequencer position.
// lengthCounter.reset();
sampleBuf = 0;
}
void Channel3::init(const bool cgb) {
this->cgb = cgb;
lengthCounter.init(cgb);
}
void Channel3::setStatePtrs(SaveState &state) {
state.spu.ch3.waveRam.set(waveRam, sizeof waveRam);
}
void Channel3::saveState(SaveState &state) const {
lengthCounter.saveState(state.spu.ch3.lcounter);
state.spu.ch3.waveCounter = waveCounter;
state.spu.ch3.lastReadTime = lastReadTime;
state.spu.ch3.nr3 = nr3;
state.spu.ch3.nr4 = nr4;
state.spu.ch3.wavePos = wavePos;
state.spu.ch3.sampleBuf = sampleBuf;
state.spu.ch3.master = master;
}
void Channel3::loadState(const SaveState &state) {
lengthCounter.loadState(state.spu.ch3.lcounter, state.spu.cycleCounter);
cycleCounter = state.spu.cycleCounter;
waveCounter = std::max(state.spu.ch3.waveCounter, state.spu.cycleCounter);
lastReadTime = state.spu.ch3.lastReadTime;
nr3 = state.spu.ch3.nr3;
nr4 = state.spu.ch3.nr4;
wavePos = state.spu.ch3.wavePos & 0x1F;
sampleBuf = state.spu.ch3.sampleBuf;
master = state.spu.ch3.master;
nr0 = state.mem.ioamhram.get()[0x11A] & 0x80;
setNr2(state.mem.ioamhram.get()[0x11C]);
}
void Channel3::updateWaveCounter(const unsigned long cc) {
if (cc >= waveCounter) {
const unsigned period = toPeriod(nr3, nr4);
const unsigned long periods = (cc - waveCounter) / period;
lastReadTime = waveCounter + periods * period;
waveCounter = lastReadTime + period;
wavePos += periods + 1;
wavePos &= 0x1F;
sampleBuf = waveRam[wavePos >> 1];
}
}
void Channel3::update(uint_least32_t *buf, const unsigned long soBaseVol, unsigned long cycles) {
const unsigned long outBase = (nr0/* & 0x80*/) ? soBaseVol & soMask : 0;
if (outBase && rShift != 4) {
const unsigned long endCycles = cycleCounter + cycles;
for (;;) {
const unsigned long nextMajorEvent = lengthCounter.getCounter() < endCycles ? lengthCounter.getCounter() : endCycles;
unsigned long out = outBase * (master ? ((sampleBuf >> (~wavePos << 2 & 4) & 0xF) >> rShift) * 2 - 15ul : 0 - 15ul);
while (waveCounter <= nextMajorEvent) {
*buf += out - prevOut;
prevOut = out;
buf += waveCounter - cycleCounter;
cycleCounter = waveCounter;
lastReadTime = waveCounter;
waveCounter += toPeriod(nr3, nr4);
++wavePos;
wavePos &= 0x1F;
sampleBuf = waveRam[wavePos >> 1];
out = outBase * (/*master ? */((sampleBuf >> (~wavePos << 2 & 4) & 0xF) >> rShift) * 2 - 15ul/* : 0 - 15ul*/);
}
if (cycleCounter < nextMajorEvent) {
*buf += out - prevOut;
prevOut = out;
buf += nextMajorEvent - cycleCounter;
cycleCounter = nextMajorEvent;
}
if (lengthCounter.getCounter() == nextMajorEvent) {
lengthCounter.event();
} else
break;
}
} else {
if (outBase) {
const unsigned long out = outBase * (0 - 15ul);
*buf += out - prevOut;
prevOut = out;
}
cycleCounter += cycles;
while (lengthCounter.getCounter() <= cycleCounter) {
updateWaveCounter(lengthCounter.getCounter());
lengthCounter.event();
}
updateWaveCounter(cycleCounter);
}
if (cycleCounter & SoundUnit::COUNTER_MAX) {
lengthCounter.resetCounters(cycleCounter);
if (waveCounter != SoundUnit::COUNTER_DISABLED)
waveCounter -= SoundUnit::COUNTER_MAX;
lastReadTime -= SoundUnit::COUNTER_MAX;
cycleCounter -= SoundUnit::COUNTER_MAX;
}
}
}

View File

@ -0,0 +1,103 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef SOUND_CHANNEL3_H
#define SOUND_CHANNEL3_H
#include "gbint.h"
#include "master_disabler.h"
#include "length_counter.h"
namespace gambatte {
struct SaveState;
class Channel3 {
class Ch3MasterDisabler : public MasterDisabler {
unsigned long &waveCounter;
public:
Ch3MasterDisabler(bool &m, unsigned long &wC) : MasterDisabler(m), waveCounter(wC) {}
void operator()() { MasterDisabler::operator()(); waveCounter = SoundUnit::COUNTER_DISABLED; }
};
unsigned char waveRam[0x10];
Ch3MasterDisabler disableMaster;
LengthCounter lengthCounter;
unsigned long cycleCounter;
unsigned long soMask;
unsigned long prevOut;
unsigned long waveCounter;
unsigned long lastReadTime;
unsigned char nr0;
unsigned char nr3;
unsigned char nr4;
unsigned char wavePos;
unsigned char rShift;
unsigned char sampleBuf;
bool master;
bool cgb;
void updateWaveCounter(unsigned long cc);
public:
Channel3();
bool isActive() const { return master; }
void reset();
void init(bool cgb);
void setStatePtrs(SaveState &state);
void saveState(SaveState &state) const;
void loadState(const SaveState &state);
void setNr0(unsigned data);
void setNr1(unsigned data) { lengthCounter.nr1Change(data, nr4, cycleCounter); }
void setNr2(unsigned data);
void setNr3(unsigned data) { nr3 = data; }
void setNr4(unsigned data);
void setSo(unsigned long soMask);
void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles);
unsigned waveRamRead(unsigned index) const {
if (master) {
if (!cgb && cycleCounter != lastReadTime)
return 0xFF;
index = wavePos >> 1;
}
return waveRam[index];
}
void waveRamWrite(unsigned index, unsigned data) {
if (master) {
if (!cgb && cycleCounter != lastReadTime)
return;
index = wavePos >> 1;
}
waveRam[index] = data;
}
};
}
#endif

View File

@ -0,0 +1,304 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "channel4.h"
#include "../savestate.h"
#include <algorithm>
static unsigned long toPeriod(const unsigned nr3) {
unsigned s = (nr3 >> 4) + 3;
unsigned r = nr3 & 7;
if (!r) {
r = 1;
--s;
}
return r << s;
}
namespace gambatte {
Channel4::Lfsr::Lfsr() :
backupCounter(COUNTER_DISABLED),
reg(0xFF),
nr3(0),
master(false)
{}
void Channel4::Lfsr::updateBackupCounter(const unsigned long cc) {
/*if (backupCounter <= cc) {
const unsigned long period = toPeriod(nr3);
backupCounter = cc - (cc - backupCounter) % period + period;
}*/
if (backupCounter <= cc) {
const unsigned long period = toPeriod(nr3);
unsigned long periods = (cc - backupCounter) / period + 1;
backupCounter += periods * period;
if (master && nr3 < 0xE0) {
if (nr3 & 8) {
while (periods > 6) {
const unsigned xored = (reg << 1 ^ reg) & 0x7E;
reg = (reg >> 6 & ~0x7E) | xored | xored << 8;
periods -= 6;
}
const unsigned xored = ((reg ^ reg >> 1) << (7 - periods)) & 0x7F;
reg = (reg >> periods & ~(0x80 - (0x80 >> periods))) | xored | xored << 8;
} else {
while (periods > 15) {
reg = reg ^ reg >> 1;
periods -= 15;
}
reg = reg >> periods | (((reg ^ reg >> 1) << (15 - periods)) & 0x7FFF);
}
}
}
}
void Channel4::Lfsr::reviveCounter(const unsigned long cc) {
updateBackupCounter(cc);
counter = backupCounter;
}
/*static const unsigned char nextStateDistance[0x40] = {
6, 1, 1, 2, 2, 1, 1, 3,
3, 1, 1, 2, 2, 1, 1, 4,
4, 1, 1, 2, 2, 1, 1, 3,
3, 1, 1, 2, 2, 1, 1, 5,
5, 1, 1, 2, 2, 1, 1, 3,
3, 1, 1, 2, 2, 1, 1, 4,
4, 1, 1, 2, 2, 1, 1, 3,
3, 1, 1, 2, 2, 1, 1, 6,
};*/
inline void Channel4::Lfsr::event() {
if (nr3 < 0xE0) {
const unsigned shifted = reg >> 1;
const unsigned xored = (reg ^ shifted) & 1;
reg = shifted | xored << 14;
if (nr3 & 8)
reg = (reg & ~0x40) | xored << 6;
}
counter += toPeriod(nr3);
backupCounter = counter;
/*if (nr3 < 0xE0) {
const unsigned periods = nextStateDistance[reg & 0x3F];
const unsigned xored = ((reg ^ reg >> 1) << (7 - periods)) & 0x7F;
reg = reg >> periods | xored << 8;
if (nr3 & 8)
reg = reg & ~(0x80 - (0x80 >> periods)) | xored;
}
const unsigned long period = toPeriod(nr3);
backupCounter = counter + period;
counter += period * nextStateDistance[reg & 0x3F];*/
}
void Channel4::Lfsr::nr3Change(const unsigned newNr3, const unsigned long cc) {
updateBackupCounter(cc);
nr3 = newNr3;
// if (counter != COUNTER_DISABLED)
// counter = backupCounter + toPeriod(nr3) * (nextStateDistance[reg & 0x3F] - 1);
}
void Channel4::Lfsr::nr4Init(unsigned long cc) {
disableMaster();
updateBackupCounter(cc);
master = true;
backupCounter += 4;
counter = backupCounter;
// counter = backupCounter + toPeriod(nr3) * (nextStateDistance[reg & 0x3F] - 1);
}
void Channel4::Lfsr::reset(const unsigned long cc) {
nr3 = 0;
disableMaster();
backupCounter = cc + toPeriod(nr3);
}
void Channel4::Lfsr::resetCounters(const unsigned long oldCc) {
updateBackupCounter(oldCc);
backupCounter -= COUNTER_MAX;
SoundUnit::resetCounters(oldCc);
}
void Channel4::Lfsr::saveState(SaveState &state, const unsigned long cc) {
updateBackupCounter(cc);
state.spu.ch4.lfsr.counter = backupCounter;
state.spu.ch4.lfsr.reg = reg;
}
void Channel4::Lfsr::loadState(const SaveState &state) {
counter = backupCounter = std::max(state.spu.ch4.lfsr.counter, state.spu.cycleCounter);
reg = state.spu.ch4.lfsr.reg;
master = state.spu.ch4.master;
nr3 = state.mem.ioamhram.get()[0x122];
}
Channel4::Channel4() :
staticOutputTest(*this, lfsr),
disableMaster(master, lfsr),
lengthCounter(disableMaster, 0x3F),
envelopeUnit(staticOutputTest),
cycleCounter(0),
soMask(0),
prevOut(0),
nr4(0),
master(false)
{
setEvent();
}
void Channel4::setEvent() {
// nextEventUnit = &lfsr;
// if (envelopeUnit.getCounter() < nextEventUnit->getCounter())
nextEventUnit = &envelopeUnit;
if (lengthCounter.getCounter() < nextEventUnit->getCounter())
nextEventUnit = &lengthCounter;
}
void Channel4::setNr1(const unsigned data) {
lengthCounter.nr1Change(data, nr4, cycleCounter);
setEvent();
}
void Channel4::setNr2(const unsigned data) {
if (envelopeUnit.nr2Change(data))
disableMaster();
else
staticOutputTest(cycleCounter);
setEvent();
}
void Channel4::setNr4(const unsigned data) {
lengthCounter.nr4Change(nr4, data, cycleCounter);
nr4 = data;
if (data & 0x80) { //init-bit
nr4 &= 0x7F;
master = !envelopeUnit.nr4Init(cycleCounter);
if (master)
lfsr.nr4Init(cycleCounter);
staticOutputTest(cycleCounter);
}
setEvent();
}
void Channel4::setSo(const unsigned long soMask) {
this->soMask = soMask;
staticOutputTest(cycleCounter);
setEvent();
}
void Channel4::reset() {
cycleCounter = 0x1000 | (cycleCounter & 0xFFF); // cycleCounter >> 12 & 7 represents the frame sequencer position.
// lengthCounter.reset();
lfsr.reset(cycleCounter);
envelopeUnit.reset();
setEvent();
}
void Channel4::init(const bool cgb) {
lengthCounter.init(cgb);
}
void Channel4::saveState(SaveState &state) {
lfsr.saveState(state, cycleCounter);
envelopeUnit.saveState(state.spu.ch4.env);
lengthCounter.saveState(state.spu.ch4.lcounter);
state.spu.ch4.nr4 = nr4;
state.spu.ch4.master = master;
}
void Channel4::loadState(const SaveState &state) {
lfsr.loadState(state);
envelopeUnit.loadState(state.spu.ch4.env, state.mem.ioamhram.get()[0x121], state.spu.cycleCounter);
lengthCounter.loadState(state.spu.ch4.lcounter, state.spu.cycleCounter);
cycleCounter = state.spu.cycleCounter;
nr4 = state.spu.ch4.nr4;
master = state.spu.ch4.master;
}
void Channel4::update(uint_least32_t *buf, const unsigned long soBaseVol, unsigned long cycles) {
const unsigned long outBase = envelopeUnit.dacIsOn() ? soBaseVol & soMask : 0;
const unsigned long outLow = outBase * (0 - 15ul);
const unsigned long endCycles = cycleCounter + cycles;
for (;;) {
const unsigned long outHigh = /*master ? */outBase * (envelopeUnit.getVolume() * 2 - 15ul)/* : outLow*/;
const unsigned long nextMajorEvent = nextEventUnit->getCounter() < endCycles ? nextEventUnit->getCounter() : endCycles;
unsigned long out = lfsr.isHighState() ? outHigh : outLow;
while (lfsr.getCounter() <= nextMajorEvent) {
*buf += out - prevOut;
prevOut = out;
buf += lfsr.getCounter() - cycleCounter;
cycleCounter = lfsr.getCounter();
lfsr.event();
out = lfsr.isHighState() ? outHigh : outLow;
}
if (cycleCounter < nextMajorEvent) {
*buf += out - prevOut;
prevOut = out;
buf += nextMajorEvent - cycleCounter;
cycleCounter = nextMajorEvent;
}
if (nextEventUnit->getCounter() == nextMajorEvent) {
nextEventUnit->event();
setEvent();
} else
break;
}
if (cycleCounter & SoundUnit::COUNTER_MAX) {
lengthCounter.resetCounters(cycleCounter);
lfsr.resetCounters(cycleCounter);
envelopeUnit.resetCounters(cycleCounter);
cycleCounter -= SoundUnit::COUNTER_MAX;
}
}
}

View File

@ -0,0 +1,102 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef SOUND_CHANNEL4_H
#define SOUND_CHANNEL4_H
#include "gbint.h"
#include "master_disabler.h"
#include "length_counter.h"
#include "envelope_unit.h"
#include "static_output_tester.h"
namespace gambatte {
struct SaveState;
class Channel4 {
class Lfsr : public SoundUnit {
unsigned long backupCounter;
unsigned short reg;
unsigned char nr3;
bool master;
void updateBackupCounter(unsigned long cc);
public:
Lfsr();
void event();
bool isHighState() const { return ~reg & 1; }
void nr3Change(unsigned newNr3, unsigned long cc);
void nr4Init(unsigned long cc);
void reset(unsigned long cc);
void saveState(SaveState &state, const unsigned long cc);
void loadState(const SaveState &state);
void resetCounters(unsigned long oldCc);
void disableMaster() { killCounter(); master = false; reg = 0xFF; }
void killCounter() { counter = COUNTER_DISABLED; }
void reviveCounter(unsigned long cc);
};
class Ch4MasterDisabler : public MasterDisabler {
Lfsr &lfsr;
public:
Ch4MasterDisabler(bool &m, Lfsr &lfsr) : MasterDisabler(m), lfsr(lfsr) {}
void operator()() { MasterDisabler::operator()(); lfsr.disableMaster(); }
};
friend class StaticOutputTester<Channel4,Lfsr>;
StaticOutputTester<Channel4,Lfsr> staticOutputTest;
Ch4MasterDisabler disableMaster;
LengthCounter lengthCounter;
EnvelopeUnit envelopeUnit;
Lfsr lfsr;
SoundUnit *nextEventUnit;
unsigned long cycleCounter;
unsigned long soMask;
unsigned long prevOut;
unsigned char nr4;
bool master;
void setEvent();
public:
Channel4();
void setNr1(unsigned data);
void setNr2(unsigned data);
void setNr3(unsigned data) { lfsr.nr3Change(data, cycleCounter); /*setEvent();*/ }
void setNr4(unsigned data);
void setSo(unsigned long soMask);
bool isActive() const { return master; }
void update(uint_least32_t *buf, unsigned long soBaseVol, unsigned long cycles);
void reset();
void init(bool cgb);
void saveState(SaveState &state);
void loadState(const SaveState &state);
};
}
#endif

View File

@ -0,0 +1,152 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "duty_unit.h"
#include <algorithm>
static inline bool toOutState(const unsigned duty, const unsigned pos) {
static const unsigned char duties[4] = { 0x80, 0x81, 0xE1, 0x7E };
return duties[duty] >> pos & 1;
}
static inline unsigned toPeriod(const unsigned freq) {
return (2048 - freq) << 1;
}
namespace gambatte {
void DutyUnit::updatePos(const unsigned long cc) {
if (cc >= nextPosUpdate) {
const unsigned long inc = (cc - nextPosUpdate) / period + 1;
nextPosUpdate += period * inc;
pos += inc;
pos &= 7;
}
}
void DutyUnit::setDuty(const unsigned nr1) {
duty = nr1 >> 6;
high = toOutState(duty, pos);
}
void DutyUnit::setCounter() {
static const unsigned char nextStateDistance[4 * 8] = {
6, 5, 4, 3, 2, 1, 0, 0,
0, 5, 4, 3, 2, 1, 0, 1,
0, 3, 2, 1, 0, 3, 2, 1,
0, 5, 4, 3, 2, 1, 0, 1
};
if (enableEvents && nextPosUpdate != COUNTER_DISABLED)
counter = nextPosUpdate + period * nextStateDistance[(duty * 8) | pos];
else
counter = COUNTER_DISABLED;
}
void DutyUnit::setFreq(const unsigned newFreq, const unsigned long cc) {
updatePos(cc);
period = toPeriod(newFreq);
setCounter();
}
void DutyUnit::event() {
unsigned inc = period << duty;
if (duty == 3)
inc -= period * 2;
if (!(high ^= true))
inc = period * 8 - inc;
counter += inc;
}
void DutyUnit::nr1Change(const unsigned newNr1, const unsigned long cc) {
updatePos(cc);
setDuty(newNr1);
setCounter();
}
void DutyUnit::nr3Change(const unsigned newNr3, const unsigned long cc) {
setFreq((getFreq() & 0x700) | newNr3, cc);
}
void DutyUnit::nr4Change(const unsigned newNr4, const unsigned long cc) {
setFreq((newNr4 << 8 & 0x700) | (getFreq() & 0xFF), cc);
if (newNr4 & 0x80) {
nextPosUpdate = (cc & ~1) + period;
setCounter();
}
}
DutyUnit::DutyUnit() :
nextPosUpdate(COUNTER_DISABLED),
period(4096),
pos(0),
duty(0),
high(false),
enableEvents(true)
{}
void DutyUnit::reset() {
pos = 0;
high = toOutState(duty, pos);
nextPosUpdate = COUNTER_DISABLED;
setCounter();
}
void DutyUnit::saveState(SaveState::SPU::Duty &dstate, const unsigned long cc) {
updatePos(cc);
dstate.nextPosUpdate = nextPosUpdate;
dstate.nr3 = getFreq() & 0xFF;
dstate.pos = pos;
}
void DutyUnit::loadState(const SaveState::SPU::Duty &dstate, const unsigned nr1, const unsigned nr4, const unsigned long cc) {
nextPosUpdate = std::max(dstate.nextPosUpdate, cc);
pos = dstate.pos & 7;
setDuty(nr1);
period = toPeriod((nr4 << 8 & 0x700) | dstate.nr3);
enableEvents = true;
setCounter();
}
void DutyUnit::resetCounters(const unsigned long oldCc) {
if (nextPosUpdate == COUNTER_DISABLED)
return;
updatePos(oldCc);
nextPosUpdate -= COUNTER_MAX;
SoundUnit::resetCounters(oldCc);
}
void DutyUnit::killCounter() {
enableEvents = false;
setCounter();
}
void DutyUnit::reviveCounter(const unsigned long cc) {
updatePos(cc);
high = toOutState(duty, pos);
enableEvents = true;
setCounter();
}
}

View File

@ -0,0 +1,68 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aam<EFBFBD>s *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef DUTY_UNIT_H
#define DUTY_UNIT_H
#include "sound_unit.h"
#include "master_disabler.h"
#include "../savestate.h"
namespace gambatte {
class DutyUnit : public SoundUnit {
unsigned long nextPosUpdate;
unsigned short period;
unsigned char pos;
unsigned char duty;
bool high;
bool enableEvents;
void setCounter();
void setDuty(unsigned nr1);
void updatePos(unsigned long cc);
public:
DutyUnit();
void event();
bool isHighState() const { return high; }
void nr1Change(unsigned newNr1, unsigned long cc);
void nr3Change(unsigned newNr3, unsigned long cc);
void nr4Change(unsigned newNr4, unsigned long cc);
void reset();
void saveState(SaveState::SPU::Duty &dstate, unsigned long cc);
void loadState(const SaveState::SPU::Duty &dstate, unsigned nr1, unsigned nr4, unsigned long cc);
void resetCounters(unsigned long oldCc);
void killCounter();
void reviveCounter(unsigned long cc);
//intended for use by SweepUnit only.
unsigned getFreq() const { return 2048 - (period >> 1); }
void setFreq(unsigned newFreq, unsigned long cc);
};
class DutyMasterDisabler : public MasterDisabler {
DutyUnit &dutyUnit;
public:
DutyMasterDisabler(bool &m, DutyUnit &dutyUnit) : MasterDisabler(m), dutyUnit(dutyUnit) {}
void operator()() { MasterDisabler::operator()(); dutyUnit.killCounter(); }
};
}
#endif

View File

@ -0,0 +1,106 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "envelope_unit.h"
#include <algorithm>
namespace gambatte {
EnvelopeUnit::VolOnOffEvent EnvelopeUnit::nullEvent;
void EnvelopeUnit::event() {
const unsigned long period = nr2 & 7;
if (period) {
unsigned newVol = volume;
if (nr2 & 8)
++newVol;
else
--newVol;
if (newVol < 0x10U) {
volume = newVol;
if (volume < 2)
volOnOffEvent(counter);
counter += period << 15;
} else
counter = COUNTER_DISABLED;
} else
counter += 8ul << 15;
}
bool EnvelopeUnit::nr2Change(const unsigned newNr2) {
if (!(nr2 & 7) && counter != COUNTER_DISABLED)
++volume;
else if (!(nr2 & 8))
volume += 2;
if ((nr2 ^ newNr2) & 8)
volume = 0x10 - volume;
volume &= 0xF;
nr2 = newNr2;
return !(newNr2 & 0xF8);
}
bool EnvelopeUnit::nr4Init(const unsigned long cc) {
{
unsigned long period = nr2 & 7;
if (!period)
period = 8;
if (!(cc & 0x7000))
++period;
counter = cc - ((cc - 0x1000) & 0x7FFF) + period * 0x8000;
}
volume = nr2 >> 4;
return !(nr2 & 0xF8);
}
EnvelopeUnit::EnvelopeUnit(VolOnOffEvent &volOnOffEvent)
: volOnOffEvent(volOnOffEvent),
nr2(0),
volume(0)
{
}
void EnvelopeUnit::reset() {
counter = COUNTER_DISABLED;
}
void EnvelopeUnit::saveState(SaveState::SPU::Env &estate) const {
estate.counter = counter;
estate.volume = volume;
}
void EnvelopeUnit::loadState(const SaveState::SPU::Env &estate, const unsigned nr2, const unsigned long cc) {
counter = std::max(estate.counter, cc);
volume = estate.volume;
this->nr2 = nr2;
}
}

View File

@ -0,0 +1,54 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef ENVELOPE_UNIT_H
#define ENVELOPE_UNIT_H
#include "sound_unit.h"
#include "../savestate.h"
namespace gambatte {
class EnvelopeUnit : public SoundUnit {
public:
struct VolOnOffEvent {
virtual ~VolOnOffEvent() {}
virtual void operator()(unsigned long /*cc*/) {}
};
private:
static VolOnOffEvent nullEvent;
VolOnOffEvent &volOnOffEvent;
unsigned char nr2;
unsigned char volume;
public:
explicit EnvelopeUnit(VolOnOffEvent &volOnOffEvent = nullEvent);
void event();
bool dacIsOn() const { return nr2 & 0xF8; }
unsigned getVolume() const { return volume; }
bool nr2Change(unsigned newNr2);
bool nr4Init(unsigned long cycleCounter);
void reset();
void saveState(SaveState::SPU::Env &estate) const;
void loadState(const SaveState::SPU::Env &estate, unsigned nr2, unsigned long cc);
};
}
#endif

View File

@ -0,0 +1,91 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "length_counter.h"
#include "master_disabler.h"
#include <algorithm>
namespace gambatte {
LengthCounter::LengthCounter(MasterDisabler &disabler, const unsigned mask) :
disableMaster(disabler),
lengthMask(mask)
{
init(false);
nr1Change(0, 0, 0);
}
void LengthCounter::event() {
counter = COUNTER_DISABLED;
lengthCounter = 0;
disableMaster();
}
void LengthCounter::nr1Change(const unsigned newNr1, const unsigned nr4, const unsigned long cycleCounter) {
lengthCounter = (~newNr1 & lengthMask) + 1;
counter = (nr4 & 0x40) ?( (cycleCounter >> 13) + lengthCounter) << 13 : static_cast<unsigned long>(COUNTER_DISABLED);
}
void LengthCounter::nr4Change(const unsigned oldNr4, const unsigned newNr4, const unsigned long cycleCounter) {
if (counter != COUNTER_DISABLED)
lengthCounter = (counter >> 13) - (cycleCounter >> 13);
{
unsigned dec = 0;
if (newNr4 & 0x40) {
dec = ~cycleCounter >> 12 & 1;
if (!(oldNr4 & 0x40) && lengthCounter) {
if (!(lengthCounter -= dec))
disableMaster();
}
}
if ((newNr4 & 0x80) && !lengthCounter)
lengthCounter = lengthMask + 1 - dec;
}
if ((newNr4 & 0x40) && lengthCounter)
counter = ((cycleCounter >> 13) + lengthCounter) << 13;
else
counter = COUNTER_DISABLED;
}
/*void LengthCounter::reset() {
counter = COUNTER_DISABLED;
if (cgb)
lengthCounter = lengthMask + 1;
}*/
void LengthCounter::init(const bool cgb) {
this->cgb = cgb;
}
void LengthCounter::saveState(SaveState::SPU::LCounter &lstate) const {
lstate.counter = counter;
lstate.lengthCounter = lengthCounter;
}
void LengthCounter::loadState(const SaveState::SPU::LCounter &lstate, const unsigned long cc) {
counter = std::max(lstate.counter, cc);
lengthCounter = lstate.lengthCounter;
}
}

View File

@ -0,0 +1,48 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef LENGTH_COUNTER_H
#define LENGTH_COUNTER_H
#include "sound_unit.h"
#include "../savestate.h"
namespace gambatte {
class MasterDisabler;
class LengthCounter : public SoundUnit {
MasterDisabler &disableMaster;
unsigned short lengthCounter;
const unsigned char lengthMask;
bool cgb;
public:
LengthCounter(MasterDisabler &disabler, unsigned lengthMask);
void event();
void nr1Change(unsigned newNr1, unsigned nr4, unsigned long cc);
void nr4Change(unsigned oldNr4, unsigned newNr4, unsigned long cc);
// void reset();
void init(bool cgb);
void saveState(SaveState::SPU::LCounter &lstate) const;
void loadState(const SaveState::SPU::LCounter &lstate, unsigned long cc);
};
}
#endif

View File

@ -0,0 +1,33 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef MASTER_DISABLER_H
#define MASTER_DISABLER_H
namespace gambatte {
class MasterDisabler {
bool &master;
public:
MasterDisabler(bool &m) : master(m) {}
virtual ~MasterDisabler() {}
virtual void operator()() { master = false; }
};
}
#endif

View File

@ -0,0 +1,39 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef SOUND_UNIT_H
#define SOUND_UNIT_H
namespace gambatte {
class SoundUnit {
protected:
unsigned long counter;
public:
enum { COUNTER_MAX = 0x80000000u, COUNTER_DISABLED = 0xFFFFFFFFu };
SoundUnit() : counter(COUNTER_DISABLED) {}
virtual ~SoundUnit() {}
virtual void event() = 0;
unsigned long getCounter() const { return counter; }
virtual void resetCounters(unsigned long /*oldCc*/) { if (counter != COUNTER_DISABLED) counter -= COUNTER_MAX; }
};
}
#endif

View File

@ -0,0 +1,45 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef STATIC_OUTPUT_TESTER_H
#define STATIC_OUTPUT_TESTER_H
#include "envelope_unit.h"
namespace gambatte {
template<class Channel, class Unit>
class StaticOutputTester : public EnvelopeUnit::VolOnOffEvent {
const Channel &ch;
Unit &unit;
public:
StaticOutputTester(const Channel &ch, Unit &unit) : ch(ch), unit(unit) {}
void operator()(unsigned long cc);
};
template<class Channel, class Unit>
void StaticOutputTester<Channel, Unit>::operator()(const unsigned long cc) {
if (ch.soMask && ch.master && ch.envelopeUnit.getVolume())
unit.reviveCounter(cc);
else
unit.killCounter();
}
}
#endif

View File

@ -0,0 +1,183 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "state_osd_elements.h"
#include "bitmap_font.h"
#include "statesaver.h"
#include <fstream>
#include <cstring>
namespace {
using namespace gambatte;
using namespace bitmapfont;
static const char stateLoadedTxt[] = { S,t,a,t,e,SPC,N0,SPC,l,o,a,d,e,d,0 };
static const char stateSavedTxt[] = { S,t,a,t,e,SPC,N0,SPC,s,a,v,e,d,0 };
static const unsigned stateLoadedTxtWidth = getWidth(stateLoadedTxt);
static const unsigned stateSavedTxtWidth = getWidth(stateSavedTxt);
class ShadedTextOsdElment : public OsdElement {
struct ShadeFill {
void operator()(uint_least32_t *dest, const unsigned pitch) const {
dest[2] = dest[1] = dest[0] = 0x000000ul;
dest += pitch;
dest[2] = dest[0] = 0x000000ul;
dest += pitch;
dest[2] = dest[1] = dest[0] = 0x000000ul;
}
};
uint_least32_t *const pixels;
unsigned life;
ShadedTextOsdElment(const ShadedTextOsdElment&);
ShadedTextOsdElment& operator=(const ShadedTextOsdElment&);
public:
ShadedTextOsdElment(unsigned w, const char *txt);
~ShadedTextOsdElment();
const uint_least32_t* update();
};
ShadedTextOsdElment::ShadedTextOsdElment(unsigned width, const char *txt) :
OsdElement(MAX_WIDTH, 144 - HEIGHT - HEIGHT, width + 2, HEIGHT + 2, THREE_FOURTHS),
pixels(new uint_least32_t[w() * h()]),
life(4 * 60) {
std::memset(pixels, 0xFF, w() * h() * sizeof(uint_least32_t));
/*print(pixels + 0 * w() + 0, w(), 0x000000ul, txt);
print(pixels + 0 * w() + 1, w(), 0x000000ul, txt);
print(pixels + 0 * w() + 2, w(), 0x000000ul, txt);
print(pixels + 1 * w() + 0, w(), 0x000000ul, txt);
print(pixels + 1 * w() + 2, w(), 0x000000ul, txt);
print(pixels + 2 * w() + 0, w(), 0x000000ul, txt);
print(pixels + 2 * w() + 1, w(), 0x000000ul, txt);
print(pixels + 2 * w() + 2, w(), 0x000000ul, txt);
print(pixels + 1 * w() + 1, w(), 0xE0E0E0ul, txt);*/
print(pixels, w(), ShadeFill(), txt);
print(pixels + 1 * w() + 1, w(), 0xE0E0E0ul, txt);
}
ShadedTextOsdElment::~ShadedTextOsdElment() {
delete []pixels;
}
const uint_least32_t* ShadedTextOsdElment::update() {
if (life--)
return pixels;
return 0;
}
/*class FramedTextOsdElment : public OsdElement {
uint_least32_t *const pixels;
unsigned life;
FramedTextOsdElment(const FramedTextOsdElment&);
FramedTextOsdElment& operator=(const FramedTextOsdElment&);
public:
FramedTextOsdElment(unsigned w, const char *txt);
~FramedTextOsdElment();
const uint_least32_t* update();
};
FramedTextOsdElment::FramedTextOsdElment(unsigned width, const char *txt) :
OsdElement(NUMBER_WIDTH, 144 - HEIGHT * 2 - HEIGHT / 2, width + NUMBER_WIDTH * 2, HEIGHT * 2),
pixels(new uint_least32_t[w() * h()]),
life(4 * 60) {
std::memset(pixels, 0x00, w() * h() * sizeof(uint_least32_t));
print(pixels + (w() - width) / 2 + ((h() - HEIGHT) / 2) * w(), w(), 0xA0A0A0ul, txt);
}
FramedTextOsdElment::~FramedTextOsdElment() {
delete []pixels;
}
const uint_least32_t* FramedTextOsdElment::update() {
if (life--)
return pixels;
return 0;
}*/
class SaveStateOsdElement : public OsdElement {
uint_least32_t pixels[StateSaver::SS_WIDTH * StateSaver::SS_HEIGHT];
unsigned life;
public:
SaveStateOsdElement(const std::string &fileName, unsigned stateNo);
const uint_least32_t* update();
};
SaveStateOsdElement::SaveStateOsdElement(const std::string &fileName, unsigned stateNo) :
OsdElement((stateNo ? stateNo - 1 : 9) * ((160 - StateSaver::SS_WIDTH) / 10)
+ ((160 - StateSaver::SS_WIDTH) / 10) / 2, 4, StateSaver::SS_WIDTH, StateSaver::SS_HEIGHT),
life(4 * 60) {
std::ifstream file(fileName.c_str(), std::ios_base::binary);
if (file.is_open()) {
file.ignore(5);
file.read(reinterpret_cast<char*>(pixels), sizeof pixels);
} else {
std::memset(pixels, 0, sizeof pixels);
{
using namespace bitmapfont;
static const char txt[] = { E,m,p,t,bitmapfont::y,0 };
print(pixels + 3 + (StateSaver::SS_HEIGHT / 2 - bitmapfont::HEIGHT / 2) * StateSaver::SS_WIDTH, StateSaver::SS_WIDTH, 0x808080ul, txt);
}
}
}
const uint_least32_t* SaveStateOsdElement::update() {
if (life--)
return pixels;
return 0;
}
} // anon namespace
namespace gambatte {
std::auto_ptr<OsdElement> newStateLoadedOsdElement(unsigned stateNo) {
char txt[sizeof stateLoadedTxt];
std::memcpy(txt, stateLoadedTxt, sizeof stateLoadedTxt);
utoa(stateNo, txt + 6);
return std::auto_ptr<OsdElement>(new ShadedTextOsdElment(stateLoadedTxtWidth, txt));
}
std::auto_ptr<OsdElement> newStateSavedOsdElement(unsigned stateNo) {
char txt[sizeof stateSavedTxt];
std::memcpy(txt, stateSavedTxt, sizeof stateSavedTxt);
utoa(stateNo, txt + 6);
return std::auto_ptr<OsdElement>(new ShadedTextOsdElment(stateSavedTxtWidth, txt));
}
std::auto_ptr<OsdElement> newSaveStateOsdElement(const std::string &fileName, unsigned stateNo) {
return std::auto_ptr<OsdElement>(new SaveStateOsdElement(fileName, stateNo));
}
}

View File

@ -0,0 +1,32 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef STATE_OSD_ELEMENTS_H
#define STATE_OSD_ELEMENTS_H
#include "osd_element.h"
#include <memory>
#include <string>
namespace gambatte {
std::auto_ptr<OsdElement> newStateLoadedOsdElement(unsigned stateNo);
std::auto_ptr<OsdElement> newStateSavedOsdElement(unsigned stateNo);
std::auto_ptr<OsdElement> newSaveStateOsdElement(const std::string &fileName, unsigned stateNo);
}
#endif

View File

@ -0,0 +1,470 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "statesaver.h"
#include "savestate.h"
#include "array.h"
#include <vector>
#include <cstring>
#include <algorithm>
#include <fstream>
namespace {
using namespace gambatte;
enum AsciiChar {
NUL, SOH, STX, ETX, EOT, ENQ, ACK, BEL, BS, TAB, LF, VT, FF, CR, SO, SI,
DLE, DC1, DC2, DC3, DC4, NAK, SYN, ETB, CAN, EM, SUB, ESC, FS, GS, RS, US,
SP, XCL, QOT, HSH, DLR, PRC, AMP, APO, LPA, RPA, AST, PLU, COM, HYP, STP, DIV,
NO0, NO1, NO2, NO3, NO4, NO5, NO6, NO7, NO8, NO9, CLN, SCL, LT, EQL, GT, QTN,
AT, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O,
P, Q, R, S, T, U, V, W, X, Y, Z, LBX, BSL, RBX, CAT, UND,
ACN, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o,
p, q, r, s, t, u, v, w, x, y, z, LBR, BAR, RBR, TLD, DEL
};
struct Saver {
const char *label;
void (*save)(std::ofstream &file, const SaveState &state);
void (*load)(std::ifstream &file, SaveState &state);
unsigned char labelsize;
};
static inline bool operator<(const Saver &l, const Saver &r) {
return std::strcmp(l.label, r.label) < 0;
}
static void put24(std::ofstream &file, const unsigned long data) {
file.put(data >> 16 & 0xFF);
file.put(data >> 8 & 0xFF);
file.put(data & 0xFF);
}
static void put32(std::ofstream &file, const unsigned long data) {
file.put(data >> 24 & 0xFF);
file.put(data >> 16 & 0xFF);
file.put(data >> 8 & 0xFF);
file.put(data & 0xFF);
}
static void write(std::ofstream &file, const unsigned char data) {
static const char inf[] = { 0x00, 0x00, 0x01 };
file.write(inf, sizeof(inf));
file.put(data & 0xFF);
}
static void write(std::ofstream &file, const unsigned short data) {
static const char inf[] = { 0x00, 0x00, 0x02 };
file.write(inf, sizeof(inf));
file.put(data >> 8 & 0xFF);
file.put(data & 0xFF);
}
static void write(std::ofstream &file, const unsigned long data) {
static const char inf[] = { 0x00, 0x00, 0x04 };
file.write(inf, sizeof(inf));
put32(file, data);
}
static inline void write(std::ofstream &file, const bool data) {
write(file, static_cast<unsigned char>(data));
}
static void write(std::ofstream &file, const unsigned char *data, const unsigned long sz) {
put24(file, sz);
file.write(reinterpret_cast<const char*>(data), sz);
}
static void write(std::ofstream &file, const bool *data, const unsigned long sz) {
put24(file, sz);
for (unsigned long i = 0; i < sz; ++i)
file.put(data[i]);
}
static unsigned long get24(std::ifstream &file) {
unsigned long tmp = file.get() & 0xFF;
tmp = tmp << 8 | (file.get() & 0xFF);
return tmp << 8 | (file.get() & 0xFF);
}
static unsigned long read(std::ifstream &file) {
unsigned long size = get24(file);
if (size > 4) {
file.ignore(size - 4);
size = 4;
}
unsigned long out = 0;
switch (size) {
case 4: out = (out | (file.get() & 0xFF)) << 8;
case 3: out = (out | (file.get() & 0xFF)) << 8;
case 2: out = (out | (file.get() & 0xFF)) << 8;
case 1: out = out | (file.get() & 0xFF);
}
return out;
}
static inline void read(std::ifstream &file, unsigned char &data) {
data = read(file) & 0xFF;
}
static inline void read(std::ifstream &file, unsigned short &data) {
data = read(file) & 0xFFFF;
}
static inline void read(std::ifstream &file, unsigned long &data) {
data = read(file);
}
static inline void read(std::ifstream &file, bool &data) {
data = read(file);
}
static void read(std::ifstream &file, unsigned char *data, unsigned long sz) {
const unsigned long size = get24(file);
if (size < sz)
sz = size;
file.read(reinterpret_cast<char*>(data), sz);
file.ignore(size - sz);
if (static_cast<unsigned char>(0x100)) {
for (unsigned long i = 0; i < sz; ++i)
data[i] &= 0xFF;
}
}
static void read(std::ifstream &file, bool *data, unsigned long sz) {
const unsigned long size = get24(file);
if (size < sz)
sz = size;
for (unsigned long i = 0; i < sz; ++i)
data[i] = file.get();
file.ignore(size - sz);
}
} // anon namespace
namespace gambatte {
class SaverList {
public:
typedef std::vector<Saver> list_t;
typedef list_t::const_iterator const_iterator;
private:
list_t list;
unsigned char maxLabelsize_;
public:
SaverList();
const_iterator begin() const { return list.begin(); }
const_iterator end() const { return list.end(); }
unsigned maxLabelsize() const { return maxLabelsize_; }
};
static void pushSaver(SaverList::list_t &list, const char *label,
void (*save)(std::ofstream &file, const SaveState &state),
void (*load)(std::ifstream &file, SaveState &state), unsigned labelsize) {
const Saver saver = { label, save, load, labelsize };
list.push_back(saver);
}
SaverList::SaverList() {
#define ADD(arg) do { \
struct Func { \
static void save(std::ofstream &file, const SaveState &state) { write(file, state.arg); } \
static void load(std::ifstream &file, SaveState &state) { read(file, state.arg); } \
}; \
\
pushSaver(list, label, Func::save, Func::load, sizeof label); \
} while (0)
#define ADDPTR(arg) do { \
struct Func { \
static void save(std::ofstream &file, const SaveState &state) { write(file, state.arg.get(), state.arg.getSz()); } \
static void load(std::ifstream &file, SaveState &state) { read(file, state.arg.ptr, state.arg.getSz()); } \
}; \
\
pushSaver(list, label, Func::save, Func::load, sizeof label); \
} while (0)
#define ADDARRAY(arg) do { \
struct Func { \
static void save(std::ofstream &file, const SaveState &state) { write(file, state.arg, sizeof(state.arg)); } \
static void load(std::ifstream &file, SaveState &state) { read(file, state.arg, sizeof(state.arg)); } \
}; \
\
pushSaver(list, label, Func::save, Func::load, sizeof label); \
} while (0)
{ static const char label[] = { c,c, NUL }; ADD(cpu.cycleCounter); }
{ static const char label[] = { p,c, NUL }; ADD(cpu.PC); }
{ static const char label[] = { s,p, NUL }; ADD(cpu.SP); }
{ static const char label[] = { a, NUL }; ADD(cpu.A); }
{ static const char label[] = { b, NUL }; ADD(cpu.B); }
{ static const char label[] = { c, NUL }; ADD(cpu.C); }
{ static const char label[] = { d, NUL }; ADD(cpu.D); }
{ static const char label[] = { e, NUL }; ADD(cpu.E); }
{ static const char label[] = { f, NUL }; ADD(cpu.F); }
{ static const char label[] = { h, NUL }; ADD(cpu.H); }
{ static const char label[] = { l, NUL }; ADD(cpu.L); }
{ static const char label[] = { s,k,i,p, NUL }; ADD(cpu.skip); }
{ static const char label[] = { h,a,l,t, NUL }; ADD(mem.halted); }
{ static const char label[] = { v,r,a,m, NUL }; ADDPTR(mem.vram); }
{ static const char label[] = { s,r,a,m, NUL }; ADDPTR(mem.sram); }
{ static const char label[] = { w,r,a,m, NUL }; ADDPTR(mem.wram); }
{ static const char label[] = { h,r,a,m, NUL }; ADDPTR(mem.ioamhram); }
{ static const char label[] = { l,d,i,v,u,p, NUL }; ADD(mem.divLastUpdate); }
{ static const char label[] = { l,t,i,m,a,u,p, NUL }; ADD(mem.timaLastUpdate); }
{ static const char label[] = { t,m,a,t,i,m,e, NUL }; ADD(mem.tmatime); }
{ static const char label[] = { s,e,r,i,a,l,t, NUL }; ADD(mem.nextSerialtime); }
{ static const char label[] = { l,o,d,m,a,u,p, NUL }; ADD(mem.lastOamDmaUpdate); }
{ static const char label[] = { m,i,n,i,n,t,t, NUL }; ADD(mem.minIntTime); }
{ static const char label[] = { u,n,h,a,l,t,t, NUL }; ADD(mem.unhaltTime); }
{ static const char label[] = { r,o,m,b,a,n,k, NUL }; ADD(mem.rombank); }
{ static const char label[] = { d,m,a,s,r,c, NUL }; ADD(mem.dmaSource); }
{ static const char label[] = { d,m,a,d,s,t, NUL }; ADD(mem.dmaDestination); }
{ static const char label[] = { r,a,m,b,a,n,k, NUL }; ADD(mem.rambank); }
{ static const char label[] = { o,d,m,a,p,o,s, NUL }; ADD(mem.oamDmaPos); }
{ static const char label[] = { i,m,e, NUL }; ADD(mem.IME); }
{ static const char label[] = { s,r,a,m,o,n, NUL }; ADD(mem.enableRam); }
{ static const char label[] = { r,a,m,b,m,o,d, NUL }; ADD(mem.rambankMode); }
{ static const char label[] = { h,d,m,a, NUL }; ADD(mem.hdmaTransfer); }
{ static const char label[] = { b,g,p, NUL }; ADDPTR(ppu.bgpData); }
{ static const char label[] = { o,b,j,p, NUL }; ADDPTR(ppu.objpData); }
{ static const char label[] = { s,p,o,s,b,u,f, NUL }; ADDPTR(ppu.oamReaderBuf); }
{ static const char label[] = { s,p,s,z,b,u,f, NUL }; ADDPTR(ppu.oamReaderSzbuf); }
{ static const char label[] = { s,p,a,t,t,r, NUL }; ADDARRAY(ppu.spAttribList); }
{ static const char label[] = { s,p,b,y,t,e,NO0, NUL }; ADDARRAY(ppu.spByte0List); }
{ static const char label[] = { s,p,b,y,t,e,NO1, NUL }; ADDARRAY(ppu.spByte1List); }
{ static const char label[] = { v,c,y,c,l,e,s, NUL }; ADD(ppu.videoCycles); }
{ static const char label[] = { e,d,M,NO0,t,i,m, NUL }; ADD(ppu.enableDisplayM0Time); }
{ static const char label[] = { m,NO0,t,i,m,e, NUL }; ADD(ppu.lastM0Time); }
{ static const char label[] = { n,m,NO0,i,r,q, NUL }; ADD(ppu.nextM0Irq); }
{ static const char label[] = { b,g,t,w, NUL }; ADD(ppu.tileword); }
{ static const char label[] = { b,g,n,t,w, NUL }; ADD(ppu.ntileword); }
{ static const char label[] = { w,i,n,y,p,o,s, NUL }; ADD(ppu.winYPos); }
{ static const char label[] = { x,p,o,s, NUL }; ADD(ppu.xpos); }
{ static const char label[] = { e,n,d,x, NUL }; ADD(ppu.endx); }
{ static const char label[] = { p,p,u,r,NO0, NUL }; ADD(ppu.reg0); }
{ static const char label[] = { p,p,u,r,NO1, NUL }; ADD(ppu.reg1); }
{ static const char label[] = { b,g,a,t,r,b, NUL }; ADD(ppu.attrib); }
{ static const char label[] = { b,g,n,a,t,r,b, NUL }; ADD(ppu.nattrib); }
{ static const char label[] = { p,p,u,s,t,a,t, NUL }; ADD(ppu.state); }
{ static const char label[] = { n,s,p,r,i,t,e, NUL }; ADD(ppu.nextSprite); }
{ static const char label[] = { c,s,p,r,i,t,e, NUL }; ADD(ppu.currentSprite); }
{ static const char label[] = { l,y,c, NUL }; ADD(ppu.lyc); }
{ static const char label[] = { m,NO0,l,y,c, NUL }; ADD(ppu.m0lyc); }
{ static const char label[] = { o,l,d,w,y, NUL }; ADD(ppu.oldWy); }
{ static const char label[] = { w,i,n,d,r,a,w, NUL }; ADD(ppu.winDrawState); }
{ static const char label[] = { w,s,c,x, NUL }; ADD(ppu.wscx); }
{ static const char label[] = { w,e,m,a,s,t,r, NUL }; ADD(ppu.weMaster); }
{ static const char label[] = { l,c,d,s,i,r,q, NUL }; ADD(ppu.pendingLcdstatIrq); }
{ static const char label[] = { s,p,u,c,n,t,r, NUL }; ADD(spu.cycleCounter); }
{ static const char label[] = { s,w,p,c,n,t,r, NUL }; ADD(spu.ch1.sweep.counter); }
{ static const char label[] = { s,w,p,s,h,d,w, NUL }; ADD(spu.ch1.sweep.shadow); }
{ static const char label[] = { s,w,p,n,e,g, NUL }; ADD(spu.ch1.sweep.negging); }
{ static const char label[] = { d,u,t,NO1,c,t,r, NUL }; ADD(spu.ch1.duty.nextPosUpdate); }
{ static const char label[] = { d,u,t,NO1,p,o,s, NUL }; ADD(spu.ch1.duty.pos); }
{ static const char label[] = { e,n,v,NO1,c,t,r, NUL }; ADD(spu.ch1.env.counter); }
{ static const char label[] = { e,n,v,NO1,v,o,l, NUL }; ADD(spu.ch1.env.volume); }
{ static const char label[] = { l,e,n,NO1,c,t,r, NUL }; ADD(spu.ch1.lcounter.counter); }
{ static const char label[] = { l,e,n,NO1,v,a,l, NUL }; ADD(spu.ch1.lcounter.lengthCounter); }
{ static const char label[] = { n,r,NO1,NO0, NUL }; ADD(spu.ch1.sweep.nr0); }
{ static const char label[] = { n,r,NO1,NO3, NUL }; ADD(spu.ch1.duty.nr3); }
{ static const char label[] = { n,r,NO1,NO4, NUL }; ADD(spu.ch1.nr4); }
{ static const char label[] = { c,NO1,m,a,s,t,r, NUL }; ADD(spu.ch1.master); }
{ static const char label[] = { d,u,t,NO2,c,t,r, NUL }; ADD(spu.ch2.duty.nextPosUpdate); }
{ static const char label[] = { d,u,t,NO2,p,o,s, NUL }; ADD(spu.ch2.duty.pos); }
{ static const char label[] = { e,n,v,NO2,c,t,r, NUL }; ADD(spu.ch2.env.counter); }
{ static const char label[] = { e,n,v,NO2,v,o,l, NUL }; ADD(spu.ch2.env.volume); }
{ static const char label[] = { l,e,n,NO2,c,t,r, NUL }; ADD(spu.ch2.lcounter.counter); }
{ static const char label[] = { l,e,n,NO2,v,a,l, NUL }; ADD(spu.ch2.lcounter.lengthCounter); }
{ static const char label[] = { n,r,NO2,NO3, NUL }; ADD(spu.ch2.duty.nr3); }
{ static const char label[] = { n,r,NO2,NO4, NUL }; ADD(spu.ch2.nr4); }
{ static const char label[] = { c,NO2,m,a,s,t,r, NUL }; ADD(spu.ch2.master); }
{ static const char label[] = { w,a,v,e,r,a,m, NUL }; ADDPTR(spu.ch3.waveRam); }
{ static const char label[] = { l,e,n,NO3,c,t,r, NUL }; ADD(spu.ch3.lcounter.counter); }
{ static const char label[] = { l,e,n,NO3,v,a,l, NUL }; ADD(spu.ch3.lcounter.lengthCounter); }
{ static const char label[] = { w,a,v,e,c,t,r, NUL }; ADD(spu.ch3.waveCounter); }
{ static const char label[] = { l,w,a,v,r,d,t, NUL }; ADD(spu.ch3.lastReadTime); }
{ static const char label[] = { w,a,v,e,p,o,s, NUL }; ADD(spu.ch3.wavePos); }
{ static const char label[] = { w,a,v,s,m,p,l, NUL }; ADD(spu.ch3.sampleBuf); }
{ static const char label[] = { n,r,NO3,NO3, NUL }; ADD(spu.ch3.nr3); }
{ static const char label[] = { n,r,NO3,NO4, NUL }; ADD(spu.ch3.nr4); }
{ static const char label[] = { c,NO3,m,a,s,t,r, NUL }; ADD(spu.ch3.master); }
{ static const char label[] = { l,f,s,r,c,t,r, NUL }; ADD(spu.ch4.lfsr.counter); }
{ static const char label[] = { l,f,s,r,r,e,g, NUL }; ADD(spu.ch4.lfsr.reg); }
{ static const char label[] = { e,n,v,NO4,c,t,r, NUL }; ADD(spu.ch4.env.counter); }
{ static const char label[] = { e,n,v,NO4,v,o,l, NUL }; ADD(spu.ch4.env.volume); }
{ static const char label[] = { l,e,n,NO4,c,t,r, NUL }; ADD(spu.ch4.lcounter.counter); }
{ static const char label[] = { l,e,n,NO4,v,a,l, NUL }; ADD(spu.ch4.lcounter.lengthCounter); }
{ static const char label[] = { n,r,NO4,NO4, NUL }; ADD(spu.ch4.nr4); }
{ static const char label[] = { c,NO4,m,a,s,t,r, NUL }; ADD(spu.ch4.master); }
{ static const char label[] = { r,t,c,b,a,s,e, NUL }; ADD(rtc.baseTime); }
{ static const char label[] = { r,t,c,h,a,l,t, NUL }; ADD(rtc.haltTime); }
{ static const char label[] = { r,t,c,d,h, NUL }; ADD(rtc.dataDh); }
{ static const char label[] = { r,t,c,d,l, NUL }; ADD(rtc.dataDl); }
{ static const char label[] = { r,t,c,h, NUL }; ADD(rtc.dataH); }
{ static const char label[] = { r,t,c,m, NUL }; ADD(rtc.dataM); }
{ static const char label[] = { r,t,c,s, NUL }; ADD(rtc.dataS); }
{ static const char label[] = { r,t,c,l,l,d, NUL }; ADD(rtc.lastLatchData); }
#undef ADD
#undef ADDPTR
#undef ADDARRAY
list.resize(list.size());
std::sort(list.begin(), list.end());
maxLabelsize_ = 0;
for (std::size_t i = 0; i < list.size(); ++i) {
if (list[i].labelsize > maxLabelsize_)
maxLabelsize_ = list[i].labelsize;
}
}
}
namespace {
struct PxlSum { unsigned long rb, g; };
static void addPxlPairs(PxlSum *const sum, const uint_least32_t *const p) {
sum[0].rb += (p[0] & 0xFF00FF) + (p[3] & 0xFF00FF);
sum[0].g += (p[0] & 0x00FF00) + (p[3] & 0x00FF00);
sum[1].rb += (p[1] & 0xFF00FF) + (p[2] & 0xFF00FF);
sum[1].g += (p[1] & 0x00FF00) + (p[2] & 0x00FF00);
}
static void blendPxlPairs(PxlSum *const dst, const PxlSum *const sums) {
dst->rb = sums[1].rb * 8 + (sums[0].rb - sums[1].rb) * 3;
dst->g = sums[1].g * 8 + (sums[0].g - sums[1].g ) * 3;
}
static void writeSnapShot(std::ofstream &file, const uint_least32_t *pixels, const int pitch) {
put24(file, pixels ? StateSaver::SS_WIDTH * StateSaver::SS_HEIGHT * sizeof(uint_least32_t) : 0);
if (pixels) {
uint_least32_t buf[StateSaver::SS_WIDTH];
for (unsigned h = StateSaver::SS_HEIGHT; h--;) {
for (unsigned x = 0; x < StateSaver::SS_WIDTH; ++x) {
const uint_least32_t *const p = pixels + x * StateSaver::SS_DIV;
PxlSum pxlsum[4] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
addPxlPairs(pxlsum , p );
addPxlPairs(pxlsum + 2, p + pitch );
addPxlPairs(pxlsum + 2, p + pitch * 2);
addPxlPairs(pxlsum , p + pitch * 3);
blendPxlPairs(pxlsum , pxlsum );
blendPxlPairs(pxlsum + 1, pxlsum + 2);
blendPxlPairs(pxlsum , pxlsum );
buf[x] = ((pxlsum[0].rb & 0xFF00FF00U) | (pxlsum[0].g & 0x00FF0000U)) >> 8;
}
file.write(reinterpret_cast<const char*>(buf), sizeof(buf));
pixels += pitch * StateSaver::SS_DIV;
}
}
}
static SaverList list;
} // anon namespace
namespace gambatte {
bool StateSaver::saveState(const SaveState &state,
const uint_least32_t *const videoBuf,
const int pitch, const std::string &filename) {
std::ofstream file(filename.c_str(), std::ios_base::binary);
if (file.fail())
return false;
{ static const char ver[] = { 0, 1 }; file.write(ver, sizeof(ver)); }
writeSnapShot(file, videoBuf, pitch);
for (SaverList::const_iterator it = list.begin(); it != list.end(); ++it) {
file.write(it->label, it->labelsize);
(*it->save)(file, state);
}
return !file.fail();
}
bool StateSaver::loadState(SaveState &state, const std::string &filename) {
std::ifstream file(filename.c_str(), std::ios_base::binary);
if (file.fail() || file.get() != 0)
return false;
file.ignore();
file.ignore(get24(file));
const Array<char> labelbuf(list.maxLabelsize());
const Saver labelbufSaver = { labelbuf, 0, 0, list.maxLabelsize() };
SaverList::const_iterator done = list.begin();
while (file.good() && done != list.end()) {
file.getline(labelbuf, list.maxLabelsize(), NUL);
SaverList::const_iterator it = done;
if (std::strcmp(labelbuf, it->label)) {
it = std::lower_bound(it + 1, list.end(), labelbufSaver);
if (it == list.end() || std::strcmp(labelbuf, it->label)) {
file.ignore(get24(file));
continue;
}
} else
++done;
(*it->load)(file, state);
}
state.cpu.cycleCounter &= 0x7FFFFFFF;
state.spu.cycleCounter &= 0x7FFFFFFF;
return true;
}
}

View File

@ -0,0 +1,45 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef STATESAVER_H
#define STATESAVER_H
#include "gbint.h"
#include <string>
namespace gambatte {
struct SaveState;
class StateSaver {
StateSaver();
public:
enum { SS_SHIFT = 2 };
enum { SS_DIV = 1 << 2 };
enum { SS_WIDTH = 160 >> SS_SHIFT };
enum { SS_HEIGHT = 144 >> SS_SHIFT };
static bool saveState(const SaveState &state,
const uint_least32_t *videoBuf, int pitch, const std::string &filename);
static bool loadState(SaveState &state, const std::string &filename);
};
}
#endif

171
libgambatte/src/tima.cpp Normal file
View File

@ -0,0 +1,171 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "tima.h"
#include "savestate.h"
static const unsigned char timaClock[4] = { 10, 4, 6, 8 };
namespace gambatte {
Tima::Tima() :
lastUpdate_(0),
tmatime_(DISABLED_TIME),
tima_(0),
tma_(0),
tac_(0)
{}
void Tima::saveState(SaveState &state) const {
state.mem.timaLastUpdate = lastUpdate_;
state.mem.tmatime = tmatime_;
}
void Tima::loadState(const SaveState &state, const TimaInterruptRequester timaIrq) {
lastUpdate_ = state.mem.timaLastUpdate;
tmatime_ = state.mem.tmatime;
tima_ = state.mem.ioamhram.get()[0x105];
tma_ = state.mem.ioamhram.get()[0x106];
tac_ = state.mem.ioamhram.get()[0x107];
timaIrq.setNextIrqEventTime((tac_ & 4)
?
(tmatime_ != DISABLED_TIME && tmatime_ > state.cpu.cycleCounter
? tmatime_
: lastUpdate_ + ((256u - tima_) << timaClock[tac_ & 3]) + 3)
:
static_cast<unsigned long>(DISABLED_TIME)
);
}
void Tima::resetCc(const unsigned long oldCc, const unsigned long newCc, const TimaInterruptRequester timaIrq) {
const unsigned long dec = oldCc - newCc;
if (tac_ & 0x04) {
updateIrq(oldCc, timaIrq);
updateTima(oldCc);
lastUpdate_ -= dec;
timaIrq.setNextIrqEventTime(timaIrq.nextIrqEventTime() - dec);
if (tmatime_ != DISABLED_TIME)
tmatime_ -= dec;
}
}
void Tima::updateTima(const unsigned long cycleCounter) {
const unsigned long ticks = (cycleCounter - lastUpdate_) >> timaClock[tac_ & 3];
lastUpdate_ += ticks << timaClock[tac_ & 3];
if (cycleCounter >= tmatime_) {
if (cycleCounter >= tmatime_ + 4)
tmatime_ = DISABLED_TIME;
tima_ = tma_;
}
unsigned long tmp = tima_ + ticks;
while (tmp > 0x100)
tmp -= 0x100 - tma_;
if (tmp == 0x100) {
tmp = 0;
tmatime_ = lastUpdate_ + 3;
if (cycleCounter >= tmatime_) {
if (cycleCounter >= tmatime_ + 4)
tmatime_ = DISABLED_TIME;
tmp = tma_;
}
}
tima_ = tmp;
}
void Tima::setTima(const unsigned data, const unsigned long cycleCounter, const TimaInterruptRequester timaIrq) {
if (tac_ & 0x04) {
updateIrq(cycleCounter, timaIrq);
updateTima(cycleCounter);
if (tmatime_ - cycleCounter < 4)
tmatime_ = DISABLED_TIME;
timaIrq.setNextIrqEventTime(lastUpdate_ + ((256u - data) << timaClock[tac_ & 3]) + 3);
}
tima_ = data;
}
void Tima::setTma(const unsigned data, const unsigned long cycleCounter, const TimaInterruptRequester timaIrq) {
if (tac_ & 0x04) {
updateIrq(cycleCounter, timaIrq);
updateTima(cycleCounter);
}
tma_ = data;
}
void Tima::setTac(const unsigned data, const unsigned long cycleCounter, const TimaInterruptRequester timaIrq) {
if (tac_ ^ data) {
unsigned long nextIrqEventTime = timaIrq.nextIrqEventTime();
if (tac_ & 0x04) {
updateIrq(cycleCounter, timaIrq);
updateTima(cycleCounter);
lastUpdate_ -= (1u << (timaClock[tac_ & 3] - 1)) + 3;
tmatime_ -= (1u << (timaClock[tac_ & 3] - 1)) + 3;
nextIrqEventTime -= (1u << (timaClock[tac_ & 3] - 1)) + 3;
if (cycleCounter >= nextIrqEventTime)
timaIrq.flagIrq();
updateTima(cycleCounter);
tmatime_ = DISABLED_TIME;
nextIrqEventTime = DISABLED_TIME;
}
if (data & 4) {
lastUpdate_ = (cycleCounter >> timaClock[data & 3]) << timaClock[data & 3];
nextIrqEventTime = lastUpdate_ + ((256u - tima_) << timaClock[data & 3]) + 3;
}
timaIrq.setNextIrqEventTime(nextIrqEventTime);
}
tac_ = data;
}
unsigned Tima::tima(unsigned long cycleCounter) {
if (tac_ & 0x04)
updateTima(cycleCounter);
return tima_;
}
void Tima::doIrqEvent(const TimaInterruptRequester timaIrq) {
timaIrq.flagIrq();
timaIrq.setNextIrqEventTime(timaIrq.nextIrqEventTime() + ((256u - tma_) << timaClock[tac_ & 3]));
}
}

67
libgambatte/src/tima.h Normal file
View File

@ -0,0 +1,67 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef TIMA_H
#define TIMA_H
#include "interruptrequester.h"
namespace gambatte {
class TimaInterruptRequester {
InterruptRequester &intreq;
public:
explicit TimaInterruptRequester(InterruptRequester &intreq) : intreq(intreq) {}
void flagIrq() const { intreq.flagIrq(4); }
unsigned long nextIrqEventTime() const { return intreq.eventTime(TIMA); }
void setNextIrqEventTime(const unsigned long time) const { intreq.setEventTime<TIMA>(time); }
};
class Tima {
unsigned long lastUpdate_;
unsigned long tmatime_;
unsigned char tima_;
unsigned char tma_;
unsigned char tac_;
void updateIrq(const unsigned long cc, const TimaInterruptRequester timaIrq) {
while (cc >= timaIrq.nextIrqEventTime())
doIrqEvent(timaIrq);
}
void updateTima(unsigned long cc);
public:
Tima();
void saveState(SaveState &) const;
void loadState(const SaveState &, TimaInterruptRequester timaIrq);
void resetCc(unsigned long oldCc, unsigned long newCc, TimaInterruptRequester timaIrq);
void setTima(unsigned tima, unsigned long cc, TimaInterruptRequester timaIrq);
void setTma(unsigned tma, unsigned long cc, TimaInterruptRequester timaIrq);
void setTac(unsigned tac, unsigned long cc, TimaInterruptRequester timaIrq);
unsigned tima(unsigned long cc);
void doIrqEvent(TimaInterruptRequester timaIrq);
};
}
#endif

807
libgambatte/src/video.cpp Normal file
View File

@ -0,0 +1,807 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "video.h"
#include "savestate.h"
#include <cstring>
#include <algorithm>
namespace gambatte {
void LCD::setDmgPalette(unsigned long *const palette, const unsigned long *const dmgColors, const unsigned data) {
palette[0] = dmgColors[data & 3];
palette[1] = dmgColors[data >> 2 & 3];
palette[2] = dmgColors[data >> 4 & 3];
palette[3] = dmgColors[data >> 6 & 3];
}
static unsigned long gbcToRgb32(const unsigned bgr15) {
const unsigned long r = bgr15 & 0x1F;
const unsigned long g = bgr15 >> 5 & 0x1F;
const unsigned long b = bgr15 >> 10 & 0x1F;
return ((r * 13 + g * 2 + b) >> 1) << 16 | (g * 3 + b) << 9 | (r * 3 + g * 2 + b * 11) >> 1;
}
/*static unsigned long gbcToRgb16(const unsigned bgr15) {
const unsigned r = bgr15 & 0x1F;
const unsigned g = bgr15 >> 5 & 0x1F;
const unsigned b = bgr15 >> 10 & 0x1F;
return (((r * 13 + g * 2 + b + 8) << 7) & 0xF800) | ((g * 3 + b + 1) >> 1) << 5 | ((r * 3 + g * 2 + b * 11 + 8) >> 4);
}
static unsigned long gbcToUyvy(const unsigned bgr15) {
const unsigned r5 = bgr15 & 0x1F;
const unsigned g5 = bgr15 >> 5 & 0x1F;
const unsigned b5 = bgr15 >> 10 & 0x1F;
// y = (r5 * 926151 + g5 * 1723530 + b5 * 854319) / 510000 + 16;
// u = (b5 * 397544 - r5 * 68824 - g5 * 328720) / 225930 + 128;
// v = (r5 * 491176 - g5 * 328720 - b5 * 162456) / 178755 + 128;
const unsigned long y = (r5 * 116 + g5 * 216 + b5 * 107 + 16 * 64 + 32) >> 6;
const unsigned long u = (b5 * 225 - r5 * 39 - g5 * 186 + 128 * 128 + 64) >> 7;
const unsigned long v = (r5 * 176 - g5 * 118 - b5 * 58 + 128 * 64 + 32) >> 6;
#ifdef WORDS_BIGENDIAN
return u << 24 | y << 16 | v << 8 | y;
#else
return y << 24 | v << 16 | y << 8 | u;
#endif
}*/
LCD::LCD(const unsigned char *const oamram, const unsigned char *const vram, const VideoInterruptRequester memEventRequester) :
ppu(nextM0Time_, oamram, vram),
eventTimes_(memEventRequester),
statReg(0),
m2IrqStatReg_(0),
m1IrqStatReg_(0)
{
std::memset( bgpData, 0, sizeof bgpData);
std::memset(objpData, 0, sizeof objpData);
for (std::size_t i = 0; i < sizeof(dmgColorsRgb32) / sizeof(dmgColorsRgb32[0]); ++i)
setDmgPaletteColor(i, (3 - (i & 3)) * 85 * 0x010101);
reset(oamram, vram, false);
setVideoBuffer(0, 160);
}
void LCD::reset(const unsigned char *const oamram, const unsigned char *vram, const bool cgb) {
ppu.reset(oamram, vram, cgb);
lycIrq.setCgb(cgb);
refreshPalettes();
}
static unsigned long mode2IrqSchedule(const unsigned statReg, const LyCounter &lyCounter, const unsigned long cycleCounter) {
if (!(statReg & 0x20))
return DISABLED_TIME;
unsigned next = lyCounter.time() - cycleCounter;
if (lyCounter.ly() >= 143 || (lyCounter.ly() == 142 && next <= 4) || (statReg & 0x08)) {
next += (153u - lyCounter.ly()) * lyCounter.lineTime();
} else {
if (next <= 4)
next += lyCounter.lineTime();
next -= 4;
}
return cycleCounter + next;
}
static inline unsigned long m0IrqTimeFromXpos166Time(const unsigned long xpos166Time, const bool cgb, const bool ds) {
return xpos166Time + cgb - ds;
}
static inline unsigned long hdmaTimeFromM0Time(const unsigned long m0Time, const bool ds) {
return m0Time + 1 - ds;
}
static unsigned long nextHdmaTime(const unsigned long lastM0Time,
const unsigned long nextM0Time, const unsigned long cycleCounter, const bool ds) {
return cycleCounter < hdmaTimeFromM0Time(lastM0Time, ds)
? hdmaTimeFromM0Time(lastM0Time, ds)
: hdmaTimeFromM0Time(nextM0Time, ds);
}
void LCD::setStatePtrs(SaveState &state) {
state.ppu.bgpData.set( bgpData, sizeof bgpData);
state.ppu.objpData.set(objpData, sizeof objpData);
ppu.setStatePtrs(state);
}
void LCD::saveState(SaveState &state) const {
state.mem.hdmaTransfer = hdmaIsEnabled();
state.ppu.nextM0Irq = eventTimes_(MODE0_IRQ) - ppu.now();
state.ppu.pendingLcdstatIrq = eventTimes_(ONESHOT_LCDSTATIRQ) != DISABLED_TIME;
lycIrq.saveState(state);
m0Irq_.saveState(state);
ppu.saveState(state);
}
void LCD::loadState(const SaveState &state, const unsigned char *const oamram) {
statReg = state.mem.ioamhram.get()[0x141];
m2IrqStatReg_ = statReg;
m1IrqStatReg_ = statReg;
ppu.loadState(state, oamram);
lycIrq.loadState(state);
m0Irq_.loadState(state);
if (ppu.lcdc() & 0x80) {
nextM0Time_.predictNextM0Time(ppu);
lycIrq.reschedule(ppu.lyCounter(), ppu.now());
eventTimes_.setm<ONESHOT_LCDSTATIRQ>(state.ppu.pendingLcdstatIrq
? ppu.now() + 1 : static_cast<unsigned long>(DISABLED_TIME));
eventTimes_.setm<ONESHOT_UPDATEWY2>(state.ppu.oldWy != state.mem.ioamhram.get()[0x14A]
? ppu.now() + 1 : static_cast<unsigned long>(DISABLED_TIME));
eventTimes_.set<LY_COUNT>(ppu.lyCounter().time());
eventTimes_.setm<SPRITE_MAP>(SpriteMapper::schedule(ppu.lyCounter(), ppu.now()));
eventTimes_.setm<LYC_IRQ>(lycIrq.time());
eventTimes_.setm<MODE1_IRQ>(ppu.lyCounter().nextFrameCycle(144 * 456, ppu.now()));
eventTimes_.setm<MODE2_IRQ>(mode2IrqSchedule(statReg, ppu.lyCounter(), ppu.now()));
eventTimes_.setm<MODE0_IRQ>((statReg & 0x08) ? ppu.now() + state.ppu.nextM0Irq : static_cast<unsigned long>(DISABLED_TIME));
eventTimes_.setm<HDMA_REQ>(state.mem.hdmaTransfer
? nextHdmaTime(ppu.lastM0Time(), nextM0Time_.predictedNextM0Time(), ppu.now(), isDoubleSpeed())
: static_cast<unsigned long>(DISABLED_TIME));
} else for (int i = 0; i < NUM_MEM_EVENTS; ++i)
eventTimes_.set(static_cast<MemEvent>(i), DISABLED_TIME);
refreshPalettes();
}
void LCD::refreshPalettes() {
if (ppu.cgb()) {
for (unsigned i = 0; i < 8 * 8; i += 2) {
ppu.bgPalette()[i >> 1] = gbcToRgb32( bgpData[i] | bgpData[i + 1] << 8);
ppu.spPalette()[i >> 1] = gbcToRgb32(objpData[i] | objpData[i + 1] << 8);
}
} else {
setDmgPalette(ppu.bgPalette() , dmgColorsRgb32 , bgpData[0]);
setDmgPalette(ppu.spPalette() , dmgColorsRgb32 + 4, objpData[0]);
setDmgPalette(ppu.spPalette() + 4, dmgColorsRgb32 + 8, objpData[1]);
}
}
namespace {
template<class Blend>
static void blitOsdElement(uint_least32_t *d,
const uint_least32_t *s, const unsigned width, unsigned h, const int dpitch, Blend blend)
{
while (h--) {
for (unsigned w = width; w--;) {
if (*s != 0xFFFFFFFF)
*d = blend(*s, *d);
++d;
++s;
}
d += dpitch - static_cast<int>(width);
}
}
template<unsigned weight>
struct Blend {
enum { SW = weight - 1 };
enum { LOWMASK = SW * 0x010101ul };
uint_least32_t operator()(const uint_least32_t s, const uint_least32_t d) const {
return (s * SW + d - (((s & LOWMASK) * SW + (d & LOWMASK)) & LOWMASK)) / weight;
}
};
template<typename T>
static void clear(T *buf, const unsigned long color, const int dpitch) {
unsigned lines = 144;
while (lines--) {
std::fill_n(buf, 160, color);
buf += dpitch;
}
}
}
void LCD::updateScreen(const bool blanklcd, const unsigned long cycleCounter) {
update(cycleCounter);
if (blanklcd && ppu.frameBuf().fb()) {
const unsigned long color = ppu.cgb() ? gbcToRgb32(0xFFFF) : dmgColorsRgb32[0];
clear(ppu.frameBuf().fb(), color, ppu.frameBuf().pitch());
}
if (ppu.frameBuf().fb() && osdElement.get()) {
if (const uint_least32_t *const s = osdElement->update()) {
uint_least32_t *const d = ppu.frameBuf().fb()
+ static_cast<long>(osdElement->y()) * ppu.frameBuf().pitch() + osdElement->x();
switch (osdElement->opacity()) {
case OsdElement::SEVEN_EIGHTHS:
blitOsdElement(d, s, osdElement->w(), osdElement->h(), ppu.frameBuf().pitch(), Blend<8>()); break;
case OsdElement::THREE_FOURTHS:
blitOsdElement(d, s, osdElement->w(), osdElement->h(), ppu.frameBuf().pitch(), Blend<4>()); break;
}
} else
osdElement.reset();
}
}
void LCD::resetCc(const unsigned long oldCc, const unsigned long newCc) {
update(oldCc);
ppu.resetCc(oldCc, newCc);
if (ppu.lcdc() & 0x80) {
const unsigned long dec = oldCc - newCc;
nextM0Time_.invalidatePredictedNextM0Time();
lycIrq.reschedule(ppu.lyCounter(), newCc);
for (int i = 0; i < NUM_MEM_EVENTS; ++i) {
if (eventTimes_(static_cast<MemEvent>(i)) != DISABLED_TIME)
eventTimes_.set(static_cast<MemEvent>(i), eventTimes_(static_cast<MemEvent>(i)) - dec);
}
eventTimes_.set<LY_COUNT>(ppu.lyCounter().time());
}
}
void LCD::speedChange(const unsigned long cycleCounter) {
update(cycleCounter);
ppu.speedChange(cycleCounter);
if (ppu.lcdc() & 0x80) {
nextM0Time_.predictNextM0Time(ppu);
lycIrq.reschedule(ppu.lyCounter(), cycleCounter);
eventTimes_.set<LY_COUNT>(ppu.lyCounter().time());
eventTimes_.setm<SPRITE_MAP>(SpriteMapper::schedule(ppu.lyCounter(), cycleCounter));
eventTimes_.setm<LYC_IRQ>(lycIrq.time());
eventTimes_.setm<MODE1_IRQ>(ppu.lyCounter().nextFrameCycle(144 * 456, cycleCounter));
eventTimes_.setm<MODE2_IRQ>(mode2IrqSchedule(statReg, ppu.lyCounter(), cycleCounter));
if (eventTimes_(MODE0_IRQ) != DISABLED_TIME && eventTimes_(MODE0_IRQ) - cycleCounter > 1)
eventTimes_.setm<MODE0_IRQ>(m0IrqTimeFromXpos166Time(ppu.predictedNextXposTime(166), ppu.cgb(), isDoubleSpeed()));
if (hdmaIsEnabled() && eventTimes_(HDMA_REQ) - cycleCounter > 1) {
eventTimes_.setm<HDMA_REQ>(nextHdmaTime(ppu.lastM0Time(),
nextM0Time_.predictedNextM0Time(), cycleCounter, isDoubleSpeed()));
}
}
}
static inline unsigned long m0TimeOfCurrentLine(const unsigned long nextLyTime,
const unsigned long lastM0Time, const unsigned long nextM0Time)
{
return nextM0Time < nextLyTime ? nextM0Time : lastM0Time;
}
unsigned long LCD::m0TimeOfCurrentLine(const unsigned long cc) {
if (cc >= nextM0Time_.predictedNextM0Time()) {
update(cc);
nextM0Time_.predictNextM0Time(ppu);
}
return gambatte::m0TimeOfCurrentLine(ppu.lyCounter().time(), ppu.lastM0Time(), nextM0Time_.predictedNextM0Time());
}
static bool isHdmaPeriod(const LyCounter &lyCounter,
const unsigned long m0TimeOfCurrentLy, const unsigned long cycleCounter)
{
const unsigned timeToNextLy = lyCounter.time() - cycleCounter;
return /*(ppu.lcdc & 0x80) && */lyCounter.ly() < 144 && timeToNextLy > 4
&& cycleCounter >= hdmaTimeFromM0Time(m0TimeOfCurrentLy, lyCounter.isDoubleSpeed());
}
void LCD::enableHdma(const unsigned long cycleCounter) {
if (cycleCounter >= nextM0Time_.predictedNextM0Time()) {
update(cycleCounter);
nextM0Time_.predictNextM0Time(ppu);
} else if (cycleCounter >= eventTimes_.nextEventTime())
update(cycleCounter);
if (isHdmaPeriod(ppu.lyCounter(),
gambatte::m0TimeOfCurrentLine(ppu.lyCounter().time(),
ppu.lastM0Time(), nextM0Time_.predictedNextM0Time()), cycleCounter)) {
eventTimes_.flagHdmaReq();
}
eventTimes_.setm<HDMA_REQ>(nextHdmaTime(ppu.lastM0Time(), nextM0Time_.predictedNextM0Time(), cycleCounter, isDoubleSpeed()));
}
void LCD::disableHdma(const unsigned long cycleCounter) {
if (cycleCounter >= eventTimes_.nextEventTime())
update(cycleCounter);
eventTimes_.setm<HDMA_REQ>(DISABLED_TIME);
}
bool LCD::vramAccessible(const unsigned long cycleCounter) {
if (cycleCounter >= eventTimes_.nextEventTime())
update(cycleCounter);
return !(ppu.lcdc() & 0x80) || ppu.lyCounter().ly() >= 144
|| ppu.lyCounter().lineCycles(cycleCounter) < 80U
|| cycleCounter + isDoubleSpeed() - ppu.cgb() + 2 >= m0TimeOfCurrentLine(cycleCounter);
}
bool LCD::cgbpAccessible(const unsigned long cycleCounter) {
if (cycleCounter >= eventTimes_.nextEventTime())
update(cycleCounter);
return !(ppu.lcdc() & 0x80) || ppu.lyCounter().ly() >= 144
|| ppu.lyCounter().lineCycles(cycleCounter) < 80U + isDoubleSpeed()
|| cycleCounter >= m0TimeOfCurrentLine(cycleCounter) + 3 - isDoubleSpeed();
}
static void doCgbColorChange(unsigned char *const pdata,
unsigned long *const palette, unsigned index, const unsigned data) {
pdata[index] = data;
index >>= 1;
palette[index] = gbcToRgb32(pdata[index << 1] | pdata[(index << 1) + 1] << 8);
}
void LCD::doCgbBgColorChange(unsigned index, const unsigned data, const unsigned long cycleCounter) {
if (cgbpAccessible(cycleCounter)) {
update(cycleCounter);
doCgbColorChange(bgpData, ppu.bgPalette(), index, data);
}
}
void LCD::doCgbSpColorChange(unsigned index, const unsigned data, const unsigned long cycleCounter) {
if (cgbpAccessible(cycleCounter)) {
update(cycleCounter);
doCgbColorChange(objpData, ppu.spPalette(), index, data);
}
}
bool LCD::oamReadable(const unsigned long cycleCounter) {
if (!(ppu.lcdc() & 0x80) || ppu.inactivePeriodAfterDisplayEnable(cycleCounter))
return true;
if (cycleCounter >= eventTimes_.nextEventTime())
update(cycleCounter);
if (ppu.lyCounter().lineCycles(cycleCounter) + 4 - ppu.lyCounter().isDoubleSpeed() * 3u >= 456)
return ppu.lyCounter().ly() >= 144-1 && ppu.lyCounter().ly() != 153;
return ppu.lyCounter().ly() >= 144 || cycleCounter + isDoubleSpeed() - ppu.cgb() + 2 >= m0TimeOfCurrentLine(cycleCounter);
}
bool LCD::oamWritable(const unsigned long cycleCounter) {
if (!(ppu.lcdc() & 0x80) || ppu.inactivePeriodAfterDisplayEnable(cycleCounter))
return true;
if (cycleCounter >= eventTimes_.nextEventTime())
update(cycleCounter);
if (ppu.lyCounter().lineCycles(cycleCounter) + 3 + ppu.cgb() - ppu.lyCounter().isDoubleSpeed() * 2u >= 456)
return ppu.lyCounter().ly() >= 144-1 && ppu.lyCounter().ly() != 153;
return ppu.lyCounter().ly() >= 144 || cycleCounter + isDoubleSpeed() - ppu.cgb() + 2 >= m0TimeOfCurrentLine(cycleCounter);
}
void LCD::mode3CyclesChange() {
nextM0Time_.invalidatePredictedNextM0Time();
if (eventTimes_(MODE0_IRQ) != DISABLED_TIME
&& eventTimes_(MODE0_IRQ) > m0IrqTimeFromXpos166Time(ppu.now(), ppu.cgb(), isDoubleSpeed())) {
eventTimes_.setm<MODE0_IRQ>(m0IrqTimeFromXpos166Time(ppu.predictedNextXposTime(166), ppu.cgb(), isDoubleSpeed()));
}
if (eventTimes_(HDMA_REQ) != DISABLED_TIME
&& eventTimes_(HDMA_REQ) > hdmaTimeFromM0Time(ppu.lastM0Time(), isDoubleSpeed())) {
nextM0Time_.predictNextM0Time(ppu);
eventTimes_.setm<HDMA_REQ>(hdmaTimeFromM0Time(nextM0Time_.predictedNextM0Time(), isDoubleSpeed()));
}
}
void LCD::wxChange(const unsigned newValue, const unsigned long cycleCounter) {
update(cycleCounter + isDoubleSpeed() + 1);
ppu.setWx(newValue);
mode3CyclesChange();
}
void LCD::wyChange(const unsigned newValue, const unsigned long cycleCounter) {
update(cycleCounter + 1);
ppu.setWy(newValue);
// mode3CyclesChange(); // should be safe to wait until after wy2 delay, because no mode3 events are close to when wy1 is read.
// wy2 is a delayed version of wy. really just slowness of ly == wy comparison.
if (ppu.cgb() && (ppu.lcdc() & 0x80)) {
eventTimes_.setm<ONESHOT_UPDATEWY2>(cycleCounter + 5);
} else {
update(cycleCounter + 2);
ppu.updateWy2();
mode3CyclesChange();
}
}
void LCD::scxChange(const unsigned newScx, const unsigned long cycleCounter) {
update(cycleCounter + ppu.cgb() + isDoubleSpeed());
ppu.setScx(newScx);
mode3CyclesChange();
}
void LCD::scyChange(const unsigned newValue, const unsigned long cycleCounter) {
update(cycleCounter + ppu.cgb() + isDoubleSpeed());
ppu.setScy(newValue);
}
void LCD::oamChange(const unsigned long cycleCounter) {
if (ppu.lcdc() & 0x80) {
update(cycleCounter);
ppu.oamChange(cycleCounter);
eventTimes_.setm<SPRITE_MAP>(SpriteMapper::schedule(ppu.lyCounter(), cycleCounter));
}
}
void LCD::oamChange(const unsigned char *const oamram, const unsigned long cycleCounter) {
update(cycleCounter);
ppu.oamChange(oamram, cycleCounter);
if (ppu.lcdc() & 0x80)
eventTimes_.setm<SPRITE_MAP>(SpriteMapper::schedule(ppu.lyCounter(), cycleCounter));
}
void LCD::lcdcChange(const unsigned data, const unsigned long cycleCounter) {
const unsigned oldLcdc = ppu.lcdc();
update(cycleCounter);
if ((oldLcdc ^ data) & 0x80) {
ppu.setLcdc(data, cycleCounter);
if (data & 0x80) {
lycIrq.lcdReset();
m0Irq_.lcdReset(statReg, lycIrq.lycReg());
if (lycIrq.lycReg() == 0 && (statReg & 0x40))
eventTimes_.flagIrq(2);
nextM0Time_.predictNextM0Time(ppu);
lycIrq.reschedule(ppu.lyCounter(), cycleCounter);
eventTimes_.set<LY_COUNT>(ppu.lyCounter().time());
eventTimes_.setm<SPRITE_MAP>(SpriteMapper::schedule(ppu.lyCounter(), cycleCounter));
eventTimes_.setm<LYC_IRQ>(lycIrq.time());
eventTimes_.setm<MODE1_IRQ>(ppu.lyCounter().nextFrameCycle(144 * 456, cycleCounter));
eventTimes_.setm<MODE2_IRQ>(mode2IrqSchedule(statReg, ppu.lyCounter(), cycleCounter));
if (statReg & 0x08)
eventTimes_.setm<MODE0_IRQ>(m0IrqTimeFromXpos166Time(ppu.predictedNextXposTime(166), ppu.cgb(), isDoubleSpeed()));
if (hdmaIsEnabled()) {
eventTimes_.setm<HDMA_REQ>(nextHdmaTime(ppu.lastM0Time(),
nextM0Time_.predictedNextM0Time(), cycleCounter, isDoubleSpeed()));
}
} else for (int i = 0; i < NUM_MEM_EVENTS; ++i)
eventTimes_.set(static_cast<MemEvent>(i), DISABLED_TIME);
} else if (data & 0x80) {
if (ppu.cgb()) {
ppu.setLcdc((oldLcdc & ~0x14) | (data & 0x14), cycleCounter);
if ((oldLcdc ^ data) & 0x04)
eventTimes_.setm<SPRITE_MAP>(SpriteMapper::schedule(ppu.lyCounter(), cycleCounter));
update(cycleCounter + isDoubleSpeed() + 1);
ppu.setLcdc(data, cycleCounter + isDoubleSpeed() + 1);
if ((oldLcdc ^ data) & 0x20)
mode3CyclesChange();
} else {
ppu.setLcdc(data, cycleCounter);
if ((oldLcdc ^ data) & 0x04)
eventTimes_.setm<SPRITE_MAP>(SpriteMapper::schedule(ppu.lyCounter(), cycleCounter));
if ((oldLcdc ^ data) & 0x22)
mode3CyclesChange();
}
} else
ppu.setLcdc(data, cycleCounter);
}
namespace {
struct LyCnt {
unsigned ly; int timeToNextLy;
LyCnt(unsigned ly, int timeToNextLy) : ly(ly), timeToNextLy(timeToNextLy) {}
};
static LyCnt const getLycCmpLy(LyCounter const &lyCounter, unsigned long cc) {
unsigned ly = lyCounter.ly();
int timeToNextLy = lyCounter.time() - cc;
if (ly == 153) {
if (timeToNextLy - (448 << lyCounter.isDoubleSpeed()) > 0) {
timeToNextLy -= (448 << lyCounter.isDoubleSpeed());
} else {
ly = 0;
timeToNextLy += lyCounter.lineTime();
}
}
return LyCnt(ly, timeToNextLy);
}
}
void LCD::lcdstatChange(unsigned const data, unsigned long const cycleCounter) {
if (cycleCounter >= eventTimes_.nextEventTime())
update(cycleCounter);
unsigned const old = statReg;
statReg = data;
lycIrq.statRegChange(data, ppu.lyCounter(), cycleCounter);
if (ppu.lcdc() & 0x80) {
int const timeToNextLy = ppu.lyCounter().time() - cycleCounter;
LyCnt const lycCmp = getLycCmpLy(ppu.lyCounter(), cycleCounter);
if (!ppu.cgb()) {
if (ppu.lyCounter().ly() < 144) {
if (cycleCounter + 1 < m0TimeOfCurrentLine(cycleCounter)) {
if (lycCmp.ly == lycIrq.lycReg() && !(old & 0x40))
eventTimes_.flagIrq(2);
} else {
if (!(old & 0x08) && !(lycCmp.ly == lycIrq.lycReg() && (old & 0x40)))
eventTimes_.flagIrq(2);
}
} else {
if (!(old & 0x10) && !(lycCmp.ly == lycIrq.lycReg() && (old & 0x40)))
eventTimes_.flagIrq(2);
}
} else if (data & ~old & 0x78) {
bool const lycperiod = lycCmp.ly == lycIrq.lycReg() && lycCmp.timeToNextLy > 4 - isDoubleSpeed() * 4;
if (!(lycperiod && (old & 0x40))) {
if (ppu.lyCounter().ly() < 144) {
if (cycleCounter + isDoubleSpeed() * 2 < m0TimeOfCurrentLine(cycleCounter) || timeToNextLy <= 4) {
if (lycperiod && (data & 0x40))
eventTimes_.flagIrq(2);
} else if (!(old & 0x08)) {
if ((data & 0x08) || (lycperiod && (data & 0x40)))
eventTimes_.flagIrq(2);
}
} else if (!(old & 0x10)) {
if ((data & 0x10) && (ppu.lyCounter().ly() < 153 || timeToNextLy > 4 - isDoubleSpeed() * 4)) {
eventTimes_.flagIrq(2);
} else if (lycperiod && (data & 0x40))
eventTimes_.flagIrq(2);
}
}
if ((data & 0x28) == 0x20 && !(old & 0x20)
&& ((timeToNextLy <= 4 && ppu.lyCounter().ly() < 143)
|| (timeToNextLy == 456*2 && ppu.lyCounter().ly() < 144))) {
eventTimes_.flagIrq(2);
}
}
if ((data & 0x08) && eventTimes_(MODE0_IRQ) == DISABLED_TIME) {
update(cycleCounter);
eventTimes_.setm<MODE0_IRQ>(m0IrqTimeFromXpos166Time(ppu.predictedNextXposTime(166), ppu.cgb(), isDoubleSpeed()));
}
eventTimes_.setm<MODE2_IRQ>(mode2IrqSchedule(data, ppu.lyCounter(), cycleCounter));
eventTimes_.setm<LYC_IRQ>(lycIrq.time());
}
m2IrqStatReg_ = eventTimes_(MODE2_IRQ) - cycleCounter > (ppu.cgb() - isDoubleSpeed()) * 4U
? data : (m2IrqStatReg_ & 0x10) | (statReg & ~0x10);
m1IrqStatReg_ = eventTimes_(MODE1_IRQ) - cycleCounter > (ppu.cgb() - isDoubleSpeed()) * 4U
? data : (m1IrqStatReg_ & 0x08) | (statReg & ~0x08);
m0Irq_.statRegChange(data, eventTimes_(MODE0_IRQ), cycleCounter, ppu.cgb());
}
void LCD::lycRegChange(unsigned const data, unsigned long const cycleCounter) {
unsigned const old = lycIrq.lycReg();
if (data == old)
return;
if (cycleCounter >= eventTimes_.nextEventTime())
update(cycleCounter);
m0Irq_.lycRegChange(data, eventTimes_(MODE0_IRQ), cycleCounter, isDoubleSpeed(), ppu.cgb());
lycIrq.lycRegChange(data, ppu.lyCounter(), cycleCounter);
if (!(ppu.lcdc() & 0x80))
return;
eventTimes_.setm<LYC_IRQ>(lycIrq.time());
int const timeToNextLy = ppu.lyCounter().time() - cycleCounter;
if ((statReg & 0x40) && data < 154
&& (ppu.lyCounter().ly() < 144
? !(statReg & 0x08) || cycleCounter < m0TimeOfCurrentLine(cycleCounter) || timeToNextLy <= 4 << ppu.cgb()
: !(statReg & 0x10) || (ppu.lyCounter().ly() == 153 && timeToNextLy <= 4 && ppu.cgb() && !isDoubleSpeed()))) {
LyCnt lycCmp = getLycCmpLy(ppu.lyCounter(), cycleCounter);
if (lycCmp.timeToNextLy <= 4 << ppu.cgb()) {
lycCmp.ly = old != lycCmp.ly || (lycCmp.timeToNextLy <= 4 && ppu.cgb() && !isDoubleSpeed())
? (lycCmp.ly == 153 ? 0 : lycCmp.ly + 1)
: 0xFF; // simultaneous ly/lyc inc. lyc flag never goes low -> no trigger.
}
if (data == lycCmp.ly) {
if (ppu.cgb() && !isDoubleSpeed()) {
eventTimes_.setm<ONESHOT_LCDSTATIRQ>(cycleCounter + 5);
} else
eventTimes_.flagIrq(2);
}
}
}
unsigned LCD::getStat(unsigned const lycReg, unsigned long const cycleCounter) {
unsigned stat = 0;
if (ppu.lcdc() & 0x80) {
if (cycleCounter >= eventTimes_.nextEventTime())
update(cycleCounter);
int const timeToNextLy = ppu.lyCounter().time() - cycleCounter;
if (ppu.lyCounter().ly() > 143) {
if (ppu.lyCounter().ly() < 153 || timeToNextLy > 4 - isDoubleSpeed() * 4)
stat = 1;
} else {
unsigned const lineCycles = 456 - (timeToNextLy >> isDoubleSpeed());
if (lineCycles < 80) {
if (!ppu.inactivePeriodAfterDisplayEnable(cycleCounter))
stat = 2;
} else if (cycleCounter + isDoubleSpeed() - ppu.cgb() + 2 < m0TimeOfCurrentLine(cycleCounter))
stat = 3;
}
LyCnt const lycCmp = getLycCmpLy(ppu.lyCounter(), cycleCounter);
if (lycReg == lycCmp.ly && lycCmp.timeToNextLy > 4 - isDoubleSpeed() * 4)
stat |= 4;
}
return stat;
}
inline void LCD::doMode2IrqEvent() {
const unsigned ly = eventTimes_(LY_COUNT) - eventTimes_(MODE2_IRQ) < 8
? (ppu.lyCounter().ly() == 153 ? 0 : ppu.lyCounter().ly() + 1)
: ppu.lyCounter().ly();
if ((ly != 0 || !(m2IrqStatReg_ & 0x10)) &&
(!(m2IrqStatReg_ & 0x40) || (lycIrq.lycReg() != 0 ? ly != (lycIrq.lycReg() + 1U) : ly > 1))) {
eventTimes_.flagIrq(2);
}
m2IrqStatReg_ = statReg;
if (!(statReg & 0x08)) {
unsigned long nextTime = eventTimes_(MODE2_IRQ) + ppu.lyCounter().lineTime();
if (ly == 0) {
nextTime -= 4;
} else if (ly == 143)
nextTime += ppu.lyCounter().lineTime() * 10 + 4;
eventTimes_.setm<MODE2_IRQ>(nextTime);
} else
eventTimes_.setm<MODE2_IRQ>(eventTimes_(MODE2_IRQ) + (70224 << isDoubleSpeed()));
}
inline void LCD::event() {
switch (eventTimes_.nextEvent()) {
case MEM_EVENT:
switch (eventTimes_.nextMemEvent()) {
case MODE1_IRQ:
eventTimes_.flagIrq((m1IrqStatReg_ & 0x18) == 0x10 ? 3 : 1);
m1IrqStatReg_ = statReg;
eventTimes_.setm<MODE1_IRQ>(eventTimes_(MODE1_IRQ) + (70224 << isDoubleSpeed()));
break;
case LYC_IRQ: {
unsigned char ifreg = 0;
lycIrq.doEvent(&ifreg, ppu.lyCounter());
eventTimes_.flagIrq(ifreg);
eventTimes_.setm<LYC_IRQ>(lycIrq.time());
break;
}
case SPRITE_MAP:
eventTimes_.setm<SPRITE_MAP>(ppu.doSpriteMapEvent(eventTimes_(SPRITE_MAP)));
mode3CyclesChange();
break;
case HDMA_REQ:
eventTimes_.flagHdmaReq();
nextM0Time_.predictNextM0Time(ppu);
eventTimes_.setm<HDMA_REQ>(hdmaTimeFromM0Time(nextM0Time_.predictedNextM0Time(), isDoubleSpeed()));
break;
case MODE2_IRQ:
doMode2IrqEvent();
break;
case MODE0_IRQ:
{
unsigned char ifreg = 0;
m0Irq_.doEvent(&ifreg, ppu.lyCounter().ly(), statReg, lycIrq.lycReg());
eventTimes_.flagIrq(ifreg);
}
eventTimes_.setm<MODE0_IRQ>((statReg & 0x08)
? m0IrqTimeFromXpos166Time(ppu.predictedNextXposTime(166), ppu.cgb(), isDoubleSpeed())
: static_cast<unsigned long>(DISABLED_TIME));
break;
case ONESHOT_LCDSTATIRQ:
eventTimes_.flagIrq(2);
eventTimes_.setm<ONESHOT_LCDSTATIRQ>(DISABLED_TIME);
break;
case ONESHOT_UPDATEWY2:
ppu.updateWy2();
mode3CyclesChange();
eventTimes_.setm<ONESHOT_UPDATEWY2>(DISABLED_TIME);
break;
}
break;
case LY_COUNT:
ppu.doLyCountEvent();
eventTimes_.set<LY_COUNT>(ppu.lyCounter().time());
break;
}
}
void LCD::update(const unsigned long cycleCounter) {
if (!(ppu.lcdc() & 0x80))
return;
while (cycleCounter >= eventTimes_.nextEventTime()) {
ppu.update(eventTimes_.nextEventTime());
event();
}
ppu.update(cycleCounter);
}
void LCD::setVideoBuffer(uint_least32_t *const videoBuf, const int pitch) {
ppu.setFrameBuf(videoBuf, pitch);
}
void LCD::setDmgPaletteColor(const unsigned index, const unsigned long rgb32) {
dmgColorsRgb32[index] = rgb32;
}
void LCD::setDmgPaletteColor(const unsigned palNum, const unsigned colorNum, const unsigned long rgb32) {
if (palNum > 2 || colorNum > 3)
return;
setDmgPaletteColor(palNum * 4 | colorNum, rgb32);
refreshPalettes();
}
}

257
libgambatte/src/video.h Normal file
View File

@ -0,0 +1,257 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef VIDEO_H
#define VIDEO_H
#include "video/ppu.h"
#include "video/lyc_irq.h"
#include "video/next_m0_time.h"
#include "interruptrequester.h"
#include "osd_element.h"
#include "minkeeper.h"
#include <memory>
namespace gambatte {
class VideoInterruptRequester {
InterruptRequester * intreq;
public:
explicit VideoInterruptRequester(InterruptRequester * intreq) : intreq(intreq) {}
void flagHdmaReq() const { gambatte::flagHdmaReq(intreq); }
void flagIrq(const unsigned bit) const { intreq->flagIrq(bit); }
void setNextEventTime(const unsigned long time) const { intreq->setEventTime<VIDEO>(time); }
};
class M0Irq {
unsigned char statReg_;
unsigned char lycReg_;
public:
M0Irq() : statReg_(0), lycReg_(0) {}
void lcdReset(const unsigned statReg, const unsigned lycReg) {
statReg_ = statReg;
lycReg_ = lycReg;
}
void statRegChange(const unsigned statReg,
const unsigned long nextM0IrqTime, const unsigned long cc, const bool cgb) {
if (nextM0IrqTime - cc > cgb * 2U)
statReg_ = statReg;
}
void lycRegChange(const unsigned lycReg,
const unsigned long nextM0IrqTime, const unsigned long cc, const bool ds, const bool cgb) {
if (nextM0IrqTime - cc > cgb * 5 + 1U - ds)
lycReg_ = lycReg;
}
void doEvent(unsigned char *const ifreg, const unsigned ly, const unsigned statReg, const unsigned lycReg) {
if (((statReg_ | statReg) & 0x08) && (!(statReg_ & 0x40) || ly != lycReg_))
*ifreg |= 2;
statReg_ = statReg;
lycReg_ = lycReg;
}
void saveState(SaveState &state) const {
state.ppu.m0lyc = lycReg_;
}
void loadState(const SaveState &state) {
lycReg_ = state.ppu.m0lyc;
statReg_ = state.mem.ioamhram.get()[0x141];
}
unsigned statReg() const { return statReg_; }
};
class LCD {
enum Event { MEM_EVENT, LY_COUNT }; enum { NUM_EVENTS = LY_COUNT + 1 };
enum MemEvent { ONESHOT_LCDSTATIRQ, ONESHOT_UPDATEWY2, MODE1_IRQ, LYC_IRQ, SPRITE_MAP,
HDMA_REQ, MODE2_IRQ, MODE0_IRQ }; enum { NUM_MEM_EVENTS = MODE0_IRQ + 1 };
class EventTimes {
MinKeeper<NUM_EVENTS> eventMin_;
MinKeeper<NUM_MEM_EVENTS> memEventMin_;
VideoInterruptRequester memEventRequester_;
void setMemEvent() {
const unsigned long nmet = nextMemEventTime();
eventMin_.setValue<MEM_EVENT>(nmet);
memEventRequester_.setNextEventTime(nmet);
}
public:
explicit EventTimes(const VideoInterruptRequester memEventRequester) : memEventRequester_(memEventRequester) {}
Event nextEvent() const { return static_cast<Event>(eventMin_.min()); }
unsigned long nextEventTime() const { return eventMin_.minValue(); }
unsigned long operator()(const Event e) const { return eventMin_.value(e); }
template<Event e> void set(const unsigned long time) { eventMin_.setValue<e>(time); }
void set(const Event e, const unsigned long time) { eventMin_.setValue(e, time); }
MemEvent nextMemEvent() const { return static_cast<MemEvent>(memEventMin_.min()); }
unsigned long nextMemEventTime() const { return memEventMin_.minValue(); }
unsigned long operator()(const MemEvent e) const { return memEventMin_.value(e); }
template<MemEvent e> void setm(const unsigned long time) { memEventMin_.setValue<e>(time); setMemEvent(); }
void set(const MemEvent e, const unsigned long time) { memEventMin_.setValue(e, time); setMemEvent(); }
void flagIrq(const unsigned bit) { memEventRequester_.flagIrq(bit); }
void flagHdmaReq() { memEventRequester_.flagHdmaReq(); }
};
PPU ppu;
unsigned long dmgColorsRgb32[3 * 4];
unsigned char bgpData[8 * 8];
unsigned char objpData[8 * 8];
EventTimes eventTimes_;
M0Irq m0Irq_;
LycIrq lycIrq;
NextM0Time nextM0Time_;
std::auto_ptr<OsdElement> osdElement;
unsigned char statReg;
unsigned char m2IrqStatReg_;
unsigned char m1IrqStatReg_;
static void setDmgPalette(unsigned long *palette, const unsigned long *dmgColors, unsigned data);
void setDmgPaletteColor(unsigned index, unsigned long rgb32);
void refreshPalettes();
void setDBuffer();
void doMode2IrqEvent();
void event();
unsigned long m0TimeOfCurrentLine(unsigned long cc);
bool cgbpAccessible(unsigned long cycleCounter);
void mode3CyclesChange();
void doCgbBgColorChange(unsigned index, unsigned data, unsigned long cycleCounter);
void doCgbSpColorChange(unsigned index, unsigned data, unsigned long cycleCounter);
public:
LCD(const unsigned char *oamram, const unsigned char *vram_in, VideoInterruptRequester memEventRequester);
void reset(const unsigned char *oamram, const unsigned char *vram, bool cgb);
void setStatePtrs(SaveState &state);
void saveState(SaveState &state) const;
void loadState(const SaveState &state, const unsigned char *oamram);
void setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned long rgb32);
void setVideoBuffer(uint_least32_t *videoBuf, int pitch);
void setOsdElement(std::auto_ptr<OsdElement> osdElement) { this->osdElement = osdElement; }
void dmgBgPaletteChange(const unsigned data, const unsigned long cycleCounter) {
update(cycleCounter);
bgpData[0] = data;
setDmgPalette(ppu.bgPalette(), dmgColorsRgb32, data);
}
void dmgSpPalette1Change(const unsigned data, const unsigned long cycleCounter) {
update(cycleCounter);
objpData[0] = data;
setDmgPalette(ppu.spPalette(), dmgColorsRgb32 + 4, data);
}
void dmgSpPalette2Change(const unsigned data, const unsigned long cycleCounter) {
update(cycleCounter);
objpData[1] = data;
setDmgPalette(ppu.spPalette() + 4, dmgColorsRgb32 + 8, data);
}
void cgbBgColorChange(unsigned index, const unsigned data, const unsigned long cycleCounter) {
if (bgpData[index] != data)
doCgbBgColorChange(index, data, cycleCounter);
}
void cgbSpColorChange(unsigned index, const unsigned data, const unsigned long cycleCounter) {
if (objpData[index] != data)
doCgbSpColorChange(index, data, cycleCounter);
}
unsigned cgbBgColorRead(const unsigned index, const unsigned long cycleCounter) {
return ppu.cgb() & cgbpAccessible(cycleCounter) ? bgpData[index] : 0xFF;
}
unsigned cgbSpColorRead(const unsigned index, const unsigned long cycleCounter) {
return ppu.cgb() & cgbpAccessible(cycleCounter) ? objpData[index] : 0xFF;
}
void updateScreen(bool blanklcd, unsigned long cc);
void resetCc(unsigned long oldCC, unsigned long newCc);
void speedChange(unsigned long cycleCounter);
bool vramAccessible(unsigned long cycleCounter);
bool oamReadable(unsigned long cycleCounter);
bool oamWritable(unsigned long cycleCounter);
void wxChange(unsigned newValue, unsigned long cycleCounter);
void wyChange(unsigned newValue, unsigned long cycleCounter);
void oamChange(unsigned long cycleCounter);
void oamChange(const unsigned char *oamram, unsigned long cycleCounter);
void scxChange(unsigned newScx, unsigned long cycleCounter);
void scyChange(unsigned newValue, unsigned long cycleCounter);
void vramChange(const unsigned long cycleCounter) { update(cycleCounter); }
unsigned getStat(unsigned lycReg, unsigned long cycleCounter);
unsigned getLyReg(const unsigned long cycleCounter) {
unsigned lyReg = 0;
if (ppu.lcdc() & 0x80) {
if (cycleCounter >= ppu.lyCounter().time())
update(cycleCounter);
lyReg = ppu.lyCounter().ly();
if (lyReg == 153) {
if (isDoubleSpeed()) {
if (ppu.lyCounter().time() - cycleCounter <= 456 * 2 - 8)
lyReg = 0;
} else
lyReg = 0;
} else if (ppu.lyCounter().time() - cycleCounter <= 4)
++lyReg;
}
return lyReg;
}
unsigned long nextMode1IrqTime() const { return eventTimes_(MODE1_IRQ); }
void lcdcChange(unsigned data, unsigned long cycleCounter);
void lcdstatChange(unsigned data, unsigned long cycleCounter);
void lycRegChange(unsigned data, unsigned long cycleCounter);
void enableHdma(unsigned long cycleCounter);
void disableHdma(unsigned long cycleCounter);
bool hdmaIsEnabled() const { return eventTimes_(HDMA_REQ) != DISABLED_TIME; }
void update(unsigned long cycleCounter);
bool isCgb() const { return ppu.cgb(); }
bool isDoubleSpeed() const { return ppu.lyCounter().isDoubleSpeed(); }
};
}
#endif

View File

@ -0,0 +1,68 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "ly_counter.h"
#include "../savestate.h"
namespace gambatte {
LyCounter::LyCounter()
: time_(0), lineTime_(0), ly_(0), ds(false)
{
setDoubleSpeed(false);
reset(0, 0);
}
void LyCounter::doEvent() {
++ly_;
if (ly_ == 154)
ly_ = 0;
time_ = time_ + lineTime_;
}
unsigned long LyCounter::nextLineCycle(const unsigned lineCycle, const unsigned long cycleCounter) const {
unsigned long tmp = time_ + (lineCycle << ds);
if (tmp - cycleCounter > lineTime_)
tmp -= lineTime_;
return tmp;
}
unsigned long LyCounter::nextFrameCycle(const unsigned long frameCycle, const unsigned long cycleCounter) const {
unsigned long tmp = time_ + (((153U - ly()) * 456U + frameCycle) << ds);
if (tmp - cycleCounter > 70224U << ds)
tmp -= 70224U << ds;
return tmp;
}
void LyCounter::reset(const unsigned long videoCycles, const unsigned long lastUpdate) {
ly_ = videoCycles / 456;
time_ = lastUpdate + ((456 - (videoCycles - ly_ * 456ul)) << isDoubleSpeed());
}
void LyCounter::setDoubleSpeed(const bool ds_in) {
ds = ds_in;
lineTime_ = 456U << ds_in;
}
}

View File

@ -0,0 +1,56 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef LY_COUNTER_H
#define LY_COUNTER_H
namespace gambatte {
struct SaveState;
class LyCounter {
unsigned long time_;
unsigned short lineTime_;
unsigned char ly_;
bool ds;
public:
LyCounter();
void doEvent();
bool isDoubleSpeed() const { return ds; }
unsigned long frameCycles(const unsigned long cc) const {
return ly_ * 456ul + lineCycles(cc);
}
unsigned lineCycles(const unsigned long cc) const {
return 456u - ((time_ - cc) >> isDoubleSpeed());
}
unsigned lineTime() const { return lineTime_; }
unsigned ly() const { return ly_; }
unsigned long nextLineCycle(unsigned lineCycle, unsigned long cycleCounter) const;
unsigned long nextFrameCycle(unsigned long frameCycle, unsigned long cycleCounter) const;
void reset(unsigned long videoCycles, unsigned long lastUpdate);
void setDoubleSpeed(bool ds_in);
unsigned long time() const { return time_; }
};
}
#endif

View File

@ -0,0 +1,102 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "lyc_irq.h"
#include "counterdef.h"
#include "ly_counter.h"
#include "savestate.h"
#include <algorithm>
namespace gambatte {
LycIrq::LycIrq() :
time_(DISABLED_TIME),
lycRegSrc_(0),
statRegSrc_(0),
lycReg_(0),
statReg_(0),
cgb_(false)
{
}
static unsigned long schedule(const unsigned statReg, const unsigned lycReg, const LyCounter &lyCounter, const unsigned long cc) {
return (statReg & 0x40) && lycReg < 154
? lyCounter.nextFrameCycle(lycReg ? lycReg * 456 : 153 * 456 + 8, cc)
: static_cast<unsigned long>(DISABLED_TIME);
}
void LycIrq::regChange(const unsigned statReg, const unsigned lycReg, const LyCounter &lyCounter, const unsigned long cc) {
const unsigned long timeSrc = schedule(statReg, lycReg, lyCounter, cc);
statRegSrc_ = statReg;
lycRegSrc_ = lycReg;
time_ = std::min(time_, timeSrc);
if (cgb_) {
if (time_ - cc > 8 || (timeSrc != time_ && time_ - cc > 4U - lyCounter.isDoubleSpeed() * 4U))
lycReg_ = lycReg;
if (time_ - cc > 4U - lyCounter.isDoubleSpeed() * 4U)
statReg_ = statReg;
} else {
if (time_ - cc > 4 || timeSrc != time_)
lycReg_ = lycReg;
if (time_ - cc > 4 || lycReg_ != 0)
statReg_ = statReg;
statReg_ = (statReg_ & 0x40) | (statReg & ~0x40);
}
}
void LycIrq::doEvent(unsigned char *const ifreg, const LyCounter &lyCounter) {
if ((statReg_ | statRegSrc_) & 0x40) {
const unsigned cmpLy = lyCounter.time() - time_ < lyCounter.lineTime() ? 0 : lyCounter.ly();
if (lycReg_ == cmpLy &&
(lycReg_ - 1U < 144U - 1U ? !(statReg_ & 0x20) : !(statReg_ & 0x10))) {
*ifreg |= 2;
}
}
lycReg_ = lycRegSrc_;
statReg_ = statRegSrc_;
time_ = schedule(statReg_, lycReg_, lyCounter, time_);
}
void LycIrq::loadState(const SaveState &state) {
lycRegSrc_ = state.mem.ioamhram.get()[0x145];
statRegSrc_ = state.mem.ioamhram.get()[0x141];
lycReg_ = state.ppu.lyc;
statReg_ = statRegSrc_;
}
void LycIrq::saveState(SaveState &state) const {
state.ppu.lyc = lycReg_;
}
void LycIrq::reschedule(const LyCounter & lyCounter, const unsigned long cc) {
time_ = std::min(schedule(statReg_ , lycReg_ , lyCounter, cc),
schedule(statRegSrc_, lycRegSrc_, lyCounter, cc));
}
void LycIrq::lcdReset() {
statReg_ = statRegSrc_;
lycReg_ = lycRegSrc_;
}
}

View File

@ -0,0 +1,59 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef VIDEO_LYC_IRQ_H
#define VIDEO_LYC_IRQ_H
namespace gambatte {
struct SaveState;
class LyCounter;
class LycIrq {
unsigned long time_;
unsigned char lycRegSrc_;
unsigned char statRegSrc_;
unsigned char lycReg_;
unsigned char statReg_;
bool cgb_;
void regChange(unsigned statReg, unsigned lycReg, const LyCounter &lyCounter, unsigned long cc);
public:
LycIrq();
void doEvent(unsigned char *ifreg, const LyCounter &lyCounter);
unsigned lycReg() const { return lycRegSrc_; }
void loadState(const SaveState &state);
void saveState(SaveState &state) const;
unsigned long time() const { return time_; }
void setCgb(const bool cgb) { cgb_ = cgb; }
void lcdReset();
void reschedule(const LyCounter & lyCounter, unsigned long cc);
void statRegChange(unsigned statReg, const LyCounter &lyCounter, unsigned long cc) {
regChange(statReg, lycRegSrc_, lyCounter, cc);
}
void lycRegChange(unsigned lycReg, const LyCounter &lyCounter, unsigned long cc) {
regChange(statRegSrc_, lycReg, lyCounter, cc);
}
};
}
#endif

View File

@ -0,0 +1,6 @@
#include "next_m0_time.h"
#include "ppu.h"
void gambatte::NextM0Time::predictNextM0Time(const PPU &ppu) {
predictedNextM0Time_ = ppu.predictedNextXposTime(167);
}

View File

@ -0,0 +1,18 @@
#ifndef NEXT_M0_TIME_H_
#define NEXT_M0_TIME_H_
namespace gambatte {
class NextM0Time {
unsigned predictedNextM0Time_;
public:
NextM0Time() : predictedNextM0Time_(0) {}
void predictNextM0Time(const class PPU &v);
void invalidatePredictedNextM0Time() { predictedNextM0Time_ = 0; }
unsigned predictedNextM0Time() const { return predictedNextM0Time_; }
};
}
#endif

File diff suppressed because it is too large Load Diff

135
libgambatte/src/video/ppu.h Normal file
View File

@ -0,0 +1,135 @@
/***************************************************************************
* Copyright (C) 2010 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef PPU_H
#define PPU_H
#include "video/ly_counter.h"
#include "video/sprite_mapper.h"
#include "gbint.h"
namespace gambatte {
class PPUFrameBuf {
uint_least32_t *buf_;
uint_least32_t *fbline_;
int pitch_;
static uint_least32_t * nullfbline() { static uint_least32_t nullfbline_[160]; return nullfbline_; }
public:
PPUFrameBuf() : buf_(0), fbline_(nullfbline()), pitch_(0) {}
uint_least32_t * fb() const { return buf_; }
uint_least32_t * fbline() const { return fbline_; }
int pitch() const { return pitch_; }
void setBuf(uint_least32_t *const buf, const int pitch) { buf_ = buf; pitch_ = pitch; fbline_ = nullfbline(); }
void setFbline(const unsigned ly) { fbline_ = buf_ ? buf_ + static_cast<long>(ly) * static_cast<long>(pitch_) : nullfbline(); }
};
struct PPUState {
void (*f)(struct PPUPriv &v);
unsigned (*predictCyclesUntilXpos_f)(const struct PPUPriv &v, int targetxpos, unsigned cycles);
unsigned char id;
};
// The PPU loop accesses a lot of state at once, so it's difficult to split this up much beyond grouping stuff into smaller structs.
struct PPUPriv {
unsigned long bgPalette[8 * 4];
unsigned long spPalette[8 * 4];
struct Sprite { unsigned char spx, oampos, line, attrib; } spriteList[11];
unsigned short spwordList[11];
unsigned char nextSprite;
unsigned char currentSprite;
const unsigned char *vram;
const PPUState *nextCallPtr;
unsigned long now;
unsigned long lastM0Time;
long cycles;
unsigned tileword;
unsigned ntileword;
SpriteMapper spriteMapper;
LyCounter lyCounter;
PPUFrameBuf framebuf;
unsigned char lcdc;
unsigned char scy;
unsigned char scx;
unsigned char wy;
unsigned char wy2;
unsigned char wx;
unsigned char winDrawState;
unsigned char wscx;
unsigned char winYPos;
unsigned char reg0;
unsigned char reg1;
unsigned char attrib;
unsigned char nattrib;
unsigned char xpos;
unsigned char endx;
bool cgb;
bool weMaster;
PPUPriv(NextM0Time &nextM0Time, const unsigned char *oamram, const unsigned char *vram);
};
class PPU {
PPUPriv p_;
public:
PPU(NextM0Time &nextM0Time, const unsigned char *oamram, const unsigned char *vram)
: p_(nextM0Time, oamram, vram)
{
}
unsigned long * bgPalette() { return p_.bgPalette; }
bool cgb() const { return p_.cgb; }
void doLyCountEvent() { p_.lyCounter.doEvent(); }
unsigned long doSpriteMapEvent(unsigned long time) { return p_.spriteMapper.doEvent(time); }
const PPUFrameBuf & frameBuf() const { return p_.framebuf; }
bool inactivePeriodAfterDisplayEnable(unsigned long cc) const { return p_.spriteMapper.inactivePeriodAfterDisplayEnable(cc); }
unsigned long lastM0Time() const { return p_.lastM0Time; }
unsigned lcdc() const { return p_.lcdc; }
void loadState(const SaveState &state, const unsigned char *oamram);
const LyCounter & lyCounter() const { return p_.lyCounter; }
unsigned long now() const { return p_.now; }
void oamChange(unsigned long cc) { p_.spriteMapper.oamChange(cc); }
void oamChange(const unsigned char *oamram, unsigned long cc) { p_.spriteMapper.oamChange(oamram, cc); }
unsigned long predictedNextXposTime(unsigned xpos) const;
void reset(const unsigned char *oamram, const unsigned char *vram, bool cgb);
void resetCc(unsigned long oldCc, unsigned long newCc);
void saveState(SaveState &ss) const;
void setFrameBuf(uint_least32_t *buf, unsigned pitch) { p_.framebuf.setBuf(buf, pitch); }
void setLcdc(unsigned lcdc, unsigned long cc);
void setScx(const unsigned scx) { p_.scx = scx; }
void setScy(const unsigned scy) { p_.scy = scy; }
void setStatePtrs(SaveState &ss) { p_.spriteMapper.setStatePtrs(ss); }
void setWx(const unsigned wx) { p_.wx = wx; }
void setWy(const unsigned wy) { p_.wy = wy; }
void updateWy2() { p_.wy2 = p_.wy; }
void speedChange(unsigned long cycleCounter);
unsigned long * spPalette() { return p_.spPalette; }
void update(unsigned long cc);
};
}
#endif

View File

@ -0,0 +1,182 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include "sprite_mapper.h"
#include "counterdef.h"
#include "next_m0_time.h"
#include "../insertion_sort.h"
#include <cstring>
#include <algorithm>
namespace gambatte {
SpriteMapper::OamReader::OamReader(const LyCounter &lyCounter, const unsigned char *oamram)
: lyCounter(lyCounter), oamram(oamram), cgb_(false) {
reset(oamram, false);
}
void SpriteMapper::OamReader::reset(const unsigned char *const oamram, const bool cgb) {
this->oamram = oamram;
this->cgb_ = cgb;
setLargeSpritesSrc(false);
lu = 0;
lastChange = 0xFF;
std::fill_n(szbuf, 40, largeSpritesSrc);
unsigned pos = 0;
unsigned distance = 80;
while (distance--) {
buf[pos] = oamram[((pos * 2) & ~3) | (pos & 1)];
++pos;
}
}
static unsigned toPosCycles(const unsigned long cc, const LyCounter &lyCounter) {
unsigned lc = lyCounter.lineCycles(cc) + 3 - lyCounter.isDoubleSpeed() * 3u;
if (lc >= 456)
lc -= 456;
return lc;
}
void SpriteMapper::OamReader::update(const unsigned long cc) {
if (cc > lu) {
if (changed()) {
const unsigned lulc = toPosCycles(lu, lyCounter);
unsigned pos = std::min(lulc, 80u);
unsigned distance = 80;
if ((cc - lu) >> lyCounter.isDoubleSpeed() < 456) {
const unsigned cclc = toPosCycles(cc, lyCounter);
distance = std::min(cclc, 80u) - pos + (cclc < lulc ? 80 : 0);
}
{
const unsigned targetDistance = lastChange - pos + (lastChange <= pos ? 80 : 0);
if (targetDistance <= distance) {
distance = targetDistance;
lastChange = 0xFF;
}
}
while (distance--) {
if (!(pos & 1)) {
if (pos == 80)
pos = 0;
if (cgb_)
szbuf[pos >> 1] = largeSpritesSrc;
buf[pos ] = oamram[pos * 2 ];
buf[pos + 1] = oamram[pos * 2 + 1];
} else
szbuf[pos >> 1] = (szbuf[pos >> 1] & cgb_) | largeSpritesSrc;
++pos;
}
}
lu = cc;
}
}
void SpriteMapper::OamReader::change(const unsigned long cc) {
update(cc);
lastChange = std::min(toPosCycles(lu, lyCounter), 80u);
}
void SpriteMapper::OamReader::setStatePtrs(SaveState &state) {
state.ppu.oamReaderBuf.set(buf, sizeof buf);
state.ppu.oamReaderSzbuf.set(szbuf, sizeof(szbuf) / sizeof(bool));
}
void SpriteMapper::OamReader::loadState(const SaveState &ss, const unsigned char *const oamram) {
this->oamram = oamram;
largeSpritesSrc = ss.mem.ioamhram.get()[0x140] >> 2 & 1;
lu = ss.ppu.enableDisplayM0Time;
change(lu);
}
void SpriteMapper::OamReader::enableDisplay(const unsigned long cc) {
std::memset(buf, 0x00, sizeof(buf));
std::fill(szbuf, szbuf + 40, false);
lu = cc + (80 << lyCounter.isDoubleSpeed());
lastChange = 80;
}
SpriteMapper::SpriteMapper(NextM0Time &nextM0Time,
const LyCounter &lyCounter,
const unsigned char *const oamram) :
nextM0Time_(nextM0Time),
oamReader(lyCounter, oamram)
{
clearMap();
}
void SpriteMapper::reset(const unsigned char *const oamram, const bool cgb) {
oamReader.reset(oamram, cgb);
clearMap();
}
void SpriteMapper::clearMap() {
std::memset(num, NEED_SORTING_MASK, sizeof num);
}
void SpriteMapper::mapSprites() {
clearMap();
for (unsigned i = 0x00; i < 0x50; i += 2) {
const int spriteHeight = 8 << largeSprites(i >> 1);
const unsigned bottom_pos = posbuf()[i] - (17u - spriteHeight);
if (bottom_pos < 143u + spriteHeight) {
const unsigned startly = static_cast<int>(bottom_pos) + 1 - spriteHeight >= 0
? static_cast<int>(bottom_pos) + 1 - spriteHeight : 0;
unsigned char *map = spritemap + startly * 10;
unsigned char *n = num + startly;
unsigned char *const nend = num + (bottom_pos < 143 ? bottom_pos : 143) + 1;
do {
if (*n < NEED_SORTING_MASK + 10)
map[(*n)++ - NEED_SORTING_MASK] = i;
map += 10;
} while (++n != nend);
}
}
nextM0Time_.invalidatePredictedNextM0Time();
}
void SpriteMapper::sortLine(const unsigned ly) const {
num[ly] &= ~NEED_SORTING_MASK;
insertionSort(spritemap + ly * 10, spritemap + ly * 10 + num[ly], SpxLess(posbuf()));
}
unsigned long SpriteMapper::doEvent(const unsigned long time) {
oamReader.update(time);
mapSprites();
return oamReader.changed() ? time + oamReader.lyCounter.lineTime() : static_cast<unsigned long>(DISABLED_TIME);
}
}

View File

@ -0,0 +1,129 @@
/***************************************************************************
* Copyright (C) 2007 by Sindre Aamås *
* aamas@stud.ntnu.no *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License version 2 as *
* published by the Free Software Foundation. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License version 2 for more details. *
* *
* You should have received a copy of the GNU General Public License *
* version 2 along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef SPRITE_MAPPER_H
#define SPRITE_MAPPER_H
#include "ly_counter.h"
#include "../savestate.h"
namespace gambatte {
class NextM0Time;
class SpriteMapper {
class OamReader {
unsigned char buf[80];
bool szbuf[40];
public:
const LyCounter &lyCounter;
private:
const unsigned char *oamram;
unsigned long lu;
unsigned char lastChange;
bool largeSpritesSrc;
bool cgb_;
public:
OamReader(const LyCounter &lyCounter, const unsigned char *oamram);
void reset(const unsigned char *oamram, bool cgb);
void change(unsigned long cc);
void change(const unsigned char *oamram, unsigned long cc) { change(cc); this->oamram = oamram; }
bool changed() const { return lastChange != 0xFF; }
bool largeSprites(unsigned spNr) const { return szbuf[spNr]; }
const unsigned char *oam() const { return oamram; }
void resetCycleCounter(const unsigned long oldCc, const unsigned long newCc) { lu -= oldCc - newCc; }
void setLargeSpritesSrc(const bool src) { largeSpritesSrc = src; }
void update(unsigned long cc);
const unsigned char *spritePosBuf() const { return buf; }
void setStatePtrs(SaveState &state);
void enableDisplay(unsigned long cc);
void saveState(SaveState &state) const { state.ppu.enableDisplayM0Time = lu; }
void loadState(const SaveState &ss, const unsigned char *oamram);
bool inactivePeriodAfterDisplayEnable(const unsigned long cc) const { return cc < lu; }
};
enum { NEED_SORTING_MASK = 0x80 };
public:
class SpxLess {
const unsigned char *const posbuf_plus1;
public:
explicit SpxLess(const unsigned char *const posbuf) : posbuf_plus1(posbuf + 1) {}
bool operator()(const unsigned char l, const unsigned char r) const {
return posbuf_plus1[l] < posbuf_plus1[r];
}
};
private:
mutable unsigned char spritemap[144*10];
mutable unsigned char num[144];
NextM0Time &nextM0Time_;
OamReader oamReader;
void clearMap();
void mapSprites();
void sortLine(unsigned ly) const;
public:
SpriteMapper(NextM0Time &nextM0Time,
const LyCounter &lyCounter,
const unsigned char *oamram_in);
void reset(const unsigned char *oamram, bool cgb);
unsigned long doEvent(unsigned long time);
bool largeSprites(unsigned spNr) const { return oamReader.largeSprites(spNr); }
unsigned numSprites(const unsigned ly) const { return num[ly] & ~NEED_SORTING_MASK; }
void oamChange(unsigned long cc) { oamReader.change(cc); }
void oamChange(const unsigned char *oamram, unsigned long cc) { oamReader.change(oamram, cc); }
const unsigned char *oamram() const { return oamReader.oam(); }
const unsigned char *posbuf() const { return oamReader.spritePosBuf(); }
void preSpeedChange(const unsigned long cc) { oamReader.update(cc); }
void postSpeedChange(const unsigned long cc) { oamReader.change(cc); }
void resetCycleCounter(const unsigned long oldCc, const unsigned long newCc) {
oamReader.update(oldCc);
oamReader.resetCycleCounter(oldCc, newCc);
}
static unsigned long schedule(const LyCounter &lyCounter, const unsigned long cycleCounter) {
return lyCounter.nextLineCycle(80, cycleCounter);
}
void setLargeSpritesSource(bool src) { oamReader.setLargeSpritesSrc(src); }
const unsigned char* sprites(const unsigned ly) const {
if (num[ly] & NEED_SORTING_MASK)
sortLine(ly);
return spritemap + ly * 10;
}
void setStatePtrs(SaveState &state) { oamReader.setStatePtrs(state); }
void enableDisplay(unsigned long cc) { oamReader.enableDisplay(cc); }
void saveState(SaveState &state) const { oamReader.saveState(state); }
void loadState(const SaveState &state, const unsigned char *const oamram) { oamReader.loadState(state, oamram); mapSprites(); }
bool inactivePeriodAfterDisplayEnable(unsigned long cc) const { return oamReader.inactivePeriodAfterDisplayEnable(cc); }
};
}
#endif