let's get rid of libgambatte sources.......

This commit is contained in:
zeromus 2020-06-07 22:23:55 -05:00
parent 62c6513ee3
commit 7a584b48bd
74 changed files with 0 additions and 16432 deletions

View File

@ -1,66 +0,0 @@
CXX = g++
RM = rm
CP = cp
MACHINE = $(shell $(CXX) -dumpmachine)
ifneq (,$(findstring i686,$(MACHINE)))
$(error 32 bit build no longer supported)
else ifneq (,$(findstring x86_64,$(MACHINE)))
ARCH = 64
else
$(error Unknown arch)
endif
CXXFLAGS = -Wall -Iinclude -Isrc -O3 -std=c++11 -fno-exceptions -flto -fPIC
TARGET = libgambatte.dll
LDFLAGS_32 = -static -static-libgcc -static-libstdc++
LDFLAGS_64 =
LDFLAGS = -shared $(LDFLAGS_$(ARCH)) $(CXXFLAGS)
DEST_32 = ../output/dll
DEST_64 = ../output/dll
SRCS = \
src/cinterface.cpp \
src/cpu.cpp \
src/gambatte.cpp \
src/initstate.cpp \
src/interruptrequester.cpp \
src/memory.cpp \
src/mem/cartridge.cpp \
src/mem/memptrs.cpp \
src/mem/rtc.cpp \
src/mem/time.cpp \
src/newstate.cpp \
src/sound.cpp \
src/sound/channel1.cpp \
src/sound/channel2.cpp \
src/sound/channel3.cpp \
src/sound/channel4.cpp \
src/sound/duty_unit.cpp \
src/sound/envelope_unit.cpp \
src/sound/length_counter.cpp \
src/tima.cpp \
src/video.cpp \
src/video/lyc_irq.cpp \
src/video/ly_counter.cpp \
src/video/next_m0_time.cpp \
src/video/ppu.cpp \
src/video/sprite_mapper.cpp
OBJS = $(SRCS:.cpp=.o)
all: $(TARGET)
%.o: %.cpp
$(CXX) -c -o $@ $< $(CXXFLAGS)
$(TARGET) : $(OBJS)
$(CXX) -o $@ $(LDFLAGS) $(OBJS)
clean:
$(RM) $(OBJS)
$(RM) $(TARGET)
install:
$(CP) $(TARGET) $(DEST_$(ARCH))

View File

@ -1,52 +0,0 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* sinamas@users.sourceforge.net *
* *
* 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., *
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
***************************************************************************/
#ifndef ARRAY_H
#define ARRAY_H
#include <cstddef>
template<typename T>
class SimpleArray : Uncopyable {
public:
explicit SimpleArray(std::size_t size = 0) : a_(size ? new T[size] : 0) {}
~SimpleArray() { delete[] defined_ptr(a_); }
void reset(std::size_t size = 0) { delete[] defined_ptr(a_); a_ = size ? new T[size] : 0; }
T* get() const { return a_; }
operator T* () const { return a_; }
private:
T* a_;
};
class Uncopyable {
protected:
Uncopyable() {}
private:
Uncopyable(Uncopyable const&);
Uncopyable& operator=(Uncopyable const&);
};
template<class T>
inline T* defined_ptr(T* t) {
typedef char type_is_defined[sizeof * t ? 1 : -1];
(void)sizeof(type_is_defined);
return t;
}
#endif

View File

@ -1,174 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef GAMBATTE_H
#define GAMBATTE_H
#include "gbint.h"
#include "loadres.h"
#include <cstddef>
#include <string>
#include "newstate.h"
namespace gambatte {
enum { BG_PALETTE = 0, SP1_PALETTE = 1, SP2_PALETTE = 2 };
typedef void (*MemoryCallback)(int32_t address, int64_t cycleOffset);
typedef void (*CDCallback)(int32_t addr, int32_t addrtype, int32_t flags);
enum eCDLog_AddrType
{
eCDLog_AddrType_ROM, eCDLog_AddrType_HRAM, eCDLog_AddrType_WRAM, eCDLog_AddrType_CartRAM,
eCDLog_AddrType_None
};
enum eCDLog_Flags
{
eCDLog_Flags_ExecFirst = 1,
eCDLog_Flags_ExecOperand = 2,
eCDLog_Flags_Data = 4,
};
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.
*/
LoadRes load(char const *romfiledata, unsigned romfilelength, unsigned flags);
int loadBios(char const *biosfiledata, std::size_t size);
/**
* Emulates until at least 'samples' audio samples are produced in the
* supplied audio buffer, or until a video frame has been drawn.
*
* There are 35112 audio (stereo) samples in a video frame.
* May run for up to 2064 audio samples too long.
*
* An audio sample consists of two native endian 2s complement 16-bit PCM samples,
* with the left sample preceding the right one. Usually casting audioBuf to
* int16_t* is OK. The reason for using an uint_least32_t* in the interface is to
* avoid implementation-defined behavior without compromising performance.
* libgambatte is strictly c++98, so fixed-width types are not an option (and even
* c99/c++11 cannot guarantee their availability).
*
* 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 completed.
*
* @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 audioBuf buffer with space >= samples + 2064
* @param samples in: number of stereo samples to produce,
* out: actual number of samples produced
* @return sample offset in audioBuf at which the video frame was completed, or -1
* if no new video frame was completed.
*/
std::ptrdiff_t runFor(gambatte::uint_least32_t *soundBuf, std::size_t &samples);
void blitTo(gambatte::uint_least32_t *videoBuf, std::ptrdiff_t pitch);
void setLayers(unsigned mask);
/**
* 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(int palNum, int colorNum, unsigned long rgb32);
void setCgbPalette(unsigned *lut);
/** Sets the callback used for getting input state. */
void setInputGetter(unsigned (*getInput)());
void setReadCallback(MemoryCallback);
void setWriteCallback(MemoryCallback);
void setExecCallback(MemoryCallback);
void setCDCallback(CDCallback);
void setTraceCallback(void (*callback)(void *));
void setScanlineCallback(void (*callback)(), int sl);
void setLinkCallback(void(*callback)());
/** Use cycle-based RTC instead of real-time. */
void setTimeMode(bool useCycles);
/** adjust the assumed clock speed of the CPU compared to the RTC */
void setRtcDivisorOffset(long const rtcDivisorOffset);
/** 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. NOT Done implicitly on ROM close. */
void loadSavedata(char const *data);
int saveSavedataLength();
void saveSavedata(char *dest);
// 0 = vram, 1 = rom, 2 = wram, 3 = cartram, 4 = oam, 5 = hram
bool getMemoryArea(int which, unsigned char **data, int *length);
/** ROM header title of currently loaded ROM image. */
std::string const romTitle() const;
unsigned char externalRead(unsigned short addr);
void externalWrite(unsigned short addr, unsigned char val);
int linkStatus(int which);
void getRegs(int *dest);
void setInterruptAddresses(int *addrs, int numAddrs);
int getHitInterruptAddress();
template<bool isReader>void SyncState(NewState *ns);
private:
struct Priv;
Priv *const p_;
GB(GB const &);
GB & operator=(GB const &);
};
}
#endif

View File

@ -1,65 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef GAMBATTE_INT_H
#define GAMBATTE_INT_H
// note that this is a hack to overcome the fact that libgambatte's original defines are not propagated.
#define HAVE_CSTDINT
#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

@ -1,23 +0,0 @@
#ifndef GAMBATTE_LOADRES_H
#define GAMBATTE_LOADRES_H
#include <string>
namespace gambatte {
enum LoadRes {
LOADRES_BAD_FILE_OR_UNKNOWN_MBC = -0x7FFF,
LOADRES_IO_ERROR,
LOADRES_UNSUPPORTED_MBC_HUC3 = -0x1FE,
LOADRES_UNSUPPORTED_MBC_TAMA5,
LOADRES_UNSUPPORTED_MBC_POCKET_CAMERA,
LOADRES_UNSUPPORTED_MBC_MBC7 = -0x122,
LOADRES_UNSUPPORTED_MBC_MBC6 = -0x120,
LOADRES_UNSUPPORTED_MBC_MBC4 = -0x117,
LOADRES_UNSUPPORTED_MBC_MMM01 = -0x10D,
LOADRES_OK = 0
};
}
#endif

View File

@ -1,28 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libgambatte", "libgambatte.vcxproj", "{5D630682-7BDA-474D-B387-0EB420DDC199}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Debug|x64 = Debug|x64
Release|Win32 = Release|Win32
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{5D630682-7BDA-474D-B387-0EB420DDC199}.Debug|Win32.ActiveCfg = Release|x64
{5D630682-7BDA-474D-B387-0EB420DDC199}.Debug|Win32.Build.0 = Release|x64
{5D630682-7BDA-474D-B387-0EB420DDC199}.Debug|x64.ActiveCfg = Release|x64
{5D630682-7BDA-474D-B387-0EB420DDC199}.Debug|x64.Build.0 = Release|x64
{5D630682-7BDA-474D-B387-0EB420DDC199}.Release|Win32.ActiveCfg = Release|Win32
{5D630682-7BDA-474D-B387-0EB420DDC199}.Release|Win32.Build.0 = Release|Win32
{5D630682-7BDA-474D-B387-0EB420DDC199}.Release|x64.ActiveCfg = Release|x64
{5D630682-7BDA-474D-B387-0EB420DDC199}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@ -1,237 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.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>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{5D630682-7BDA-474D-B387-0EB420DDC199}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>libgambatte</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</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>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<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|x64'">
<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>..\output\dll</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>..\output\dll</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>..\output\dll</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>..\output\dll</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>
<DisableSpecificWarnings>4244;4373;4800;4804</DisableSpecificWarnings>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBGAMBATTE_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>include;src;src\common</AdditionalIncludeDirectories>
<DisableSpecificWarnings>4244;4373;4800;4804</DisableSpecificWarnings>
</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>
<DisableSpecificWarnings>4244;4373;4800;4804</DisableSpecificWarnings>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<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>
<DisableSpecificWarnings>4244;4373;4800;4804</DisableSpecificWarnings>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="src\array.h" />
<ClInclude Include="include\gambatte.h" />
<ClInclude Include="include\gbint.h" />
<ClInclude Include="include\loadres.h" />
<ClInclude Include="src\interrupter.h" />
<ClInclude Include="src\cinterface.h" />
<ClInclude Include="src\counterdef.h" />
<ClInclude Include="src\cpu.h" />
<ClInclude Include="src\initstate.h" />
<ClInclude Include="src\insertion_sort.h" />
<ClInclude Include="src\interruptrequester.h" />
<ClInclude Include="src\memory.h" />
<ClInclude Include="src\mem\cartridge.h" />
<ClInclude Include="src\mem\huc3.h" />
<ClInclude Include="src\mem\memptrs.h" />
<ClInclude Include="src\mem\rtc.h" />
<ClInclude Include="src\mem\time.h" />
<ClInclude Include="src\minkeeper.h" />
<ClInclude Include="src\newstate.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\psgdef.h" />
<ClInclude Include="src\sound\sound_unit.h" />
<ClInclude Include="src\sound\static_output_tester.h" />
<ClInclude Include="src\tima.h" />
<ClInclude Include="src\video.h" />
<ClInclude Include="src\video\lcddef.h" />
<ClInclude Include="src\video\lyc_irq.h" />
<ClInclude Include="src\video\ly_counter.h" />
<ClInclude Include="src\video\mstat_irq.h" />
<ClInclude Include="src\video\next_m0_time.h" />
<ClInclude Include="src\video\ppu.h" />
<ClInclude Include="src\video\sprite_mapper.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\Interrupter.cpp" />
<ClCompile Include="src\cinterface.cpp" />
<ClCompile Include="src\cpu.cpp" />
<ClCompile Include="src\gambatte.cpp" />
<ClCompile Include="src\initstate.cpp" />
<ClCompile Include="src\interruptrequester.cpp" />
<ClCompile Include="src\memory.cpp" />
<ClCompile Include="src\mem\cartridge.cpp" />
<ClCompile Include="src\mem\huc3.cpp" />
<ClCompile Include="src\mem\memptrs.cpp" />
<ClCompile Include="src\mem\rtc.cpp" />
<ClCompile Include="src\mem\time.cpp" />
<ClCompile Include="src\newstate.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\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>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
<ProjectExtensions>
<VisualStudio>
<UserProperties />
</VisualStudio>
</ProjectExtensions>
</Project>

View File

@ -1,228 +0,0 @@
<?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>
<ClInclude Include="include\gambatte.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\gbint.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\loadres.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cinterface.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\counterdef.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\cpu.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\initstate.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\insertion_sort.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\interruptrequester.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\memory.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\mem\rtc.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\mem\time.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\minkeeper.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\newstate.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\savestate.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\sound.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\sound\duty_unit.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\sound\envelope_unit.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\sound\static_output_tester.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\tima.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\video.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\video\lyc_irq.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\video\ly_counter.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\video\next_m0_time.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\video\ppu.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\video\sprite_mapper.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\interrupter.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\array.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\sound\psgdef.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\video\mstat_irq.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\video\lcddef.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\mem\huc3.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\cinterface.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\interruptrequester.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\memory.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\mem\time.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\newstate.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\sound.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\tima.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\video.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\video\lyc_irq.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\video\ly_counter.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>
<ClCompile Include="src\Interrupter.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\mem\huc3.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@ -1,103 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "interrupter.h"
#include "memory.h"
namespace gambatte {
Interrupter::Interrupter(unsigned short& sp, unsigned short& pc, unsigned char& opcode, bool& prefetched)
: sp_(sp)
, pc_(pc)
, opcode_(opcode)
, prefetched_(prefetched)
{
}
void Interrupter::prefetch(unsigned long cc, Memory& mem) {
if (!prefetched_) {
opcode_ = mem.read(pc_, cc);
pc_ = (pc_ + 1) & 0xFFFF;
prefetched_ = true;
}
}
unsigned long Interrupter::interrupt(unsigned long cc, Memory& memory) {
// undo prefetch (presumably unconditional on hw).
if (prefetched_) {
pc_ = (pc_ - 1) & 0xFFFF;
prefetched_ = false;
}
cc += 12;
sp_ = (sp_ - 1) & 0xFFFF;
memory.write(sp_, pc_ >> 8, cc);
cc += 4;
unsigned const pendingIrqs = memory.pendingIrqs(cc);
unsigned const n = pendingIrqs & -pendingIrqs;
unsigned address;
if (n <= 4) {
static unsigned char const lut[] = { 0x00, 0x40, 0x48, 0x48, 0x50 };
address = lut[n];
}
else
address = 0x50 + n;
sp_ = (sp_ - 1) & 0xFFFF;
memory.write(sp_, pc_ & 0xFF, cc);
memory.ackIrq(n, cc);
pc_ = address;
cc += 4;
if (address == 0x40 && !gsCodes_.empty())
applyVblankCheats(cc, memory);
return cc;
}
static int asHex(char c) {
return c >= 'A' ? c - 'A' + 0xA : c - '0';
}
void Interrupter::setGameShark(std::string const& codes) {
std::string code;
gsCodes_.clear();
for (std::size_t pos = 0; pos < codes.length(); pos += code.length() + 1) {
code = codes.substr(pos, codes.find(';', pos) - pos);
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(unsigned long const cc, 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, cc);
}
}
}

View File

@ -1,57 +0,0 @@
/***************************************************************************
* Copyright (C) 2008 by Sindre Aamås *
* sinamas@users.sourceforge.net *
* *
* 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., *
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
***************************************************************************/
#ifndef ARRAY_H
#define ARRAY_H
#include <cstddef>
template<typename T>
class SimpleArray : Uncopyable {
public:
explicit SimpleArray(std::size_t size = 0) : a_(size ? new T[size] : 0) {}
~SimpleArray() { delete[] defined_ptr(a_); }
void reset(std::size_t size = 0) { delete[] defined_ptr(a_); a_ = size ? new T[size] : 0; }
T* get() const { return a_; }
operator T* () const { return a_; }
private:
T* a_;
};
class Uncopyable {
protected:
Uncopyable() {}
private:
Uncopyable(Uncopyable const&);
Uncopyable& operator=(Uncopyable const&);
};
template<class T>
inline T* defined_ptr(T* t) {
typedef char type_is_defined[sizeof * t ? 1 : -1];
(void)sizeof(type_is_defined);
return t;
}
template<class T>
inline void defined_delete(T* t) { delete defined_ptr(t); }
struct defined_deleter { template<class T> static void del(T* p) { defined_delete(p); } };
#endif

View File

@ -1,205 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "cinterface.h"
#include "gambatte.h"
#include <cstdlib>
#include <cstring>
#include "newstate.h"
// new is actually called in a few different places, so replace all of them for determinism guarantees
void *operator new(std::size_t n) {
void *p = std::malloc(n);
std::memset(p, 0, n);
return p;
}
void operator delete(void *p) {
std::free(p);
}
namespace {
using namespace gambatte;
GBEXPORT GB * gambatte_create() {
return new GB();
}
GBEXPORT void gambatte_destroy(GB *g) {
delete g;
}
GBEXPORT int gambatte_load(GB *g, char const *romfiledata, unsigned romfilelength, unsigned flags) {
return g->load(romfiledata, romfilelength, flags);
}
GBEXPORT int gambatte_loadbios(GB *g, char const *biosfiledata, unsigned size) {
return g->loadBios(biosfiledata, size);
}
GBEXPORT int gambatte_runfor(GB *g, short *soundbuf, unsigned *samples) {
std::size_t sampv = *samples;
int ret = g->runFor((unsigned int *) soundbuf, sampv);
*samples = sampv;
return ret;
}
GBEXPORT void gambatte_blitto(GB *g, unsigned int *videobuf, int pitch) {
g->blitTo((unsigned int *)videobuf, pitch);
}
GBEXPORT void gambatte_setlayers(GB *g, unsigned mask) {
g->setLayers(mask);
}
GBEXPORT void gambatte_settimemode(GB *g, bool useCycles) {
g->setTimeMode(useCycles);
}
GBEXPORT void gambatte_setrtcdivisoroffset(GB *g, int rtcDivisorOffset) {
g->setRtcDivisorOffset(rtcDivisorOffset);
}
GBEXPORT void gambatte_reset(GB *g) {
g->reset();
}
GBEXPORT void gambatte_setdmgpalettecolor(GB *g, unsigned palnum, unsigned colornum, unsigned rgb32) {
g->setDmgPaletteColor(palnum, colornum, rgb32);
}
GBEXPORT void gambatte_setcgbpalette(GB *g, unsigned *lut) {
g->setCgbPalette(lut);
}
GBEXPORT void gambatte_setinputgetter(GB *g, unsigned (*getinput)(void)) {
g->setInputGetter(getinput);
}
GBEXPORT void gambatte_setreadcallback(GB *g, MemoryCallback callback) {
g->setReadCallback(callback);
}
GBEXPORT void gambatte_setwritecallback(GB *g, MemoryCallback callback) {
g->setWriteCallback(callback);
}
GBEXPORT void gambatte_setexeccallback(GB *g, MemoryCallback callback) {
g->setExecCallback(callback);
}
GBEXPORT void gambatte_setcdcallback(GB *g, CDCallback cdc) {
g->setCDCallback(cdc);
}
GBEXPORT void gambatte_settracecallback(GB *g, void (*callback)(void *)) {
g->setTraceCallback(callback);
}
GBEXPORT void gambatte_setscanlinecallback(GB *g, void (*callback)(), int sl) {
g->setScanlineCallback(callback, sl);
}
GBEXPORT void gambatte_setlinkcallback(GB *g, void(*callback)()) {
g->setLinkCallback(callback);
}
GBEXPORT int gambatte_iscgb(GB *g) {
return g->isCgb();
}
GBEXPORT int gambatte_isloaded(GB *g) {
return g->isLoaded();
}
GBEXPORT void gambatte_savesavedata(GB *g, char *dest) {
g->saveSavedata(dest);
}
GBEXPORT void gambatte_loadsavedata(GB *g, char const *data) {
g->loadSavedata(data);
}
GBEXPORT int gambatte_savesavedatalength(GB *g) {
return g->saveSavedataLength();
}
GBEXPORT int gambatte_newstatelen(GB *g) {
NewStateDummy dummy;
g->SyncState<false>(&dummy);
return dummy.GetLength();
}
GBEXPORT int gambatte_newstatesave(GB *g, char *data, int len) {
NewStateExternalBuffer saver(data, len);
g->SyncState<false>(&saver);
return !saver.Overflow() && saver.GetLength() == len;
}
GBEXPORT int gambatte_newstateload(GB *g, char const *data, int len) {
NewStateExternalBuffer loader((char *)data, len);
g->SyncState<true>(&loader);
return !loader.Overflow() && loader.GetLength() == len;
}
GBEXPORT void gambatte_newstatesave_ex(GB *g, FPtrs *ff) {
NewStateExternalFunctions saver(ff);
g->SyncState<false>(&saver);
}
GBEXPORT void gambatte_newstateload_ex(GB *g, FPtrs *ff) {
NewStateExternalFunctions loader(ff);
g->SyncState<true>(&loader);
}
GBEXPORT void gambatte_romtitle(GB *g, char *dest) {
std::strcpy(dest, g->romTitle().c_str());
}
GBEXPORT int gambatte_getmemoryarea(GB *g, int which, unsigned char **data, int *length) {
return g->getMemoryArea(which, data, length);
}
GBEXPORT unsigned char gambatte_cpuread(GB *g, unsigned short addr) {
return g->externalRead(addr);
}
GBEXPORT void gambatte_cpuwrite(GB *g, unsigned short addr, unsigned char val) {
g->externalWrite(addr, val);
}
GBEXPORT int gambatte_linkstatus(GB *g, int which)
{
return g->linkStatus(which);
}
GBEXPORT void gambatte_getregs(GB *g, int *dest) {
g->getRegs(dest);
}
GBEXPORT void gambatte_setinterruptaddresses(GB *g, int *addrs, int numAddrs) {
g->setInterruptAddresses(addrs, numAddrs);
}
GBEXPORT int gambatte_gethitinterruptaddress(GB *g) {
return g->getHitInterruptAddress();
}
}

View File

@ -1,30 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef CINTERFACE_H
#define CINTERFACE_H
// these are all documented on the C# side
#ifdef _WIN32
#define GBEXPORT extern "C" __declspec(dllexport)
#elif __linux__
#define GBEXPORT extern "C"
#endif
#endif

View File

@ -1,10 +0,0 @@
#ifndef COUNTERDEF_H
#define COUNTERDEF_H
namespace gambatte {
enum { disabled_time = 0xfffffffful };
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,134 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef CPU_H
#define CPU_H
#include "memory.h"
#include "newstate.h"
namespace gambatte {
class CPU {
public:
CPU();
long runFor(unsigned long cycles);
void setStatePtrs(SaveState &state);
void loadState(SaveState const &state);
void setLayers(unsigned mask) { mem_.setLayers(mask); }
void loadSavedata(char const *data) { mem_.loadSavedata(data, cycleCounter_); }
int saveSavedataLength() {return mem_.saveSavedataLength(); }
void saveSavedata(char *dest) { mem_.saveSavedata(dest, cycleCounter_); }
bool getMemoryArea(int which, unsigned char **data, int *length) { return mem_.getMemoryArea(which, data, length); }
void setVideoBuffer(uint_least32_t *videoBuf, std::ptrdiff_t pitch) {
mem_.setVideoBuffer(videoBuf, pitch);
}
void setInputGetter(unsigned (*getInput)()) {
mem_.setInputGetter(getInput);
}
void setReadCallback(MemoryCallback callback) {
mem_.setReadCallback(callback);
}
void setWriteCallback(MemoryCallback callback) {
mem_.setWriteCallback(callback);
}
void setExecCallback(MemoryCallback callback) {
mem_.setExecCallback(callback);
}
void setCDCallback(CDCallback cdc) {
mem_.setCDCallback(cdc);
}
void setTraceCallback(void (*callback)(void *)) {
tracecallback = callback;
}
void setScanlineCallback(void (*callback)(), int sl) {
mem_.setScanlineCallback(callback, sl);
}
void setLinkCallback(void(*callback)()) {
mem_.setLinkCallback(callback);
}
LoadRes load(char const *romfiledata, unsigned romfilelength, unsigned flags) {
return mem_.loadROM(romfiledata, romfilelength, flags);
}
bool loaded() const { return mem_.loaded(); }
char const * romTitle() const { return mem_.romTitle(); }
void setSoundBuffer(uint_least32_t *buf) { mem_.setSoundBuffer(buf); }
std::size_t fillSoundBuffer() { return mem_.fillSoundBuffer(cycleCounter_); }
bool isCgb() const { return mem_.isCgb(); }
void setDmgPaletteColor(int palNum, int colorNum, unsigned long rgb32) {
mem_.setDmgPaletteColor(palNum, colorNum, rgb32);
}
void setCgbPalette(unsigned *lut) {
mem_.setCgbPalette(lut);
}
void setTimeMode(bool useCycles) { mem_.setTimeMode(useCycles, cycleCounter_); }
void setRtcDivisorOffset(long const rtcDivisorOffset) { mem_.setRtcDivisorOffset(rtcDivisorOffset); }
void setBios(char const *buffer, std::size_t size) { mem_.setBios(buffer, size); }
unsigned char externalRead(unsigned short addr) {return mem_.peek(addr); }
void externalWrite(unsigned short addr, unsigned char val) {
mem_.write_nocb(addr, val, cycleCounter_);
}
int linkStatus(int which) { return mem_.linkStatus(which); }
void getRegs(int *dest);
void setInterruptAddresses(int *addrs, int numAddrs);
int getHitInterruptAddress();
private:
Memory mem_;
unsigned long cycleCounter_;
unsigned short pc;
unsigned short sp;
unsigned hf1, hf2, zf, cf;
unsigned char a, b, c, d, e, /*f,*/ h, l;
unsigned char opcode_;
bool prefetched_;
int *interruptAddresses;
int numInterruptAddresses;
int hitInterruptAddress;
void process(unsigned long cycles);
void (*tracecallback)(void *);
public:
template<bool isReader>void SyncState(NewState *ns);
};
}
#endif

View File

@ -1,245 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "gambatte.h"
#include "cpu.h"
#include "initstate.h"
#include "savestate.h"
#include <cstring>
#include <sstream>
namespace gambatte {
struct GB::Priv {
CPU cpu;
unsigned loadflags;
unsigned layersMask;
uint_least32_t vbuff[160*144];
Priv() : loadflags(0), layersMask(layer_mask_bg | layer_mask_window | layer_mask_obj)
{
}
};
GB::GB() : p_(new Priv) {}
GB::~GB() {
delete p_;
}
std::ptrdiff_t GB::runFor(gambatte::uint_least32_t *const soundBuf, std::size_t &samples) {
if (!p_->cpu.loaded()) {
samples = 0;
return -1;
}
p_->cpu.setVideoBuffer(p_->vbuff, 160);
p_->cpu.setSoundBuffer(soundBuf);
long const cyclesSinceBlit = p_->cpu.runFor(samples * 2);
samples = p_->cpu.fillSoundBuffer();
return cyclesSinceBlit >= 0
? static_cast<std::ptrdiff_t>(samples) - (cyclesSinceBlit >> 1)
: cyclesSinceBlit;
}
void GB::setLayers(unsigned mask) {
p_->cpu.setLayers(mask);
}
void GB::blitTo(gambatte::uint_least32_t *videoBuf, std::ptrdiff_t pitch) {
gambatte::uint_least32_t *src = p_->vbuff;
gambatte::uint_least32_t *dst = videoBuf;
for (int i = 0; i < 144; i++)
{
std::memcpy(dst, src, sizeof gambatte::uint_least32_t * 160);
src += 160;
dst += pitch;
}
}
void GB::reset() {
if (p_->cpu.loaded()) {
int length = p_->cpu.saveSavedataLength();
char *s;
if (length > 0)
{
s = (char *) std::malloc(length);
p_->cpu.saveSavedata(s);
}
SaveState state;
p_->cpu.setStatePtrs(state);
setInitState(state, !(p_->loadflags & FORCE_DMG));
p_->cpu.loadState(state);
if (length > 0)
{
p_->cpu.loadSavedata(s);
std::free(s);
}
}
}
void GB::setInputGetter(unsigned (*getInput)()) {
p_->cpu.setInputGetter(getInput);
}
void GB::setReadCallback(MemoryCallback callback) {
p_->cpu.setReadCallback(callback);
}
void GB::setWriteCallback(MemoryCallback callback) {
p_->cpu.setWriteCallback(callback);
}
void GB::setExecCallback(MemoryCallback callback) {
p_->cpu.setExecCallback(callback);
}
void GB::setCDCallback(CDCallback cdc) {
p_->cpu.setCDCallback(cdc);
}
void GB::setTraceCallback(void (*callback)(void *)) {
p_->cpu.setTraceCallback(callback);
}
void GB::setScanlineCallback(void (*callback)(), int sl) {
p_->cpu.setScanlineCallback(callback, sl);
}
void GB::setLinkCallback(void(*callback)()) {
p_->cpu.setLinkCallback(callback);
}
void GB::setTimeMode(bool useCycles) {
p_->cpu.setTimeMode(useCycles);
}
void GB::setRtcDivisorOffset(long const rtcDivisorOffset) {
p_->cpu.setRtcDivisorOffset(rtcDivisorOffset);
}
LoadRes GB::load(char const *romfiledata, unsigned romfilelength, unsigned const flags) {
LoadRes const loadres = p_->cpu.load(romfiledata, romfilelength, flags);
if (loadres == LOADRES_OK) {
SaveState state;
p_->cpu.setStatePtrs(state);
p_->loadflags = flags;
setInitState(state, !(flags & FORCE_DMG));
p_->cpu.loadState(state);
}
return loadres;
}
int GB::loadBios(char const* biosfiledata, std::size_t size) {
p_->cpu.setBios(biosfiledata, size);
return 0;
}
bool GB::isCgb() const {
return p_->cpu.isCgb();
}
bool GB::isLoaded() const {
return p_->cpu.loaded();
}
void GB::saveSavedata(char *dest) {
if (p_->cpu.loaded())
p_->cpu.saveSavedata(dest);
}
void GB::loadSavedata(char const *data) {
if (p_->cpu.loaded())
p_->cpu.loadSavedata(data);
}
int GB::saveSavedataLength() {
if (p_->cpu.loaded())
return p_->cpu.saveSavedataLength();
else
return -1;
}
bool GB::getMemoryArea(int which, unsigned char **data, int *length) {
if (p_->cpu.loaded())
return p_->cpu.getMemoryArea(which, data, length);
else
return false;
}
unsigned char GB::externalRead(unsigned short addr) {
if (p_->cpu.loaded())
return p_->cpu.externalRead(addr);
else
return 0;
}
void GB::externalWrite(unsigned short addr, unsigned char val) {
if (p_->cpu.loaded())
p_->cpu.externalWrite(addr, val);
}
void GB::setDmgPaletteColor(int palNum, int colorNum, unsigned long rgb32) {
p_->cpu.setDmgPaletteColor(palNum, colorNum, rgb32);
}
void GB::setCgbPalette(unsigned *lut) {
p_->cpu.setCgbPalette(lut);
}
std::string const 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();
}
int GB::linkStatus(int which) {
return p_->cpu.linkStatus(which);
}
void GB::getRegs(int *dest) {
p_->cpu.getRegs(dest);
}
void GB::setInterruptAddresses(int *addrs, int numAddrs) {
p_->cpu.setInterruptAddresses(addrs, numAddrs);
}
int GB::getHitInterruptAddress() {
return p_->cpu.getHitInterruptAddress();
}
SYNCFUNC(GB)
{
SSS(p_->cpu);
NSS(p_->loadflags);
NSS(p_->vbuff);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,29 +0,0 @@
//
// Copyright (C) 2008 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef INITSTATE_H
#define INITSTATE_H
#include <cstdint>
namespace gambatte {
void setInitState(struct SaveState &state, bool cgb);
}
#endif

View File

@ -1,49 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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) {
T const 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 *start, T *end) {
insertionSort(start, end, std::less<T>());
}
#endif /*INSERTION_SORT_H*/

View File

@ -1,54 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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 Memory;
class Interrupter {
public:
Interrupter(unsigned short& sp, unsigned short& pc, unsigned char& opcode, bool& prefetched);
void prefetch(unsigned long cc, Memory& mem);
unsigned long interrupt(unsigned long cycleCounter, Memory& memory);
void setGameShark(std::string const& codes);
private:
unsigned short& sp_;
unsigned short& pc_;
unsigned char& opcode_;
bool& prefetched_;
std::vector<GsCode> gsCodes_;
void applyVblankCheats(unsigned long cc, Memory& mem);
};
}
#endif

View File

@ -1,132 +0,0 @@
//
// Copyright (C) 2010 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "interruptrequester.h"
#include "savestate.h"
namespace gambatte {
InterruptRequester::InterruptRequester()
: eventTimes_(disabled_time)
, minIntTime_(0)
, ifreg_(0)
, iereg_(0)
{
}
void InterruptRequester::loadState(SaveState const &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<intevent_interrupts>(intFlags_.imeOrHalted() && pendingIrqs()
? minIntTime_
: static_cast<unsigned long>(disabled_time));
}
void InterruptRequester::resetCc(unsigned long oldCc, unsigned long newCc) {
minIntTime_ = minIntTime_ < oldCc ? 0 : minIntTime_ - (oldCc - newCc);
if (eventTimes_.value(intevent_interrupts) != disabled_time)
eventTimes_.setValue<intevent_interrupts>(minIntTime_);
}
void InterruptRequester::ei(unsigned long cc) {
intFlags_.setIme();
minIntTime_ = cc + 1;
if (pendingIrqs())
eventTimes_.setValue<intevent_interrupts>(minIntTime_);
}
void InterruptRequester::di() {
intFlags_.unsetIme();
if (!intFlags_.imeOrHalted())
eventTimes_.setValue<intevent_interrupts>(disabled_time);
}
void InterruptRequester::halt() {
intFlags_.setHalted();
if (pendingIrqs())
eventTimes_.setValue<intevent_interrupts>(minIntTime_);
}
void InterruptRequester::unhalt() {
intFlags_.unsetHalted();
if (!intFlags_.imeOrHalted())
eventTimes_.setValue<intevent_interrupts>(disabled_time);
}
void InterruptRequester::flagIrq(unsigned bit) {
ifreg_ |= bit;
if (intFlags_.imeOrHalted() && pendingIrqs())
eventTimes_.setValue<intevent_interrupts>(minIntTime_);
}
void InterruptRequester::flagIrq(unsigned bit, unsigned long cc) {
unsigned const prevPending = pendingIrqs();
ifreg_ |= bit;
if (!prevPending && pendingIrqs() && intFlags_.imeOrHalted()) {
minIntTime_ = std::max(minIntTime_, cc);
eventTimes_.setValue<intevent_interrupts>(minIntTime_);
}
}
void InterruptRequester::setIereg(unsigned iereg) {
iereg_ = iereg & 0x1F;
if (intFlags_.imeOrHalted()) {
eventTimes_.setValue<intevent_interrupts>(pendingIrqs()
? minIntTime_
: static_cast<unsigned long>(disabled_time));
}
}
void InterruptRequester::setIfreg(unsigned ifreg) {
ifreg_ = ifreg;
if (intFlags_.imeOrHalted()) {
eventTimes_.setValue<intevent_interrupts>(pendingIrqs()
? minIntTime_
: static_cast<unsigned long>(disabled_time));
}
}
void InterruptRequester::setMinIntTime(unsigned long cc) {
minIntTime_ = cc;
if (eventTimes_.value(intevent_interrupts) < minIntTime_)
eventTimes_.setValue<intevent_interrupts>(minIntTime_);
}
SYNCFUNC(InterruptRequester)
{
SSS(eventTimes_);
NSS(minIntTime_);
NSS(ifreg_);
NSS(iereg_);
NSS(intFlags_.flags_);
}
}

View File

@ -1,105 +0,0 @@
//
// Copyright (C) 2010 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef INTERRUPT_REQUESTER_H
#define INTERRUPT_REQUESTER_H
#include "counterdef.h"
#include "minkeeper.h"
#include "newstate.h"
namespace gambatte {
struct SaveState;
enum IntEventId { intevent_unhalt,
intevent_end,
intevent_blit,
intevent_serial,
intevent_oam,
intevent_dma,
intevent_tima,
intevent_video,
intevent_interrupts, intevent_last = intevent_interrupts };
class InterruptRequester {
public:
InterruptRequester();
void loadState(SaveState const &);
void resetCc(unsigned long oldCc, unsigned long newCc);
unsigned ifreg() const { return ifreg_; }
unsigned iereg() const { return iereg_; }
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 flagIrq(unsigned bit, unsigned long cc);
void ackIrq(unsigned bit) { ifreg_ &= ~bit; }
void setIereg(unsigned iereg);
void setIfreg(unsigned ifreg);
void setMinIntTime(unsigned long cc);
IntEventId minEventId() const { return static_cast<IntEventId>(eventTimes_.min()); }
unsigned long minEventTime() const { return eventTimes_.minValue(); }
template<IntEventId id> void setEventTime(unsigned long value) { eventTimes_.setValue<id>(value); }
void setEventTime(IntEventId id, unsigned long value) { eventTimes_.setValue(id, value); }
unsigned long eventTime(IntEventId id) const { return eventTimes_.value(id); }
private:
class IntFlags {
friend class InterruptRequester;
public:
IntFlags() : flags_(0) {}
bool ime() const { return flags_ & flag_ime; }
bool halted() const { return flags_ & flag_halted; }
bool imeOrHalted() const { return flags_; }
void setIme() { flags_ |= flag_ime; }
void unsetIme() { flags_ &= ~(1u * flag_ime); }
void setHalted() { flags_ |= flag_halted; }
void unsetHalted() { flags_ &= ~(1u * flag_halted); }
void set(bool ime, bool halted) { flags_ = halted * flag_halted + ime * flag_ime; }
private:
unsigned char flags_;
enum { flag_ime = 1, flag_halted = 2 };
};
MinKeeper<intevent_last + 1> eventTimes_;
unsigned long minIntTime_;
unsigned ifreg_;
unsigned iereg_;
IntFlags intFlags_;
public:
template<bool isReader>void SyncState(NewState *ns);
};
inline void flagHdmaReq(InterruptRequester &intreq) { intreq.setEventTime<intevent_dma>(0); }
inline void flagGdmaReq(InterruptRequester &intreq) { intreq.setEventTime<intevent_dma>(1); }
inline void ackDmaReq(InterruptRequester &intreq) { intreq.setEventTime<intevent_dma>(disabled_time); }
inline bool hdmaReqFlagged(InterruptRequester const &intreq) { return intreq.eventTime(intevent_dma) == 0; }
inline bool gdmaReqFlagged(InterruptRequester const &intreq) { return intreq.eventTime(intevent_dma) == 1; }
}
#endif

View File

@ -1,904 +0,0 @@
//
// Copyright (C) 2007-2010 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "cartridge.h"
#include "../savestate.h"
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <fstream>
using namespace gambatte;
namespace {
unsigned toMulti64Rombank(unsigned rombank) {
return (rombank >> 1 & 0x30) | (rombank & 0xF);
}
class DefaultMbc : public Mbc {
public:
virtual bool isAddressWithinAreaRombankCanBeMappedTo(unsigned addr, unsigned bank) const {
return (addr < rombank_size()) == (bank == 0);
}
virtual void SyncState(NewState *ns, bool isReader)
{
}
};
class Mbc0 : public DefaultMbc {
public:
explicit Mbc0(MemPtrs &memptrs)
: memptrs_(memptrs)
, enableRam_(false)
{
}
virtual unsigned char curRomBank() const {
return 1;
}
virtual void romWrite(unsigned const p, unsigned const data, unsigned long const /*cc*/) {
if (p < rambank_size()) {
enableRam_ = (data & 0xF) == 0xA;
memptrs_.setRambank(enableRam_ ? MemPtrs::read_en | MemPtrs::write_en : 0, 0);
}
}
virtual void loadState(SaveState::Mem const &ss) {
enableRam_ = ss.enableRam;
memptrs_.setRambank(enableRam_ ? MemPtrs::read_en | MemPtrs::write_en : 0, 0);
}
private:
MemPtrs &memptrs_;
bool enableRam_;
public:
virtual void SyncState(NewState *ns, bool isReader)
{
NSS(enableRam_);
}
};
inline unsigned rambanks(MemPtrs const &memptrs) {
return (memptrs.rambankdataend() - memptrs.rambankdata()) / rambank_size();
}
inline unsigned rombanks(MemPtrs const &memptrs) {
return (memptrs.romdataend() - memptrs.romdata()) / rombank_size();
}
class Mbc1 : public DefaultMbc {
public:
explicit Mbc1(MemPtrs &memptrs)
: memptrs_(memptrs)
, rombank_(1)
, rambank_(0)
, enableRam_(false)
, rambankMode_(false)
{
}
virtual unsigned char curRomBank() const {
return rombank_;
}
virtual void romWrite(unsigned const p, unsigned const data, unsigned long const /*cc*/) {
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:
// Should this take effect immediately rather?
rambankMode_ = data & 1;
break;
}
}
virtual void loadState(SaveState::Mem const &ss) {
rombank_ = ss.rombank;
rambank_ = ss.rambank;
enableRam_ = ss.enableRam;
rambankMode_ = ss.rambankMode;
setRambank();
setRombank();
}
private:
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:
virtual void SyncState(NewState *ns, bool isReader)
{
NSS(rombank_);
NSS(rambank_);
NSS(enableRam_);
NSS(rambankMode_);
}
};
class Mbc1Multi64 : public Mbc {
public:
explicit Mbc1Multi64(MemPtrs &memptrs)
: memptrs_(memptrs)
, rombank_(1)
, enableRam_(false)
, rombank0Mode_(false)
{
}
virtual unsigned char curRomBank() const {
return rombank_;
}
virtual void romWrite(unsigned const p, unsigned const data, unsigned long const /*cc*/) {
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(rombank0Mode_
? adjustedRombank(toMulti64Rombank(rombank_))
: adjustedRombank(rombank_) & (rombanks(memptrs_) - 1));
break;
case 2:
rombank_ = (data << 5 & 0x60) | (rombank_ & 0x1F);
setRombank();
break;
case 3:
rombank0Mode_ = data & 1;
setRombank();
break;
}
}
virtual void loadState(SaveState::Mem const &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);
}
private:
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_) {
unsigned const rb = toMulti64Rombank(rombank_);
memptrs_.setRombank0(rb & 0x30);
memptrs_.setRombank(adjustedRombank(rb));
} else {
memptrs_.setRombank0(0);
memptrs_.setRombank(adjustedRombank(rombank_) & (rombanks(memptrs_) - 1));
}
}
public:
virtual void SyncState(NewState *ns, bool isReader)
{
NSS(rombank_);
NSS(enableRam_);
NSS(rombank0Mode_);
}
};
class Mbc2 : public DefaultMbc {
public:
explicit Mbc2(MemPtrs &memptrs)
: memptrs_(memptrs)
, rombank_(1)
, enableRam_(false)
{
}
virtual unsigned char curRomBank() const {
return rombank_;
}
virtual void romWrite(unsigned const p, unsigned const data, unsigned long const /*cc*/) {
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 loadState(SaveState::Mem const &ss) {
rombank_ = ss.rombank;
enableRam_ = ss.enableRam;
memptrs_.setRambank(enableRam_ ? MemPtrs::read_en | MemPtrs::write_en : 0, 0);
memptrs_.setRombank(rombank_ & (rombanks(memptrs_) - 1));
}
private:
MemPtrs &memptrs_;
unsigned char rombank_;
bool enableRam_;
public:
virtual void SyncState(NewState *ns, bool isReader)
{
NSS(rombank_);
NSS(enableRam_);
}
};
class Mbc3 : public DefaultMbc {
public:
Mbc3(MemPtrs &memptrs, Rtc *const rtc)
: memptrs_(memptrs)
, rtc_(rtc)
, rombank_(1)
, rambank_(0)
, enableRam_(false)
{
}
virtual unsigned char curRomBank() const {
return rombank_;
}
virtual void romWrite(unsigned const p, unsigned const data, unsigned long const cc) {
switch (p >> 13 & 3) {
case 0:
enableRam_ = (data & 0xF) == 0xA;
setRambank();
break;
case 1:
rombank_ = data & 0x7F;
setRombank();
break;
case 2:
rambank_ = data;
setRambank();
break;
case 3:
if (rtc_)
rtc_->latch(data, cc);
break;
}
}
virtual void loadState(SaveState::Mem const &ss) {
rombank_ = ss.rombank;
rambank_ = ss.rambank;
enableRam_ = ss.enableRam;
setRambank();
setRombank();
}
private:
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_->activeData())
flags |= MemPtrs::rtc_en;
}
memptrs_.setRambank(flags, rambank_ & (rambanks(memptrs_) - 1));
}
void setRombank() const {
memptrs_.setRombank(std::max(rombank_ & (rombanks(memptrs_) - 1), 1u));
}
public:
virtual void SyncState(NewState *ns, bool isReader)
{
NSS(rombank_);
NSS(rambank_);
NSS(enableRam_);
}
};
class HuC1 : public DefaultMbc {
public:
explicit HuC1(MemPtrs &memptrs)
: memptrs_(memptrs)
, rombank_(1)
, rambank_(0)
, enableRam_(false)
, rambankMode_(false)
{
}
virtual unsigned char curRomBank() const {
return rombank_;
}
virtual void romWrite(unsigned const p, unsigned const data, unsigned long const /*cc*/) {
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 loadState(SaveState::Mem const &ss) {
rombank_ = ss.rombank;
rambank_ = ss.rambank;
enableRam_ = ss.enableRam;
rambankMode_ = ss.rambankMode;
setRambank();
setRombank();
}
private:
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:
virtual void SyncState(NewState *ns, bool isReader)
{
NSS(rombank_);
NSS(rambank_);
NSS(enableRam_);
NSS(rambankMode_);
}
};
class HuC3 : public DefaultMbc {
public:
HuC3(MemPtrs& memptrs, HuC3Chip* const huc3)
: memptrs_(memptrs)
, huc3_(huc3)
, rombank_(1)
, rambank_(0)
, ramflag_(0)
{
}
virtual unsigned char curRomBank() const {
return rombank_;
}
virtual bool disabledRam() const {
return false;
}
virtual void romWrite(unsigned const p, unsigned const data, unsigned long const /*cc*/) {
switch (p >> 13 & 3) {
case 0:
ramflag_ = data;
//printf("[HuC3] set ramflag to %02X\n", data);
setRambank();
break;
case 1:
//printf("[HuC3] set rombank to %02X\n", data);
rombank_ = data;
setRombank();
break;
case 2:
//printf("[HuC3] set rambank to %02X\n", data);
rambank_ = data;
setRambank();
break;
case 3:
// GEST: "programs will write 1 here"
break;
}
}
virtual void SyncState(NewState *ns, bool isReader)
{
NSS(rombank_);
NSS(rambank_);
NSS(ramflag_);
}
virtual void loadState(SaveState::Mem const& ss) {
rombank_ = ss.rombank;
rambank_ = ss.rambank;
ramflag_ = ss.HuC3RAMflag;
setRambank();
setRombank();
}
private:
MemPtrs& memptrs_;
HuC3Chip* const huc3_;
unsigned char rombank_;
unsigned char rambank_;
unsigned char ramflag_;
void setRambank() const {
huc3_->setRamflag(ramflag_);
unsigned flags;
if (ramflag_ >= 0x0B && ramflag_ < 0x0F) {
// System registers mode
flags = MemPtrs::read_en | MemPtrs::write_en | MemPtrs::rtc_en;
}
else if (ramflag_ == 0x0A || ramflag_ > 0x0D) {
// Read/write mode
flags = MemPtrs::read_en | MemPtrs::write_en;
}
else {
// Read-only mode ??
flags = MemPtrs::read_en;
}
memptrs_.setRambank(flags, rambank_ & (rambanks(memptrs_) - 1));
}
void setRombank() const {
memptrs_.setRombank(std::max(rombank_ & (rombanks(memptrs_) - 1), 1u));
}
};
class Mbc5 : public DefaultMbc {
public:
explicit Mbc5(MemPtrs &memptrs)
: memptrs_(memptrs)
, rombank_(1)
, rambank_(0)
, enableRam_(false)
{
}
virtual unsigned char curRomBank() const {
return rombank_;
}
virtual void romWrite(unsigned const p, unsigned const data, unsigned long const /*cc*/) {
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 loadState(SaveState::Mem const &ss) {
rombank_ = ss.rombank;
rambank_ = ss.rambank;
enableRam_ = ss.enableRam;
setRambank();
setRombank();
}
private:
MemPtrs &memptrs_;
unsigned short rombank_;
unsigned char rambank_;
bool enableRam_;
void setRambank() const {
memptrs_.setRambank(enableRam_ ? MemPtrs::read_en | MemPtrs::write_en : 0,
rambank_ & (rambanks(memptrs_) - 1));
}
void setRombank() const { memptrs_.setRombank(rombank_ & (rombanks(memptrs_) - 1)); }
public:
virtual void SyncState(NewState *ns, bool isReader)
{
NSS(rombank_);
NSS(rambank_);
NSS(enableRam_);
}
};
std::string stripExtension(std::string const& str) {
std::string::size_type const lastDot = str.find_last_of('.');
std::string::size_type const lastSlash = str.find_last_of('/');
if (lastDot != std::string::npos && (lastSlash == std::string::npos || lastSlash < lastDot))
return str.substr(0, lastDot);
return str;
}
std::string stripDir(std::string const& str) {
std::string::size_type const lastSlash = str.find_last_of('/');
if (lastSlash != std::string::npos)
return str.substr(lastSlash + 1);
return str;
}
void enforce8bit(unsigned char* data, std::size_t size) {
if (static_cast<unsigned char>(0x100))
while (size--)
*data++ &= 0xFF;
}
unsigned pow2ceil(unsigned n) {
--n;
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
++n;
return n;
}
bool presumedMulti64Mbc1(unsigned char const header[], unsigned rombanks) {
return header[0x147] == 1 && header[0x149] == 0 && rombanks == 64;
}
bool hasBattery(unsigned char headerByte0x147) {
switch (headerByte0x147) {
case 0x03:
case 0x06:
case 0x09:
case 0x0F:
case 0x10:
case 0x13:
case 0x1B:
case 0x1E:
case 0xFE: // huc3
case 0xFF:
return true;
}
return false;
}
bool hasRtc(unsigned headerByte0x147) {
switch (headerByte0x147) {
case 0x0F:
case 0x10:
case 0xFE: // huc3
return true;
}
return false;
}
int asHex(char c) {
return c >= 'A' ? c - 'A' + 0xA : c - '0';
}
}
Cartridge::Cartridge()
: rtc_(time_)
, huc3_(time_)
{
}
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::loadState(SaveState const &state) {
huc3_.loadState(state);
rtc_.loadState(state);
time_.loadState(state);
mbc_->loadState(state.mem);
}
static bool isMbc2(unsigned char h147) { return h147 == 5 || h147 == 6; }
static unsigned numRambanksFromH14x(unsigned char h147, unsigned char h149) {
switch (h149) {
case 0x00: return isMbc2(h147) ? 1 : 0;
case 0x01:
case 0x02: return 1;
case 0x03: return 4;
case 0x04: return 16;
case 0x05: return 8;
}
return 4;
}
LoadRes Cartridge::loadROM(char const *romfiledata, unsigned romfilelength, bool const forceDmg, bool const multicartCompat) {
enum Cartridgetype { type_plain,
type_mbc1,
type_mbc2,
type_mbc3,
type_mbc5,
type_huc1,
type_huc3 };
Cartridgetype type = type_plain;
unsigned rambanks = 1;
unsigned rombanks = 2;
bool cgb = false;
{
unsigned char header[0x150];
if (romfilelength >= sizeof header)
std::memcpy(header, romfiledata, sizeof header);
else
return LOADRES_IO_ERROR;
switch (header[0x0147]) {
case 0x00: type = type_plain; break;
case 0x01:
case 0x02:
case 0x03: type = type_mbc1; break;
case 0x05:
case 0x06: type = type_mbc2; break;
case 0x08:
case 0x09: type = type_plain; break;
case 0x0B:
case 0x0C:
case 0x0D: return LOADRES_UNSUPPORTED_MBC_MMM01;
case 0x0F:
case 0x10:
case 0x11:
case 0x12:
case 0x13: type = type_mbc3; break;
case 0x15:
case 0x16:
case 0x17: return LOADRES_UNSUPPORTED_MBC_MBC4;
case 0x19:
case 0x1A:
case 0x1B:
case 0x1C:
case 0x1D:
case 0x1E: type = type_mbc5; break;
case 0x20: return LOADRES_UNSUPPORTED_MBC_MBC6;
case 0x22: return LOADRES_UNSUPPORTED_MBC_MBC7;
case 0xFC: return LOADRES_UNSUPPORTED_MBC_POCKET_CAMERA;
case 0xFD: return LOADRES_UNSUPPORTED_MBC_TAMA5;
case 0xFE: type = type_huc3; break;
case 0xFF: type = type_huc1; break;
default: return LOADRES_BAD_FILE_OR_UNKNOWN_MBC;
}
/*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;
}*/
rambanks = numRambanksFromH14x(header[0x147], header[0x149]);
cgb = !forceDmg;
}
std::size_t const filesize = romfilelength;
rombanks = std::max(pow2ceil(filesize / rombank_size()), 2u);
mbc_.reset();
memptrs_.reset(rombanks, rambanks, cgb ? 8 : 2);
rtc_.set(false, 0);
huc3_.set(false);
std::memcpy(memptrs_.romdata(), romfiledata, (filesize / rombank_size() * rombank_size()));
std::memset(memptrs_.romdata() + filesize / rombank_size() * rombank_size(),
0xFF,
(rombanks - filesize / rombank_size()) * rombank_size());
enforce8bit(memptrs_.romdata(), rombanks * rombank_size());
switch (type) {
case type_plain: mbc_.reset(new Mbc0(memptrs_)); break;
case type_mbc1:
if (multicartCompat && presumedMulti64Mbc1(memptrs_.romdata(), rombanks)) {
mbc_.reset(new Mbc1Multi64(memptrs_));
} else
mbc_.reset(new Mbc1(memptrs_));
break;
case type_mbc2: mbc_.reset(new Mbc2(memptrs_)); break;
case type_mbc3:
mbc_.reset(new Mbc3(memptrs_, hasRtc(memptrs_.romdata()[0x147]) ? &rtc_ : 0));
break;
case type_mbc5: mbc_.reset(new Mbc5(memptrs_)); break;
case type_huc1: mbc_.reset(new HuC1(memptrs_)); break;
case type_huc3:
huc3_.set(true);
mbc_.reset(new HuC3(memptrs_, &huc3_));
break;
}
return LOADRES_OK;
}
void Cartridge::loadSavedata(char const *data, unsigned long const cc) {
if (hasBattery(memptrs_.romdata()[0x147])) {
int length = memptrs_.rambankdataend() - memptrs_.rambankdata();
std::memcpy(memptrs_.rambankdata(), data, length);
data += length;
enforce8bit(memptrs_.rambankdata(), length);
}
if (hasRtc(memptrs_.romdata()[0x147])) {
timeval basetime;
basetime.tv_sec = (*data++);
basetime.tv_sec = basetime.tv_sec << 8 | (*data++);
basetime.tv_sec = basetime.tv_sec << 8 | (*data++);
basetime.tv_sec = basetime.tv_sec << 8 | (*data++);
basetime.tv_usec = (*data++);
basetime.tv_usec = basetime.tv_usec << 8 | (*data++);
basetime.tv_usec = basetime.tv_usec << 8 | (*data++);
basetime.tv_usec = basetime.tv_usec << 8 | (*data++);
time_.setBaseTime(basetime, cc);
}
}
int Cartridge::saveSavedataLength() {
int ret = 0;
if (hasBattery(memptrs_.romdata()[0x147])) {
ret = memptrs_.rambankdataend() - memptrs_.rambankdata();
}
if (hasRtc(memptrs_.romdata()[0x147])) {
ret += 8;
}
return ret;
}
void Cartridge::saveSavedata(char *dest, unsigned long const cc) {
if (hasBattery(memptrs_.romdata()[0x147])) {
int length = memptrs_.rambankdataend() - memptrs_.rambankdata();
std::memcpy(dest, memptrs_.rambankdata(), length);
dest += length;
}
if (hasRtc(memptrs_.romdata()[0x147])) {
timeval basetime = time_.baseTime(cc);
*dest++ = (basetime.tv_sec >> 24 & 0xFF);
*dest++ = (basetime.tv_sec >> 16 & 0xFF);
*dest++ = (basetime.tv_sec >> 8 & 0xFF);
*dest++ = (basetime.tv_sec & 0xFF);
*dest++ = (basetime.tv_usec >> 24 & 0xFF);
*dest++ = (basetime.tv_usec >> 16 & 0xFF);
*dest++ = (basetime.tv_usec >> 8 & 0xFF);
*dest++ = (basetime.tv_usec & 0xFF);
}
}
bool Cartridge::getMemoryArea(int which, unsigned char **data, int *length) const {
if (!data || !length)
return false;
switch (which)
{
case 0:
*data = memptrs_.vramdata();
*length = memptrs_.vramdataend() - memptrs_.vramdata();
return true;
case 1:
*data = memptrs_.romdata();
*length = memptrs_.romdataend() - memptrs_.romdata();
return true;
case 2:
*data = memptrs_.wramdata(0);
*length = memptrs_.wramdataend() - memptrs_.wramdata(0);
return true;
case 3:
*data = memptrs_.rambankdata();
*length = memptrs_.rambankdataend() - memptrs_.rambankdata();
return true;
default:
return false;
}
return false;
}
SYNCFUNC(Cartridge)
{
SSS(huc3_);
SSS(memptrs_);
SSS(time_);
SSS(rtc_);
TSS(mbc_);
}

View File

@ -1,103 +0,0 @@
//
// Copyright (C) 2007-2010 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef CARTRIDGE_H
#define CARTRIDGE_H
#include "loadres.h"
#include "memptrs.h"
#include "time.h"
#include "rtc.h"
#include "huc3.h"
#include "savestate.h"
#include <memory>
#include <string>
#include <vector>
#include "newstate.h"
namespace gambatte {
class Mbc {
public:
virtual ~Mbc() {}
virtual unsigned char curRomBank() const = 0;
virtual void romWrite(unsigned P, unsigned data, unsigned long cycleCounter) = 0;
virtual void loadState(SaveState::Mem const &ss) = 0;
virtual bool isAddressWithinAreaRombankCanBeMappedTo(unsigned address, unsigned rombank) const = 0;
template<bool isReader>void SyncState(NewState *ns)
{
// can't have virtual templates, so..
SyncState(ns, isReader);
}
virtual void SyncState(NewState *ns, bool isReader) = 0;
};
class Cartridge {
public:
Cartridge();
void setStatePtrs(SaveState &);
void loadState(SaveState const &);
bool loaded() const { return mbc_.get(); }
unsigned char const * 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); }
unsigned char const * rdisabledRam() const { return memptrs_.rdisabledRam(); }
unsigned char const * 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(); }
bool isInOamDmaConflictArea(unsigned p) const { return memptrs_.isInOamDmaConflictArea(p); }
void setVrambank(unsigned bank) { memptrs_.setVrambank(bank); }
void setWrambank(unsigned bank) { memptrs_.setWrambank(bank); }
void setOamDmaSrc(OamDmaSrc oamDmaSrc) { memptrs_.setOamDmaSrc(oamDmaSrc); }
unsigned char curRomBank() const { return mbc_->curRomBank(); }
void mbcWrite(unsigned addr, unsigned data, unsigned long const cc) { mbc_->romWrite(addr, data, cc); }
bool isCgb() const { return gambatte::isCgb(memptrs_); }
void resetCc(unsigned long const oldCc, unsigned long const newCc) { time_.resetCc(oldCc, newCc); }
void speedChange(unsigned long const cc) { time_.speedChange(cc); }
void setTimeMode(bool useCycles, unsigned long const cc) { time_.setTimeMode(useCycles, cc); }
void setRtcDivisorOffset(long const rtcDivisorOffset) { time_.setRtcDivisorOffset(rtcDivisorOffset); }
void rtcWrite(unsigned data, unsigned long const cc) { rtc_.write(data, cc); }
unsigned char rtcRead() const { return *rtc_.activeData(); }
void loadSavedata(char const *data, unsigned long cycleCounter);
int saveSavedataLength();
void saveSavedata(char *dest, unsigned long cycleCounter);
bool getMemoryArea(int which, unsigned char **data, int *length) const;
LoadRes loadROM(char const *romfiledata, unsigned romfilelength, bool forceDmg, bool multicartCompat);
char const * romTitle() const { return reinterpret_cast<char const *>(memptrs_.romdata() + 0x134); }
bool isHuC3() const { return huc3_.isHuC3(); }
unsigned char HuC3Read(unsigned p, unsigned long const cc) { return huc3_.read(p, cc); }
void HuC3Write(unsigned p, unsigned data, unsigned long const cc) { huc3_.write(p, data, cc); }
private:
MemPtrs memptrs_;
Time time_;
Rtc rtc_;
HuC3Chip huc3_;
std::unique_ptr<Mbc> mbc_;
public:
template<bool isReader>void SyncState(NewState *ns);
};
}
#endif

View File

@ -1,201 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "huc3.h"
#include "../savestate.h"
#include <stdio.h>
namespace gambatte {
HuC3Chip::HuC3Chip(Time &time)
: time_(time)
, haltTime_(0)
, dataTime_(0)
, writingTime_(0)
, ramValue_(0)
, shift_(0)
, ramflag_(0)
, modeflag_(HUC3_NONE)
, irBaseCycle_(0)
, enabled_(false)
, lastLatchData_(false)
, halted_(false)
, irReceivingPulse_(false)
{
}
void HuC3Chip::doLatch(unsigned long const cc) {
std::uint32_t tmp = time(cc);
unsigned minute = (tmp / 60) % 1440;
unsigned day = (tmp / 86400) & 0xFFF;
dataTime_ = (day << 12) | minute;
}
//void HuC3Chip::setStatePtrs(SaveState &state) {
// state.huc3.haltTime.set(haltTime_, sizeof haltTime_);
// state.huc3.dataTime.set(dataTime_, sizeof dataTime_);
// state.huc3.writingTime.set(writingTime_, sizeof writingTime_);
// state.huc3.irBaseCycle.set(irBaseCycle_, sizeof irBaseCycle_);
// state.huc3.halted.set(halted_, sizeof halted_);
// state.huc3.shift.set(shift_, sizeof shift_);
// state.huc3.ramValue.set(ramValue_, sizeof ramValue_);
// state.huc3.modeflag.set(modeflag_, sizeof modeflag_);
// state.huc3.irReceivingPulse.set(irReceivingPulse_, sizeof irReceivingPulse_);
//}
void HuC3Chip::loadState(SaveState const &state) {
haltTime_ = state.huc3.haltTime;
dataTime_ = state.huc3.dataTime;
ramValue_ = state.huc3.ramValue;
shift_ = state.huc3.shift;
halted_ = state.huc3.halted;
modeflag_ = state.huc3.modeflag;
writingTime_ = state.huc3.writingTime;
irBaseCycle_ = state.huc3.irBaseCycle;
irReceivingPulse_ = state.huc3.irReceivingPulse;
}
unsigned char HuC3Chip::read(unsigned /*p*/, unsigned long const cc) {
// should only reach here with ramflag = 0B-0E
if(ramflag_ == 0x0E) {
// INFRARED
if(!irReceivingPulse_) {
irReceivingPulse_ = true;
irBaseCycle_ = cc;
}
unsigned long cyclesSinceStart = cc - irBaseCycle_;
unsigned char modulation = (cyclesSinceStart/105) & 1; // 4194304 Hz CPU, 40000 Hz remote signal
unsigned long timeUs = cyclesSinceStart*36/151; // actually *1000000/4194304
// sony protocol
if(timeUs < 10000) {
// initialization allowance
return 0;
}
else if(timeUs < 10000 + 2400) {
// initial mark
return modulation;
}
else if(timeUs < 10000 + 2400 + 600) {
// initial space
return 0;
}
else {
// send data
timeUs -= 13000;
// write 20 bits (any 20 seem to do)
unsigned int data = 0xFFFFF;
for(unsigned long mask = 1UL << (20-1); mask; mask >>= 1) {
unsigned int markTime = (data & mask) ? 1200 : 600;
if(timeUs < markTime) { return modulation; }
timeUs -= markTime;
if(timeUs < 600) { return 0; }
timeUs -= 600;
}
return 0;
}
}
if(ramflag_ < 0x0B || ramflag_ > 0x0D) {
//printf("[HuC3] error, hit huc3 read with ramflag=%02X\n", ramflag_);
return 0xFF;
}
if(ramflag_ == 0x0D) return 1;
else return ramValue_;
}
void HuC3Chip::write(unsigned /*p*/, unsigned data, unsigned long const cc) {
// as above
if(ramflag_ == 0x0B) {
// command
switch(data & 0xF0) {
case 0x10:
// read time
doLatch(cc);
if(modeflag_ == HUC3_READ) {
ramValue_ = (dataTime_ >> shift_) & 0x0F;
shift_ += 4;
if(shift_ > 24) shift_ = 0;
}
break;
case 0x30:
// write time
if(modeflag_ == HUC3_WRITE) {
if(shift_ == 0) writingTime_ = 0;
if(shift_ < 24) {
writingTime_ |= (data & 0x0F) << shift_;
shift_ += 4;
if(shift_ == 24) {
updateTime(cc);
modeflag_ = HUC3_READ;
}
}
}
break;
case 0x40:
// some kind of mode shift
switch(data & 0x0F) {
case 0x0:
// shift reset?
shift_ = 0;
break;
case 0x3:
// write time?
modeflag_ = HUC3_WRITE;
shift_ = 0;
break;
case 0x7:
modeflag_ = HUC3_READ;
shift_ = 0;
break;
// others are unimplemented so far
}
break;
case 0x50:
// ???
break;
case 0x60:
modeflag_ = HUC3_READ; // ???
break;
}
}
// do nothing for 0C/0D yet
}
void HuC3Chip::updateTime(unsigned long const cc) {
unsigned minute = (writingTime_ & 0xFFF) % 1440;
unsigned day = (writingTime_ & 0xFFF000) >> 12;
std::uint32_t seconds = minute*60 + day*86400;
time_.reset(seconds, cc);
haltTime_ = seconds;
}
SYNCFUNC(HuC3Chip)
{
NSS(haltTime_);
NSS(dataTime_);
NSS(writingTime_);
NSS(ramValue_);
NSS(shift_);
NSS(halted_);
NSS(modeflag_);
NSS(irBaseCycle_);
NSS(irReceivingPulse_);
}
}

View File

@ -1,76 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef HuC3Chip_H
#define HuC3Chip_H
enum
{
HUC3_READ = 0,
HUC3_WRITE = 1,
HUC3_NONE = 2
};
#include "time.h"
namespace gambatte {
struct SaveState;
class HuC3Chip {
public:
HuC3Chip(Time &time);
//void setStatePtrs(SaveState &);
void loadState(SaveState const& state);
void setRamflag(unsigned char ramflag) { ramflag_ = ramflag; irReceivingPulse_ = false; }
bool isHuC3() const { return enabled_; }
void set(bool enabled) {
enabled_ = enabled;
}
unsigned char read(unsigned p, unsigned long const cc);
void write(unsigned p, unsigned data, unsigned long cycleCounter);
private:
Time &time_;
std::uint32_t haltTime_;
unsigned dataTime_;
unsigned writingTime_;
unsigned char ramValue_;
unsigned char shift_;
unsigned char ramflag_;
unsigned char modeflag_;
unsigned long irBaseCycle_;
bool enabled_;
bool lastLatchData_;
bool halted_;
bool irReceivingPulse_;
void doLatch(unsigned long cycleCounter);
void updateTime(unsigned long cycleCounter);
std::uint32_t time(unsigned long const cc) {
return halted_ ? haltTime_ : time_.get(cc);
}
public:
template<bool isReader>void SyncState(NewState* ns);
};
}
#endif

View File

@ -1,241 +0,0 @@
//
// Copyright (C) 2007-2010 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "memptrs.h"
#include <algorithm>
#include <cstring>
using namespace gambatte;
namespace {
template <OamDmaSrc src, bool cgb> struct OamDmaConflictMap;
template <bool cgb> struct OamDmaConflictMap<oam_dma_src_rom, cgb> { enum { r = 0xFCFF }; };
template <bool cgb> struct OamDmaConflictMap<oam_dma_src_sram, cgb> { enum { r = 0xFCFF }; };
template <bool cgb> struct OamDmaConflictMap<oam_dma_src_vram, cgb> { enum { r = 0x0300 }; };
template <bool cgb> struct OamDmaConflictMap<oam_dma_src_wram, cgb> { enum { r = cgb ? 0xF000 : 0xFCFF }; };
template <bool cgb> struct OamDmaConflictMap<oam_dma_src_invalid, cgb> { enum { r = cgb ? 0xFCFF : 0x0000 }; };
template <bool cgb>
bool isInOamDmaConflictArea(OamDmaSrc src, unsigned p)
{
static unsigned short const m[] = {
OamDmaConflictMap<oam_dma_src_rom, cgb>::r,
OamDmaConflictMap<oam_dma_src_sram, cgb>::r,
OamDmaConflictMap<oam_dma_src_vram, cgb>::r,
OamDmaConflictMap<oam_dma_src_wram, cgb>::r,
OamDmaConflictMap<oam_dma_src_invalid, cgb>::r,
0 };
return p < mm_oam_begin && (m[src] >> (p >> 12) & 1);
}
template <OamDmaSrc src, bool cgb>
void disconnectOamDmaAreas(unsigned char const* (&rmem)[0x10], unsigned char* (&wmem)[0x10])
{
if (OamDmaConflictMap<src, cgb>::r & 0x00FF)
std::fill_n(rmem, 8, static_cast<unsigned char*>(0));
if (OamDmaConflictMap<src, cgb>::r & 0x0C00)
rmem[0xB] = rmem[0xA] = wmem[0xB] = wmem[0xA] = 0;
if (OamDmaConflictMap<src, cgb>::r & 0x7000)
rmem[0xE] = rmem[0xD] = rmem[0xC] = wmem[0xE] = wmem[0xD] = wmem[0xC] = 0;
}
template <bool cgb>
void disconnectOamDmaAreas(unsigned char const* (&rmem)[0x10], unsigned char* (&wmem)[0x10],
OamDmaSrc src)
{
switch (src) {
case oam_dma_src_rom: disconnectOamDmaAreas<oam_dma_src_rom, cgb>(rmem, wmem); break;
case oam_dma_src_sram: disconnectOamDmaAreas<oam_dma_src_sram, cgb>(rmem, wmem); break;
case oam_dma_src_vram: disconnectOamDmaAreas<oam_dma_src_vram, cgb>(rmem, wmem); break;
case oam_dma_src_wram: disconnectOamDmaAreas<oam_dma_src_wram, cgb>(rmem, wmem); break;
case oam_dma_src_invalid: disconnectOamDmaAreas<oam_dma_src_invalid, cgb>(rmem, wmem); break;
case oam_dma_src_off: break;
}
}
} // unnamed namespace.
MemPtrs::MemPtrs()
: rmem_()
, wmem_()
, romdata_()
, wramdata_()
, vrambankptr_(0)
, rsrambankptr_(0)
, wsrambankptr_(0)
, rambankdata_(0)
, wramdataend_(0)
, oamDmaSrc_(oam_dma_src_off)
, curRomBank_(1)
, memchunk_len(0)
{
}
void MemPtrs::reset(unsigned const rombanks, unsigned const rambanks, unsigned const wrambanks) {
int const num_disabled_ram_areas = 2;
memchunk_.reset(
pre_rom_pad_size()
+ rombanks * rombank_size()
+ max_num_vrambanks * vrambank_size()
+ rambanks * rambank_size()
+ wrambanks * wrambank_size()
+ num_disabled_ram_areas * rambank_size());
romdata_[0] = romdata();
rambankdata_ = romdata_[0] + rombanks * rombank_size() + max_num_vrambanks * vrambank_size();
wramdata_[0] = rambankdata_ + rambanks * rambank_size();
wramdataend_ = wramdata_[0] + wrambanks * wrambank_size();
std::fill_n(rdisabledRamw(), rambank_size(), 0xFF);
oamDmaSrc_ = oam_dma_src_off;
rmem_[0x3] = rmem_[0x2] = rmem_[0x1] = rmem_[0x0] = romdata_[0];
rmem_[0xC] = wmem_[0xC] = wramdata_[0] - mm_wram_begin;
rmem_[0xE] = wmem_[0xE] = wramdata_[0] - mm_wram_mirror_begin;
setRombank(1);
setRambank(0, 0);
setVrambank(0);
setWrambank(1);
// we save only the ram areas
memchunk_saveoffs = vramdata() - memchunk_;
memchunk_savelen = wramdataend() - memchunk_ - memchunk_saveoffs;
}
void MemPtrs::setRombank0(unsigned bank) {
romdata_[0] = romdata() + bank * rombank_size();
rmem_[0x3] = rmem_[0x2] = rmem_[0x1] = rmem_[0x0] = romdata_[0];
disconnectOamDmaAreas();
}
void MemPtrs::setRombank(unsigned bank) {
curRomBank_ = bank;
romdata_[1] = romdata() + bank * rombank_size() - mm_rom1_begin;
rmem_[0x7] = rmem_[0x6] = rmem_[0x5] = rmem_[0x4] = romdata_[1];
disconnectOamDmaAreas();
}
void MemPtrs::setRambank(unsigned const flags, unsigned const rambank) {
unsigned char* srambankptr = 0;
if (!(flags & rtc_en)) {
srambankptr = rambankdata() != rambankdataend()
? rambankdata_ + rambank * rambank_size() - mm_sram_begin
: wdisabledRam() - mm_sram_begin;
}
rsrambankptr_ = (flags & read_en) && srambankptr != wdisabledRam() - mm_sram_begin
? srambankptr
: rdisabledRamw() - mm_sram_begin;
wsrambankptr_ = flags & write_en
? srambankptr
: wdisabledRam() - mm_sram_begin;
rmem_[0xB] = rmem_[0xA] = rsrambankptr_;
wmem_[0xB] = wmem_[0xA] = wsrambankptr_;
disconnectOamDmaAreas();
}
void MemPtrs::setWrambank(unsigned bank) {
wramdata_[1] = wramdata_[0] + (bank & 0x07 ? bank & 0x07 : 1) * wrambank_size();
rmem_[0xD] = wmem_[0xD] = wramdata_[1] - mm_wram1_begin;
disconnectOamDmaAreas();
}
void MemPtrs::setOamDmaSrc(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] - mm_wram_begin;
rmem_[0xD] = wmem_[0xD] = wramdata_[1] - mm_wram1_begin;
rmem_[0xE] = wmem_[0xE] = wramdata_[0] - mm_wram_mirror_begin;
oamDmaSrc_ = oamDmaSrc;
disconnectOamDmaAreas();
}
void MemPtrs::disconnectOamDmaAreas() {
return isCgb(*this)
? ::disconnectOamDmaAreas<true>(rmem_, wmem_, oamDmaSrc_)
: ::disconnectOamDmaAreas<false>(rmem_, wmem_, oamDmaSrc_);
}
bool MemPtrs::isInOamDmaConflictArea(unsigned p) const
{
return isCgb(*this)
? ::isInOamDmaConflictArea<true>(oamDmaSrc_, p)
: ::isInOamDmaConflictArea<false>(oamDmaSrc_, p);
}
// all pointers here are relative to memchunk_
#define MSS(a) RSS(a,memchunk_)
#define MSL(a) RSL(a,memchunk_)
SYNCFUNC(MemPtrs)
{
NSS(memchunk_len);
NSS(memchunk_saveoffs);
NSS(memchunk_savelen);
PSS(memchunk_ + memchunk_saveoffs, memchunk_savelen);
MSS(rmem_[0x0]);
MSS(wmem_[0x0]);
MSS(rmem_[0x1]);
MSS(wmem_[0x1]);
MSS(rmem_[0x2]);
MSS(wmem_[0x2]);
MSS(rmem_[0x3]);
MSS(wmem_[0x3]);
MSS(rmem_[0x4]);
MSS(wmem_[0x4]);
MSS(rmem_[0x5]);
MSS(wmem_[0x5]);
MSS(rmem_[0x6]);
MSS(wmem_[0x6]);
MSS(rmem_[0x7]);
MSS(wmem_[0x7]);
MSS(rmem_[0x8]);
MSS(wmem_[0x8]);
MSS(rmem_[0x9]);
MSS(wmem_[0x9]);
MSS(rmem_[0xa]);
MSS(wmem_[0xa]);
MSS(rmem_[0xb]);
MSS(wmem_[0xb]);
MSS(rmem_[0xc]);
MSS(wmem_[0xc]);
MSS(rmem_[0xd]);
MSS(wmem_[0xd]);
MSS(rmem_[0xe]);
MSS(wmem_[0xe]);
MSS(rmem_[0xf]);
MSS(wmem_[0xf]);
MSS(romdata_[0]);
MSS(romdata_[1]);
MSS(wramdata_[0]);
MSS(wramdata_[1]);
MSS(vrambankptr_);
MSS(rsrambankptr_);
MSS(wsrambankptr_);
MSS(rambankdata_);
MSS(wramdataend_);
NSS(oamDmaSrc_);
NSS(curRomBank_);
}

View File

@ -1,121 +0,0 @@
//
// Copyright (C) 2007-2010 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef MEMPTRS_H
#define MEMPTRS_H
#include "newstate.h"
#include "array.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 };
enum {
mm_rom_begin = 0x0000,
mm_rom1_begin = 0x4000,
mm_vram_begin = 0x8000,
mm_sram_begin = 0xA000,
mm_wram_begin = 0xC000,
mm_wram1_begin = 0xD000,
mm_wram_mirror_begin = 0xE000,
mm_oam_begin = 0xFE00,
mm_io_begin = 0xFF00,
mm_hram_begin = 0xFF80 };
enum { max_num_vrambanks = 2 };
inline std::size_t rambank_size() { return 0x2000; }
inline std::size_t rombank_size() { return 0x4000; }
inline std::size_t vrambank_size() { return 0x2000; }
inline std::size_t wrambank_size() { return 0x1000; }
class MemPtrs {
public:
enum RamFlag { read_en = 1, write_en = 2, rtc_en = 4 };
MemPtrs();
void reset(unsigned rombanks, unsigned rambanks, unsigned wrambanks);
unsigned char const* rmem(unsigned area) const { return rmem_[area]; }
unsigned char* wmem(unsigned area) const { return wmem_[area]; }
unsigned char* romdata() const { return memchunk_ + pre_rom_pad_size(); }
unsigned char* romdata(unsigned area) const { return romdata_[area]; }
unsigned char* romdataend() const { return rambankdata_ - max_num_vrambanks * vrambank_size(); }
unsigned char* vramdata() const { return romdataend(); }
unsigned char* vramdataend() const { return rambankdata_; }
unsigned char* rambankdata() const { return rambankdata_; }
unsigned char* rambankdataend() const { return wramdata_[0]; }
unsigned char* wramdata(unsigned area) const { return wramdata_[area]; }
unsigned char* wramdataend() const { return wramdataend_; }
unsigned char const* rdisabledRam() const { return rdisabledRamw(); }
unsigned char const* rsrambankptr() const { return rsrambankptr_; }
unsigned char* wsrambankptr() const { return wsrambankptr_; }
unsigned char* vrambankptr() const { return vrambankptr_; }
OamDmaSrc oamDmaSrc() const { return oamDmaSrc_; }
bool isInOamDmaConflictArea(unsigned p) const;
void setRombank0(unsigned bank);
void setRombank(unsigned bank);
void setRambank(unsigned ramFlags, unsigned rambank);
void setVrambank(unsigned bank) { vrambankptr_ = vramdata() + bank * vrambank_size() - mm_vram_begin; }
void setWrambank(unsigned bank);
void setOamDmaSrc(OamDmaSrc oamDmaSrc);
private:
unsigned char const *rmem_[0x10];
unsigned char *wmem_[0x10];
unsigned char *romdata_[2];
unsigned char *wramdata_[2];
unsigned char *vrambankptr_;
unsigned char *rsrambankptr_;
unsigned char *wsrambankptr_;
SimpleArray<unsigned char> memchunk_;
unsigned char *rambankdata_;
unsigned char *wramdataend_;
OamDmaSrc oamDmaSrc_;
unsigned curRomBank_;
int memchunk_len;
int memchunk_saveoffs;
int memchunk_savelen;
static std::size_t pre_rom_pad_size() { return mm_rom1_begin; }
void disconnectOamDmaAreas();
unsigned char * rdisabledRamw() const { return wramdataend_; }
unsigned char * wdisabledRam() const { return wramdataend_ + rambank_size(); }
public:
template<bool isReader>void SyncState(NewState *ns);
};
inline bool isCgb(MemPtrs const& memptrs) {
int const num_cgb_wrambanks = 8;
std::size_t const wramsize = memptrs.wramdataend() - memptrs.wramdata(0);
return wramsize == num_cgb_wrambanks * wrambank_size();
}
}
#endif

View File

@ -1,178 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "rtc.h"
#include "../savestate.h"
#include <cstdlib>
namespace gambatte {
Rtc::Rtc(Time &time)
: time_(time)
, activeData_(0)
, activeSet_(0)
, haltTime_(0)
, index_(5)
, dataDh_(0)
, dataDl_(0)
, dataH_(0)
, dataM_(0)
, dataS_(0)
, enabled_(false)
, lastLatchData_(false)
{
}
void Rtc::doLatch(unsigned long const cc) {
std::uint32_t tmp = time(cc);
if (tmp >= 0x200 * 86400) {
tmp %= 0x200 * 86400;
time_.set(tmp, cc);
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_ = 0;
activeSet_ = 0;
} 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::loadState(SaveState const &state) {
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(unsigned const newDh, unsigned const long cc) {
std::uint32_t seconds = time(cc);
std::uint32_t const oldHighdays = (seconds / 86400) & 0x100;
seconds -= oldHighdays * 86400;
seconds += ((newDh & 0x1) << 8) * 86400;
time_.set(seconds, cc);
if ((dataDh_ ^ newDh) & 0x40) {
if (newDh & 0x40)
haltTime_ = seconds;
else
time_.set(haltTime_, cc);
}
}
void Rtc::setDl(unsigned const newLowdays, unsigned const long cc) {
std::uint32_t seconds = time(cc);
std::uint32_t const oldLowdays = (seconds / 86400) & 0xFF;
seconds -= oldLowdays * 86400;
seconds += newLowdays * 86400;
time_.set(seconds, cc);
}
void Rtc::setH(unsigned const newHours, unsigned const long cc) {
std::uint32_t seconds = time(cc);
std::uint32_t const oldHours = (seconds / 3600) % 24;
seconds -= oldHours * 3600;
seconds += newHours * 3600;
time_.set(seconds, cc);
}
void Rtc::setM(unsigned const newMinutes, unsigned const long cc) {
std::uint32_t seconds = time(cc);
std::uint32_t const oldMinutes = (seconds / 60) % 60;
seconds -= oldMinutes * 60;
seconds += newMinutes * 60;
time_.set(seconds, cc);
}
void Rtc::setS(unsigned const newSeconds, unsigned const long cc) {
std::uint32_t seconds = time(cc);
seconds -= seconds % 60;
seconds += newSeconds;
time_.reset(seconds, cc);
}
SYNCFUNC(Rtc)
{
EBS(activeData_, 0);
EVS(activeData_, &dataS_, 1);
EVS(activeData_, &dataM_, 2);
EVS(activeData_, &dataH_, 3);
EVS(activeData_, &dataDl_, 4);
EVS(activeData_, &dataDh_, 5);
EES(activeData_, NULL);
EBS(activeSet_, 0);
EVS(activeSet_, &Rtc::setS, 1);
EVS(activeSet_, &Rtc::setM, 2);
EVS(activeSet_, &Rtc::setH, 3);
EVS(activeSet_, &Rtc::setDl, 4);
EVS(activeSet_, &Rtc::setDh, 5);
EES(activeSet_, NULL);
NSS(haltTime_);
NSS(index_);
NSS(dataDh_);
NSS(dataDl_);
NSS(dataH_);
NSS(dataM_);
NSS(dataS_);
NSS(enabled_);
NSS(lastLatchData_);
}
}

View File

@ -1,89 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef RTC_H
#define RTC_H
#include <cstdint>
#include "time.h"
#include "newstate.h"
namespace gambatte {
struct SaveState;
class Rtc {
public:
Rtc(Time &time);
unsigned char const * activeData() const { return activeData_; }
void latch(unsigned data, unsigned long const cc) {
if (!lastLatchData_ && data == 1)
doLatch(cc);
lastLatchData_ = data;
}
void loadState(SaveState const &state);
void set(bool enabled, unsigned bank) {
bank &= 0xF;
bank -= 8;
enabled_ = enabled;
index_ = bank;
doSwapActive();
}
void write(unsigned data, unsigned long const cc) {
(this->*activeSet_)(data, cc);
*activeData_ = data;
}
private:
Time &time_;
unsigned char *activeData_;
void (Rtc::*activeSet_)(unsigned, unsigned long);
std::uint32_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(unsigned long cycleCounter);
void doSwapActive();
void setDh(unsigned newDh, unsigned long cycleCounter);
void setDl(unsigned newLowdays, unsigned long cycleCounter);
void setH(unsigned newHours, unsigned long cycleCounter);
void setM(unsigned newMinutes, unsigned long cycleCounter);
void setS(unsigned newSeconds, unsigned long cycleCounter);
std::uint32_t time(unsigned long const cc) {
return dataDh_ & 0x40 ? haltTime_ : time_.get(cc);
}
public:
template<bool isReader>void SyncState(NewState *ns);
};
}
#endif

View File

@ -1,145 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "time.h"
#include "../savestate.h"
namespace gambatte {
static timeval operator-(timeval l, timeval r) {
timeval t;
t.tv_sec = l.tv_sec - r.tv_sec;
t.tv_usec = l.tv_usec - r.tv_usec;
if (t.tv_usec < 0) {
t.tv_sec--;
t.tv_usec += 1000000;
}
return t;
}
Time::Time()
: useCycles_(true)
, rtcDivisor_(0x400000)
{
}
void Time::loadState(SaveState const &state) {
seconds_ = state.time.seconds;
lastTime_.tv_sec = state.time.lastTimeSec;
lastTime_.tv_usec = state.time.lastTimeUsec;
lastCycles_ = state.time.lastCycles;
ds_ = state.mem.ioamhram.get()[0x14D] >> 7;
}
std::uint32_t Time::get(unsigned long const cc) {
update(cc);
return seconds_;
}
void Time::set(std::uint32_t seconds, unsigned long const cc) {
update(cc);
seconds_ = seconds;
}
void Time::reset(std::uint32_t seconds, unsigned long const cc) {
set(seconds, cc);
lastTime_ = now();
lastCycles_ = cc;
}
void Time::resetCc(unsigned long const oldCc, unsigned long const newCc) {
update(oldCc);
lastCycles_ -= oldCc - newCc;
}
void Time::speedChange(unsigned long const cc) {
update(cc);
if (useCycles_) {
unsigned long diff = cc - lastCycles_;
lastCycles_ = cc - (ds_ ? diff >> 1 : diff << 1);
}
ds_ = !ds_;
}
timeval Time::baseTime(unsigned long const cc) {
if (useCycles_)
timeFromCycles(cc);
timeval baseTime = lastTime_;
baseTime.tv_sec -= seconds_;
return baseTime;
}
void Time::setBaseTime(timeval baseTime, unsigned long const cc) {
seconds_ = (now() - baseTime).tv_sec;
lastTime_ = baseTime;
lastTime_.tv_sec += seconds_;
if (useCycles_)
cyclesFromTime(cc);
}
void Time::setTimeMode(bool useCycles, unsigned long const cc) {
if (useCycles != useCycles_) {
if (useCycles_)
timeFromCycles(cc);
else
cyclesFromTime(cc);
useCycles_ = useCycles;
}
}
void Time::update(unsigned long const cc) {
if (useCycles_) {
std::uint32_t diff = (cc - lastCycles_) / (rtcDivisor_ << ds_);
seconds_ += diff;
lastCycles_ += diff * (rtcDivisor_ << ds_);
} else {
std::uint32_t diff = (now() - lastTime_).tv_sec;
seconds_ += diff;
lastTime_.tv_sec += diff;
}
}
void Time::cyclesFromTime(unsigned long const cc) {
update(cc);
timeval diff = now() - lastTime_;
lastCycles_ = cc - diff.tv_usec * ((rtcDivisor_ << ds_) / 1000000.0f);
}
void Time::timeFromCycles(unsigned long const cc) {
update(cc);
unsigned long diff = cc - lastCycles_;
timeval usec = { 0, (long)(diff / ((rtcDivisor_ << ds_) / 1000000.0f)) };
lastTime_ = now() - usec;
}
SYNCFUNC(Time)
{
NSS(seconds_);
NSS(lastTime_.tv_sec);
NSS(lastTime_.tv_usec);
NSS(lastCycles_);
NSS(useCycles_);
NSS(ds_);
}
}

View File

@ -1,80 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef TIME_H
#define TIME_H
#include <chrono>
#include <cstdint>
#include <ctime>
#include "newstate.h"
namespace gambatte {
struct SaveState;
struct timeval {
std::uint32_t tv_sec;
std::uint32_t tv_usec;
};
class Time {
public:
static timeval now() {
long long micros = std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::high_resolution_clock::now().time_since_epoch())
.count();
timeval t;
t.tv_usec = micros % 1000000;
t.tv_sec = micros / 1000000;
return t;
}
Time();
void loadState(SaveState const &state);
std::uint32_t get(unsigned long cycleCounter);
void set(std::uint32_t seconds, unsigned long cycleCounter);
void reset(std::uint32_t seconds, unsigned long cycleCounter);
void resetCc(unsigned long oldCc, unsigned long newCc);
void speedChange(unsigned long cycleCounter);
timeval baseTime(unsigned long cycleCounter);
void setBaseTime(timeval baseTime, unsigned long cycleCounter);
void setTimeMode(bool useCycles, unsigned long cycleCounter);
void setRtcDivisorOffset(long const rtcDivisorOffset) { rtcDivisor_ = 0x400000L + rtcDivisorOffset; }
private:
std::uint32_t seconds_;
timeval lastTime_;
unsigned long lastCycles_;
bool useCycles_;
unsigned long rtcDivisor_;
bool ds_;
void update(unsigned long cycleCounter);
void cyclesFromTime(unsigned long cycleCounter);
void timeFromCycles(unsigned long cycleCounter);
public:
template<bool isReader>void SyncState(NewState *ns);
};
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,339 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef MEMORY_H
#define MEMORY_H
static unsigned char const agbOverride[0xD] = { 0xFF, 0x00, 0xCD, 0x03, 0x35, 0xAA, 0x31, 0x90, 0x94, 0x00, 0x00, 0x00, 0x00 };
#include "mem/cartridge.h"
#include "interrupter.h"
#include "sound.h"
#include "tima.h"
#include "video.h"
#include "newstate.h"
#include "gambatte.h"
namespace gambatte {
class FilterInfo;
class Memory {
public:
explicit Memory(Interrupter const &interrupter);
~Memory();
bool loaded() const { return cart_.loaded(); }
unsigned curRomBank() const { return cart_.curRomBank(); }
char const * romTitle() const { return cart_.romTitle(); }
int debugGetLY() const { return lcd_.debugGetLY(); }
void setStatePtrs(SaveState &state);
void loadState(SaveState const &state);
void loadSavedata(char const *data, unsigned long const cc) { cart_.loadSavedata(data, cc); }
int saveSavedataLength() {return cart_.saveSavedataLength(); }
void saveSavedata(char *dest, unsigned long const cc) { cart_.saveSavedata(dest, cc); }
void updateInput();
void setBios(char const *buffer, std::size_t size) {
delete []bios_;
bios_ = new unsigned char[size];
memcpy(bios_, buffer, size);
biosSize_ = size;
}
bool getMemoryArea(int which, unsigned char **data, int *length);
unsigned long stop(unsigned long cycleCounter, bool& skip);
bool isCgb() const { return lcd_.isCgb(); }
bool isCgbDmg() const { return lcd_.isCgbDmg(); }
bool ime() const { return intreq_.ime(); }
bool halted() const { return intreq_.halted(); }
unsigned long nextEventTime() const { return intreq_.minEventTime(); }
void setLayers(unsigned mask) { lcd_.setLayers(mask); }
bool isActive() const { return intreq_.eventTime(intevent_end) != disabled_time; }
long cyclesSinceBlit(unsigned long cc) const {
if (cc < intreq_.eventTime(intevent_blit))
return -1;
return (cc - intreq_.eventTime(intevent_blit)) >> isDoubleSpeed();
}
void freeze(unsigned long cc);
bool halt(unsigned long cc);
void ei(unsigned long cycleCounter) { if (!ime()) { intreq_.ei(cycleCounter); } }
void di() { intreq_.di(); }
unsigned pendingIrqs(unsigned long cc);
void ackIrq(unsigned bit, unsigned long cc);
unsigned readBios(unsigned p) {
if(isCgb() && agbMode_ && p >= 0xF3 && p < 0x100) {
return (agbOverride[p-0xF3] + bios_[p]) & 0xFF;
}
return bios_[p];
}
unsigned ff_read(unsigned p, unsigned long cc) {
if (readCallback_)
readCallback_(p, (cc - basetime_) >> 1);
return p < 0x80 ? nontrivial_ff_read(p, cc) : ioamhram_[p + 0x100];
}
struct CDMapResult
{
eCDLog_AddrType type;
unsigned addr;
};
CDMapResult CDMap(const unsigned p) const
{
if(p < 0x4000)
{
CDMapResult ret = { eCDLog_AddrType_ROM, p };
return ret;
}
else if(p < 0x8000)
{
unsigned bank = cart_.rmem(p >> 12) - cart_.rmem(0);
unsigned addr = p + bank;
CDMapResult ret = { eCDLog_AddrType_ROM, addr };
return ret;
}
else if(p < 0xA000) {}
else if(p < 0xC000)
{
if(cart_.wsrambankptr())
{
//not bankable. but. we're not sure how much might be here
unsigned char *data;
int length;
bool has = cart_.getMemoryArea(3,&data,&length);
unsigned addr = p & (length-1);
if(has && length!=0)
{
CDMapResult ret = { eCDLog_AddrType_CartRAM, addr };
return ret;
}
}
}
else if(p < 0xE000)
{
unsigned bank = cart_.wramdata(p >> 12 & 1) - cart_.wramdata(0);
unsigned addr = (p & 0xFFF) + bank;
CDMapResult ret = { eCDLog_AddrType_WRAM, addr };
return ret;
}
else if(p < 0xFF80) {}
else
{
////this is just for debugging, really, it's pretty useless
//CDMapResult ret = { eCDLog_AddrType_HRAM, (P-0xFF80) };
//return ret;
}
CDMapResult ret = { eCDLog_AddrType_None };
return ret;
}
unsigned read(unsigned p, unsigned long cc) {
if (readCallback_)
readCallback_(p, (cc - basetime_) >> 1);
if(biosMode_) {
if (p < biosSize_ && !(p >= 0x100 && p < 0x200))
return readBios(p);
}
else if(cdCallback_) {
CDMapResult map = CDMap(p);
if(map.type != eCDLog_AddrType_None)
cdCallback_(map.addr, map.type, eCDLog_Flags_Data);
}
return cart_.rmem(p >> 12) ? cart_.rmem(p >> 12)[p] : nontrivial_read(p, cc);
}
unsigned read_excb(unsigned p, unsigned long cc, bool first) {
if (execCallback_)
execCallback_(p, (cc - basetime_) >> 1);
if (biosMode_) {
if(p < biosSize_ && !(p >= 0x100 && p < 0x200))
return readBios(p);
}
else if(cdCallback_) {
CDMapResult map = CDMap(p);
if(map.type != eCDLog_AddrType_None)
cdCallback_(map.addr, map.type, first ? eCDLog_Flags_ExecFirst : eCDLog_Flags_ExecOperand);
}
return cart_.rmem(p >> 12) ? cart_.rmem(p >> 12)[p] : nontrivial_read(p, cc);
}
unsigned peek(unsigned p) {
if (biosMode_ && p < biosSize_ && !(p >= 0x100 && p < 0x200)) {
return readBios(p);
}
return cart_.rmem(p >> 12) ? cart_.rmem(p >> 12)[p] : nontrivial_peek(p);
}
void write_nocb(unsigned p, unsigned data, unsigned long cc) {
if (cart_.wmem(p >> 12)) {
cart_.wmem(p >> 12)[p] = data;
} else
nontrivial_write(p, data, cc);
}
void write(unsigned p, unsigned data, unsigned long cc) {
if (cart_.wmem(p >> 12)) {
cart_.wmem(p >> 12)[p] = data;
} else
nontrivial_write(p, data, cc);
if (writeCallback_)
writeCallback_(p, (cc - basetime_) >> 1);
if(cdCallback_ && !biosMode_) {
CDMapResult map = CDMap(p);
if(map.type != eCDLog_AddrType_None)
cdCallback_(map.addr, map.type, eCDLog_Flags_Data);
}
}
void ff_write(unsigned p, unsigned data, unsigned long cc) {
if (p - 0x80u < 0x7Fu) {
ioamhram_[p + 0x100] = data;
} else
nontrivial_ff_write(p, data, cc);
if (writeCallback_)
writeCallback_(0xff00 + p, (cc - basetime_) >> 1);
if(cdCallback_ && !biosMode_)
{
CDMapResult map = CDMap(0xff00 + p);
if(map.type != eCDLog_AddrType_None)
cdCallback_(map.addr, map.type, eCDLog_Flags_Data);
}
}
unsigned long event(unsigned long cycleCounter);
unsigned long resetCounters(unsigned long cycleCounter);
LoadRes loadROM(char const *romfiledata, unsigned romfilelength, unsigned flags);
void setInputGetter(unsigned (*getInput)()) {
getInput_ = getInput;
}
void setReadCallback(MemoryCallback callback) {
this->readCallback_ = callback;
}
void setWriteCallback(MemoryCallback callback) {
this->writeCallback_ = callback;
}
void setExecCallback(MemoryCallback callback) {
this->execCallback_ = callback;
}
void setCDCallback(CDCallback cdc) {
this->cdCallback_ = cdc;
}
void setScanlineCallback(void (*callback)(), int sl) {
lcd_.setScanlineCallback(callback, sl);
}
void setLinkCallback(void(*callback)()) {
this->linkCallback_ = callback;
}
void setEndtime(unsigned long cc, unsigned long inc);
void setBasetime(unsigned long cc) { basetime_ = cc; }
void setSoundBuffer(uint_least32_t *buf) { psg_.setBuffer(buf); }
std::size_t fillSoundBuffer(unsigned long cc);
void setVideoBuffer(uint_least32_t *videoBuf, std::ptrdiff_t pitch) {
lcd_.setVideoBuffer(videoBuf, pitch);
}
void setDmgPaletteColor(int palNum, int colorNum, unsigned long rgb32) {
lcd_.setDmgPaletteColor(palNum, colorNum, rgb32);
}
void blackScreen() {
lcd_.blackScreen();
}
void setCgbPalette(unsigned *lut);
void setTimeMode(bool useCycles, unsigned long const cc) {
cart_.setTimeMode(useCycles, cc);
}
void setRtcDivisorOffset(long const rtcDivisorOffset) { cart_.setRtcDivisorOffset(rtcDivisorOffset); }
int linkStatus(int which);
private:
Cartridge cart_;
unsigned char ioamhram_[0x200];
unsigned char *bios_;
std::size_t biosSize_;
unsigned (*getInput_)();
unsigned long divLastUpdate_;
unsigned long lastOamDmaUpdate_;
InterruptRequester intreq_;
Tima tima_;
LCD lcd_;
PSG psg_;
Interrupter interrupter_;
unsigned short dmaSource_;
unsigned short dmaDestination_;
unsigned char oamDmaPos_;
unsigned char oamDmaStartPos_;
unsigned char serialCnt_;
bool blanklcd_;
bool biosMode_;
bool agbMode_;
unsigned long basetime_;
bool stopped_;
enum HdmaState { hdma_low, hdma_high, hdma_requested } haltHdmaState_;
MemoryCallback readCallback_;
MemoryCallback writeCallback_;
MemoryCallback execCallback_;
CDCallback cdCallback_;
void(*linkCallback_)();
bool LINKCABLE_;
bool linkClockTrigger_;
void decEventCycles(IntEventId eventId, unsigned long dec);
void oamDmaInitSetup();
void updateOamDma(unsigned long cycleCounter);
void startOamDma(unsigned long cycleCounter);
void endOamDma(unsigned long cycleCounter);
unsigned char const * oamDmaSrcPtr() const;
unsigned long dma(unsigned long cc);
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);
unsigned nontrivial_peek(unsigned p);
unsigned nontrivial_ff_peek(unsigned p);
void updateSerial(unsigned long cc);
void updateTimaIrq(unsigned long cc);
void updateIrqs(unsigned long cc);
bool isDoubleSpeed() const { return lcd_.isDoubleSpeed(); }
public:
template<bool isReader>void SyncState(NewState *ns);
};
}
#endif

View File

@ -1,168 +0,0 @@
//
// Copyright (C) 2009 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef MINKEEPER_H
#define MINKEEPER_H
#include <algorithm>
#include "newstate.h"
namespace min_keeper_detail {
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 CeiledDiv2n { enum { r = CeiledDiv2n<(v + 1) / 2, n - 1>::r }; };
template<int v> struct CeiledDiv2n<v, 0> { enum { r = v }; };
// alternatively: template<int v, int n> struct CeiledDiv2n { enum { r = (v + (1 << n) - 1) >> n }; };
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 be inverted 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 for it.
template<int ids>
class MinKeeper {
public:
explicit MinKeeper(unsigned long initValue);
int min() const { return a_[0]; }
unsigned long minValue() const { return minValue_; }
template<int id>
void setValue(unsigned long cnt) {
values_[id] = cnt;
updateValue<id / 2>(*this);
}
void setValue(int id, unsigned long cnt) {
values_[id] = cnt;
updateValueLut.call(id >> 1, *this);
}
unsigned long value(int id) const { return values_[id]; }
private:
enum { height = min_keeper_detail::CeiledLog2<ids>::r };
template<int depth> struct Num { enum { r = min_keeper_detail::CeiledDiv2n<ids, height - depth>::r }; };
template<int depth> struct Sum { enum { r = min_keeper_detail::Sum<Num, depth>::r }; };
template<int id, int depth>
struct UpdateValue {
enum { p = Sum<depth - 1>::r + id
, c0 = Sum<depth>::r + id * 2
, c1 = id * 2 + 1 < Num<depth>::r ? c0 + 1 : c0 };
static void updateValue(MinKeeper<ids> &m) {
m.a_[p] = m.values_[m.a_[c0]] < m.values_[m.a_[c1]] ? m.a_[c0] : m.a_[c1];
UpdateValue<id / 2, depth - 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 {
public:
UpdateValueLut() { FillLut<Num<height - 1>::r - 1, 0>::fillLut(*this); }
void call(int id, MinKeeper<ids> &mk) const { lut_[id](mk); }
private:
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<height - 1>::r])(MinKeeper<ids> &);
};
static UpdateValueLut updateValueLut;
unsigned long values_[ids];
unsigned long minValue_;
int a_[Sum<height>::r];
template<int id> static void updateValue(MinKeeper<ids> &m);
public:
// not sure if i understood everything in minkeeper correctly, so something might be missing here?
template<bool isReader>
void SyncState(gambatte::NewState *ns)
{
NSS(values_);
NSS(minValue_);
NSS(a_);
}
};
template<int ids> typename MinKeeper<ids>::UpdateValueLut MinKeeper<ids>::updateValueLut;
template<int ids>
MinKeeper<ids>::MinKeeper(unsigned long const initValue) {
std::fill(values_, values_ + ids, initValue);
// todo: simplify/less template bloat.
for (int i = 0; i < Num<height - 1>::r; ++i) {
int const c0 = i * 2;
int const c1 = c0 + 1 < ids ? c0 + 1 : c0;
a_[Sum<height - 1>::r + i] = values_[c0] < values_[c1] ? c0 : c1;
}
int n = Num<height - 1>::r;
int offset = Sum<height - 1>::r;
while (offset) {
int const pn = (n + 1) >> 1;
int const poff = offset - pn;
for (int i = 0; i < pn; ++i) {
int const c0 = offset + i * 2;
int const c1 = i * 2 + 1 < n ? c0 + 1 : c0;
a_[poff + i] = values_[a_[c0]] < values_[a_[c1]] ? a_[c0] : a_[c1];
}
offset = poff;
n = pn;
}
minValue_ = values_[a_[0]];
}
template<int ids>
template<int id>
void MinKeeper<ids>::updateValue(MinKeeper<ids> &m) {
enum { c0 = id * 2
, c1 = c0 + 1 < ids ? c0 + 1 : c0 };
m.a_[Sum<height - 1>::r + id] = m.values_[c0] < m.values_[c1] ? c0 : c1;
UpdateValue<id / 2, height - 1>::updateValue(m);
}
#endif

View File

@ -1,69 +0,0 @@
#include "newstate.h"
#include <cstring>
#include <algorithm>
namespace gambatte {
NewStateDummy::NewStateDummy()
:length(0)
{
}
void NewStateDummy::Save(void const *ptr, size_t size, char const *name)
{
length += size;
}
void NewStateDummy::Load(void *ptr, size_t size, char const *name)
{
}
NewStateExternalBuffer::NewStateExternalBuffer(char *buffer, long maxlength)
:buffer(buffer), length(0), maxlength(maxlength)
{
}
void NewStateExternalBuffer::Save(void const *ptr, size_t size, char const *name)
{
if (maxlength - length >= (long)size)
{
std::memcpy(buffer + length, ptr, size);
}
length += size;
}
void NewStateExternalBuffer::Load(void *ptr, size_t size, char const *name)
{
char *dst = static_cast<char *>(ptr);
if (maxlength - length >= (long)size)
{
std::memcpy(dst, buffer + length, size);
}
length += size;
}
NewStateExternalFunctions::NewStateExternalFunctions(const FPtrs *ff)
:Save_(ff->Save_),
Load_(ff->Load_),
EnterSection_(ff->EnterSection_),
ExitSection_(ff->ExitSection_)
{
}
void NewStateExternalFunctions::Save(void const *ptr, size_t size, char const *name)
{
Save_(ptr, size, name);
}
void NewStateExternalFunctions::Load(void *ptr, size_t size, char const *name)
{
Load_(ptr, size, name);
}
void NewStateExternalFunctions::EnterSection(char const *name)
{
EnterSection_(name);
}
void NewStateExternalFunctions::ExitSection(char const *name)
{
ExitSection_(name);
}
}

View File

@ -1,102 +0,0 @@
#ifndef NEWSTATE_H
#define NEWSTATE_H
#include <cstring>
#include <cstddef>
namespace gambatte {
class NewState
{
public:
virtual void Save(void const *ptr, std::size_t size, char const *name) = 0;
virtual void Load(void *ptr, std::size_t size, char const *name) = 0;
virtual void EnterSection(char const *name) { }
virtual void ExitSection(char const *name) { }
};
class NewStateDummy : public NewState
{
private:
long length;
public:
NewStateDummy();
long GetLength() { return length; }
void Rewind() { length = 0; }
virtual void Save(void const *ptr, std::size_t size, char const *name);
virtual void Load(void *ptr, std::size_t size, char const *name);
};
class NewStateExternalBuffer : public NewState
{
private:
char *const buffer;
long length;
const long maxlength;
public:
NewStateExternalBuffer(char *buffer, long maxlength);
long GetLength() { return length; }
void Rewind() { length = 0; }
bool Overflow() { return length > maxlength; }
virtual void Save(void const *ptr, std::size_t size, char const *name);
virtual void Load(void *ptr, std::size_t size, char const *name);
};
struct FPtrs
{
void (*Save_)(void const *ptr, std::size_t size, char const *name);
void (*Load_)(void *ptr, std::size_t size, char const *name);
void (*EnterSection_)(char const *name);
void (*ExitSection_)(char const *name);
};
class NewStateExternalFunctions : public NewState
{
private:
void (*Save_)(void const *ptr, std::size_t size, char const *name);
void (*Load_)(void *ptr, std::size_t size, char const *name);
void (*EnterSection_)(char const *name);
void (*ExitSection_)(char const *name);
public:
NewStateExternalFunctions(const FPtrs *ff);
virtual void Save(void const *ptr, std::size_t size, char const *name);
virtual void Load(void *ptr, std::size_t size, char const *name);
virtual void EnterSection(char const *name);
virtual void ExitSection(char const *name);
};
// defines and explicitly instantiates
#define SYNCFUNC(x)\
template void x::SyncState<false>(NewState *ns);\
template void x::SyncState<true>(NewState *ns);\
template<bool isReader>void x::SyncState(NewState *ns)
// N = normal variable
// P = pointer to fixed size data
// S = "sub object"
// T = "ptr to sub object"
// R = pointer, store its offset from some other pointer
// E = general purpose cased value "enum"
// first line is default value in converted enum; last line is default value in argument x
#define EBS(x,d) do { int _ttmp = (d); if (isReader) ns->Load(&_ttmp, sizeof _ttmp, #x); if (0)
#define EVS(x,v,n) else if (!isReader && (x) == (v)) _ttmp = (n); else if (isReader && _ttmp == (n)) (x) = (v)
#define EES(x,d) else if (isReader) (x) = (d); if (!isReader) ns->Save(&_ttmp, sizeof _ttmp, #x); } while (0)
#define RSS(x,b) do { if (isReader)\
{ std::ptrdiff_t _ttmp; ns->Load(&_ttmp, sizeof _ttmp, #x); (x) = (_ttmp == (std::ptrdiff_t)0xdeadbeef ? 0 : (b) + _ttmp); }\
else\
{ std::ptrdiff_t _ttmp = (x) == 0 ? 0xdeadbeef : (x) - (b); ns->Save(&_ttmp, sizeof _ttmp, #x); } } while (0)
#define PSS(x,s) do { if (isReader) ns->Load((x), (s), #x); else ns->Save((x), (s), #x); } while (0)
#define NSS(x) do { if (isReader) ns->Load(&(x), sizeof x, #x); else ns->Save(&(x), sizeof x, #x); } while (0)
#define SSS(x) do { ns->EnterSection(#x); (x).SyncState<isReader>(ns); ns->ExitSection(#x); } while (0)
#define TSS(x) do { ns->EnterSection(#x); (x)->SyncState<isReader>(ns); ns->ExitSection(#x); } while (0)
}
#endif

View File

@ -1,225 +0,0 @@
//
// Copyright (C) 2008 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef SAVESTATE_H
#define SAVESTATE_H
#include <cstddef>
#include <cstdint>
namespace gambatte {
class SaverList;
struct SaveState {
template<typename T>
class Ptr {
public:
Ptr() : ptr(0), size_(0) {}
T const * get() const { return ptr; }
std::size_t size() const { return size_; }
void set(T *p, std::size_t size) { ptr = p; size_ = size; }
friend class SaverList;
friend void setInitState(SaveState &, bool);
private:
T *ptr;
std::size_t size_;
};
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;
unsigned char opcode;
unsigned char /*bool*/ prefetched;
unsigned char /*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;
unsigned char haltHdmaState;
unsigned char HuC3RAMflag;
unsigned char /*bool*/ IME;
unsigned char /*bool*/ halted;
unsigned char /*bool*/ enableRam;
unsigned char /*bool*/ rambankMode;
unsigned char /*bool*/ hdmaTransfer;
unsigned char /*bool*/ biosMode;
unsigned char /*bool*/ stopped;
} 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;
unsigned char /*bool*/ weMaster;
unsigned char /*bool*/ pendingLcdstatIrq;
unsigned char /*bool*/ notCgbDmg;
} ppu;
struct SPU {
struct Duty {
unsigned long nextPosUpdate;
unsigned char nr3;
unsigned char pos;
unsigned char /*bool*/ high;
};
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;
unsigned char /*bool*/ neg;
} sweep;
Duty duty;
Env env;
LCounter lcounter;
unsigned char nr4;
unsigned char /*bool*/ master;
} ch1;
struct {
Duty duty;
Env env;
LCounter lcounter;
unsigned char nr4;
unsigned char /*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;
unsigned char /*bool*/ master;
} ch3;
struct {
struct {
unsigned long counter;
unsigned short reg;
} lfsr;
Env env;
LCounter lcounter;
unsigned char nr4;
unsigned char /*bool*/ master;
} ch4;
unsigned long cycleCounter;
unsigned char lastUpdate;
} spu;
struct Time {
unsigned long seconds;
unsigned long lastTimeSec;
unsigned long lastTimeUsec;
unsigned long lastCycles;
} time;
struct RTC {
unsigned long haltTime;
unsigned char dataDh;
unsigned char dataDl;
unsigned char dataH;
unsigned char dataM;
unsigned char dataS;
unsigned char /*bool*/ lastLatchData;
} rtc;
struct HuC3 {
unsigned long haltTime;
unsigned long dataTime;
unsigned long writingTime;
unsigned long irBaseCycle;
unsigned char /*bool*/ halted;
unsigned char shift;
unsigned char ramValue;
unsigned char modeflag;
unsigned char /*bool*/ irReceivingPulse;
} huc3;
};
}
#endif

View File

@ -1,230 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "sound.h"
#include "savestate.h"
#include <algorithm>
#include <cstring>
/*
Frame Sequencer
Step Length Ctr Vol Env Sweep
- - - - - - - - - - - - - - - - - - - -
S0 0 Clock - Clock
S1 1 - Clock -
2 Clock - -
3 - - -
4 Clock - Clock
5 - - -
6 Clock - -
7 - - -
- - - - - - - - - - - - - - - - - - - -
Rate 256 Hz 64 Hz 128 Hz
S0) start step on sound power on.
S1) step gets immediately incremented at power on if closer to previous edge than next.
Clock) clock timer on transition to step.
*/
using namespace gambatte;
PSG::PSG()
: buffer_(0)
, bufferPos_(0)
, lastUpdate_(0)
, cycleCounter_(0)
, soVol_(0)
, rsum_(0x8000) // initialize to 0x8000 to prevent borrows from high word, xor away later
, enabled_(false)
{
}
void PSG::init(bool cgb) {
ch1_.init(cgb);
ch3_.init(cgb);
}
void PSG::reset(bool ds) {
int const divOffset = lastUpdate_ & ds;
unsigned long const cc = cycleCounter_ + divOffset;
// cycleCounter >> 12 & 7 represents the frame sequencer position.
cycleCounter_ = (cc & 0xFFF) + 2 * (~(cc + 1 + !ds) & 0x800);
lastUpdate_ = ((lastUpdate_ + 3) & -4) - !ds;
ch1_.reset();
ch2_.reset();
ch3_.reset();
ch4_.reset(cycleCounter_);
}
void PSG::divReset(bool ds) {
int const divOffset = lastUpdate_ & ds;
unsigned long const cc = cycleCounter_ + divOffset;
cycleCounter_ = (cc & -0x1000) + 2 * (cc & 0x800) - divOffset;
ch1_.resetCc(cc - divOffset, cycleCounter_);
ch2_.resetCc(cc - divOffset, cycleCounter_);
ch3_.resetCc(cc - divOffset, cycleCounter_);
ch4_.resetCc(cc - divOffset, cycleCounter_);
}
void PSG::speedChange(unsigned long const cpuCc, bool const ds) {
generateSamples(cpuCc, ds);
lastUpdate_ -= ds;
// correct for cycles since DIV reset (if any).
if (!ds) {
unsigned long const cc = cycleCounter_;
unsigned const divCycles = cc & 0xFFF;
cycleCounter_ = cc - divCycles / 2 - lastUpdate_ % 2;
ch1_.resetCc(cc, cycleCounter_);
ch2_.resetCc(cc, cycleCounter_);
ch3_.resetCc(cc, cycleCounter_);
ch4_.resetCc(cc, cycleCounter_);
}
}
void PSG::setStatePtrs(SaveState &state) {
ch3_.setStatePtrs(state);
}
void PSG::loadState(SaveState const &state) {
ch1_.loadState(state);
ch2_.loadState(state);
ch3_.loadState(state);
ch4_.loadState(state);
cycleCounter_ = state.spu.cycleCounter;
lastUpdate_ = state.cpu.cycleCounter - (1 - state.spu.lastUpdate) % 4u;
setSoVolume(state.mem.ioamhram.get()[0x124]);
mapSo(state.mem.ioamhram.get()[0x125]);
enabled_ = state.mem.ioamhram.get()[0x126] >> 7 & 1;
}
inline void PSG::accumulateChannels(unsigned long const cycles) {
unsigned long const cc = cycleCounter_;
uint_least32_t* const buf = buffer_ + bufferPos_;
std::memset(buf, 0, cycles * sizeof * buf);
ch1_.update(buf, soVol_, cc, cc + cycles);
ch2_.update(buf, soVol_, cc, cc + cycles);
ch3_.update(buf, soVol_, cc, cc + cycles);
ch4_.update(buf, soVol_, cc, cc + cycles);
cycleCounter_ = (cc + cycles) % SoundUnit::counter_max;
}
void PSG::generateSamples(unsigned long const cpuCc, bool const doubleSpeed) {
unsigned long const cycles = (cpuCc - lastUpdate_) >> (1 + doubleSpeed);
lastUpdate_ += cycles << (1 + doubleSpeed);
if (cycles)
accumulateChannels(cycles);
bufferPos_ += cycles;
}
void PSG::resetCounter(unsigned long newCc, unsigned long oldCc, bool doubleSpeed) {
generateSamples(oldCc, doubleSpeed);
lastUpdate_ = newCc - (oldCc - lastUpdate_);
}
std::size_t PSG::fillBuffer() {
uint_least32_t sum = rsum_;
uint_least32_t *b = buffer_;
std::size_t n = bufferPos_;
if (std::size_t 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;
// xor away the initial rsum value of 0x8000 (which prevents
// borrows from the high word) from the low word
*b++ = sum ^ 0x8000;
}
rsum_ = sum;
return bufferPos_;
}
static bool isBigEndianSampleOrder() {
union {
uint_least32_t ul32;
unsigned char uc[sizeof(uint_least32_t)];
} u;
u.ul32 = -0x10000;
return u.uc[0];
}
static unsigned long so1Mul() { return isBigEndianSampleOrder() ? 0x00000001 : 0x00010000; }
static unsigned long so2Mul() { return isBigEndianSampleOrder() ? 0x00010000 : 0x00000001; }
void PSG::setSoVolume(unsigned nr50) {
soVol_ = ((nr50 & 0x7) + 1) * so1Mul() * 64
+ ((nr50 >> 4 & 0x7) + 1) * so2Mul() * 64;
}
void PSG::mapSo(unsigned nr51) {
unsigned long so = nr51 * so1Mul() + (nr51 >> 4) * so2Mul();
ch1_.setSo((so & 0x00010001) * 0xFFFF, cycleCounter_);
ch2_.setSo((so >> 1 & 0x00010001) * 0xFFFF, cycleCounter_);
ch3_.setSo((so >> 2 & 0x00010001) * 0xFFFF);
ch4_.setSo((so >> 3 & 0x00010001) * 0xFFFF, cycleCounter_);
}
unsigned PSG::getStatus() const {
return ch1_.isActive()
| ch2_.isActive() << 1
| ch3_.isActive() << 2
| ch4_.isActive() << 3;
}
// the buffer and position are not saved, as they're set and flushed on each runfor() call
SYNCFUNC(PSG)
{
SSS(ch1_);
SSS(ch2_);
SSS(ch3_);
SSS(ch4_);
NSS(lastUpdate_);
NSS(cycleCounter_);
NSS(soVol_);
NSS(rsum_);
NSS(enabled_);
}

View File

@ -1,98 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef SOUND_H
#define SOUND_H
#include "sound/channel1.h"
#include "sound/channel2.h"
#include "sound/channel3.h"
#include "sound/channel4.h"
#include "newstate.h"
namespace gambatte {
class PSG {
public:
PSG();
void init(bool cgb);
void reset(bool ds);
void divReset(bool ds);
void setStatePtrs(SaveState &state);
void loadState(SaveState const &state);
void generateSamples(unsigned long cycleCounter, bool doubleSpeed);
void resetCounter(unsigned long newCc, unsigned long oldCc, bool doubleSpeed);
void speedChange(unsigned long cc, bool doubleSpeed);
std::size_t fillBuffer();
void setBuffer(uint_least32_t *buf) { buffer_ = buf; bufferPos_ = 0; }
bool isEnabled() const { return enabled_; }
void setEnabled(bool value) { enabled_ = value; }
void setNr10(unsigned data) { ch1_.setNr0(data); }
void setNr11(unsigned data) { ch1_.setNr1(data, cycleCounter_); }
void setNr12(unsigned data) { ch1_.setNr2(data, cycleCounter_); }
void setNr13(unsigned data) { ch1_.setNr3(data, cycleCounter_); }
void setNr14(unsigned data, bool ds) { ch1_.setNr4(data, cycleCounter_, !(lastUpdate_ & ds)); }
void setNr21(unsigned data) { ch2_.setNr1(data, cycleCounter_); }
void setNr22(unsigned data) { ch2_.setNr2(data, cycleCounter_); }
void setNr23(unsigned data) { ch2_.setNr3(data, cycleCounter_); }
void setNr24(unsigned data, bool ds) { ch2_.setNr4(data, cycleCounter_, !(lastUpdate_ & ds)); }
void setNr30(unsigned data) { ch3_.setNr0(data); }
void setNr31(unsigned data) { ch3_.setNr1(data, cycleCounter_); }
void setNr32(unsigned data) { ch3_.setNr2(data); }
void setNr33(unsigned data) { ch3_.setNr3(data); }
void setNr34(unsigned data) { ch3_.setNr4(data, cycleCounter_); }
unsigned waveRamRead(unsigned index) const { return ch3_.waveRamRead(index, cycleCounter_); }
void waveRamWrite(unsigned index, unsigned data) { ch3_.waveRamWrite(index, data, cycleCounter_); }
void setNr41(unsigned data) { ch4_.setNr1(data, cycleCounter_); }
void setNr42(unsigned data) { ch4_.setNr2(data, cycleCounter_); }
void setNr43(unsigned data) { ch4_.setNr3(data, cycleCounter_); }
void setNr44(unsigned data) { ch4_.setNr4(data, cycleCounter_); }
void setSoVolume(unsigned nr50);
void mapSo(unsigned nr51);
unsigned getStatus() const;
private:
Channel1 ch1_;
Channel2 ch2_;
Channel3 ch3_;
Channel4 ch4_;
uint_least32_t *buffer_;
std::size_t bufferPos_;
unsigned long lastUpdate_;
unsigned long cycleCounter_;
unsigned long soVol_;
uint_least32_t rsum_;
bool enabled_;
void accumulateChannels(unsigned long cycles);
public:
template<bool isReader>void SyncState(NewState *ns);
};
}
#endif

View File

@ -1,259 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "channel1.h"
#include "psgdef.h"
#include "../savestate.h"
#include <algorithm>
using namespace gambatte;
Channel1::SweepUnit::SweepUnit(MasterDisabler &disabler, DutyUnit &dutyUnit)
: disableMaster_(disabler)
, dutyUnit_(dutyUnit)
, shadow_(0)
, nr0_(0)
, neg_(false)
, cgb_(false)
{
}
unsigned Channel1::SweepUnit::calcFreq() {
unsigned const freq = nr0_ & psg_nr10_neg
? shadow_ - (shadow_ >> (nr0_ & psg_nr10_rsh))
: shadow_ + (shadow_ >> (nr0_ & psg_nr10_rsh));
if (nr0_ & psg_nr10_neg)
neg_ = true;
if (freq & 2048)
disableMaster_();
return freq;
}
void Channel1::SweepUnit::event() {
unsigned long const period = (nr0_ & psg_nr10_time) / (1u * psg_nr10_time & -psg_nr10_time);
if (period) {
unsigned const freq = calcFreq();
if (!(freq & 2048) && nr0_ & psg_nr10_rsh) {
shadow_ = freq;
dutyUnit_.setFreq(freq, counter_);
calcFreq();
}
counter_ += period << 14;
} else
counter_ += 8ul << 14;
}
void Channel1::SweepUnit::nr0Change(unsigned newNr0) {
if (neg_ && !(newNr0 & 0x08))
disableMaster_();
nr0_ = newNr0;
}
void Channel1::SweepUnit::nr4Init(unsigned long const cc) {
neg_ = false;
shadow_ = dutyUnit_.freq();
unsigned const period = (nr0_ & psg_nr10_time) / (1u * psg_nr10_time & -psg_nr10_time);
unsigned const rsh = nr0_ & psg_nr10_rsh;
if (period | rsh)
counter_ = ((((cc + 2 + cgb_ * 2) >> 14) + (period ? period : 8)) << 14) + 2;
else
counter_ = counter_disabled;
if (rsh)
calcFreq();
}
void Channel1::SweepUnit::reset() {
counter_ = counter_disabled;
}
void Channel1::SweepUnit::loadState(SaveState const &state) {
counter_ = std::max(state.spu.ch1.sweep.counter, state.spu.cycleCounter);
shadow_ = state.spu.ch1.sweep.shadow;
nr0_ = state.spu.ch1.sweep.nr0;
neg_ = state.spu.ch1.sweep.neg;
}
template<bool isReader>
void Channel1::SweepUnit::SyncState(NewState *ns)
{
NSS(counter_);
NSS(shadow_);
NSS(nr0_);
NSS(neg_);
NSS(cgb_);
}
Channel1::Channel1()
: staticOutputTest_(*this, dutyUnit_)
, disableMaster_(master_, dutyUnit_)
, lengthCounter_(disableMaster_, 0x3F)
, envelopeUnit_(staticOutputTest_)
, sweepUnit_(disableMaster_, dutyUnit_)
, nextEventUnit_(0)
, soMask_(0)
, prevOut_(0)
, nr4_(0)
, master_(false)
{
setEvent();
}
void Channel1::setEvent() {
nextEventUnit_ = &sweepUnit_;
if (envelopeUnit_.counter() < nextEventUnit_->counter())
nextEventUnit_ = &envelopeUnit_;
if (lengthCounter_.counter() < nextEventUnit_->counter())
nextEventUnit_ = &lengthCounter_;
}
void Channel1::setNr0(unsigned data) {
sweepUnit_.nr0Change(data);
setEvent();
}
void Channel1::setNr1(unsigned data, unsigned long cc) {
lengthCounter_.nr1Change(data, nr4_, cc);
dutyUnit_.nr1Change(data, cc);
setEvent();
}
void Channel1::setNr2(unsigned data, unsigned long cc) {
if (envelopeUnit_.nr2Change(data))
disableMaster_();
else
staticOutputTest_(cc);
setEvent();
}
void Channel1::setNr3(unsigned data, unsigned long cc) {
dutyUnit_.nr3Change(data, cc);
setEvent();
}
void Channel1::setNr4(unsigned data, unsigned long cc, unsigned long ref) {
lengthCounter_.nr4Change(nr4_, data, cc);
dutyUnit_.nr4Change(data, cc, ref, master_);
nr4_ = data;
if (nr4_ & psg_nr4_init) {
nr4_ -= psg_nr4_init;
master_ = !envelopeUnit_.nr4Init(cc);
sweepUnit_.nr4Init(cc);
staticOutputTest_(cc);
}
setEvent();
}
void Channel1::setSo(unsigned long soMask, unsigned long cc) {
soMask_ = soMask;
staticOutputTest_(cc);
setEvent();
}
void Channel1::reset() {
dutyUnit_.reset();
envelopeUnit_.reset();
sweepUnit_.reset();
setEvent();
}
void Channel1::init(bool cgb) {
sweepUnit_.init(cgb);
}
void Channel1::loadState(SaveState const &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);
nr4_ = state.spu.ch1.nr4;
master_ = state.spu.ch1.master;
}
void Channel1::update(uint_least32_t* buf, unsigned long const soBaseVol, unsigned long cc, unsigned long const end) {
unsigned long const outBase = envelopeUnit_.dacIsOn() ? soBaseVol & soMask_ : 0;
unsigned long const outLow = outBase * -15;
while (cc < end) {
unsigned long const outHigh = master_
? outBase * (envelopeUnit_.getVolume() * 2l - 15)
: outLow;
unsigned long const nextMajorEvent = std::min(nextEventUnit_->counter(), end);
unsigned long out = dutyUnit_.isHighState() ? outHigh : outLow;
while (dutyUnit_.counter() <= nextMajorEvent) {
*buf = out - prevOut_;
prevOut_ = out;
buf += dutyUnit_.counter() - cc;
cc = dutyUnit_.counter();
dutyUnit_.event();
out = dutyUnit_.isHighState() ? outHigh : outLow;
}
if (cc < nextMajorEvent) {
*buf = out - prevOut_;
prevOut_ = out;
buf += nextMajorEvent - cc;
cc = nextMajorEvent;
}
if (nextEventUnit_->counter() == nextMajorEvent) {
nextEventUnit_->event();
setEvent();
}
}
if (cc >= SoundUnit::counter_max) {
dutyUnit_.resetCounters(cc);
lengthCounter_.resetCounters(cc);
envelopeUnit_.resetCounters(cc);
sweepUnit_.resetCounters(cc);
}
}
SYNCFUNC(Channel1)
{
SSS(lengthCounter_);
SSS(dutyUnit_);
SSS(envelopeUnit_);
SSS(sweepUnit_);
EBS(nextEventUnit_, 0);
EVS(nextEventUnit_, &dutyUnit_, 1);
EVS(nextEventUnit_, &sweepUnit_, 2);
EVS(nextEventUnit_, &envelopeUnit_, 3);
EVS(nextEventUnit_, &lengthCounter_, 4);
EES(nextEventUnit_, NULL);
NSS(soMask_);
NSS(prevOut_);
NSS(nr4_);
NSS(master_);
}

View File

@ -1,98 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef SOUND_CHANNEL1_H
#define SOUND_CHANNEL1_H
#include "duty_unit.h"
#include "envelope_unit.h"
#include "gbint.h"
#include "length_counter.h"
#include "master_disabler.h"
#include "static_output_tester.h"
#include "newstate.h"
namespace gambatte {
struct SaveState;
class Channel1 {
public:
Channel1();
void setNr0(unsigned data);
void setNr1(unsigned data, unsigned long cc);
void setNr2(unsigned data, unsigned long cc);
void setNr3(unsigned data, unsigned long cc);
void setNr4(unsigned data, unsigned long cc, unsigned long ref);
void setSo(unsigned long soMask, unsigned long cc);
bool isActive() const { return master_; }
void update(uint_least32_t* buf, unsigned long soBaseVol, unsigned long cc, unsigned long end);
void reset();
void resetCc(unsigned long cc, unsigned long ncc) { dutyUnit_.resetCc(cc, ncc); }
void init(bool cgb);
void loadState(SaveState const &state);
private:
class SweepUnit : public SoundUnit {
public:
SweepUnit(MasterDisabler &disabler, DutyUnit &dutyUnit);
virtual void event();
void nr0Change(unsigned newNr0);
void nr4Init(unsigned long cycleCounter);
void reset();
void resetCc(unsigned long cc, unsigned long ncc) { dutyUnit_.resetCc(cc, ncc); }
void init(bool cgb) { cgb_ = cgb; }
void loadState(SaveState const &state);
private:
MasterDisabler &disableMaster_;
DutyUnit &dutyUnit_;
unsigned short shadow_;
unsigned char nr0_;
bool neg_;
bool cgb_;
unsigned calcFreq();
public:
template<bool isReader>void SyncState(NewState *ns);
};
friend class StaticOutputTester<Channel1, DutyUnit>;
StaticOutputTester<Channel1, DutyUnit> staticOutputTest_;
DutyMasterDisabler disableMaster_;
LengthCounter lengthCounter_;
DutyUnit dutyUnit_;
EnvelopeUnit envelopeUnit_;
SweepUnit sweepUnit_;
SoundUnit *nextEventUnit_;
unsigned long soMask_;
unsigned long prevOut_;
unsigned char nr4_;
bool master_;
void setEvent();
public:
template<bool isReader>void SyncState(NewState *ns);
};
}
#endif

View File

@ -1,158 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "channel2.h"
#include "psgdef.h"
#include "../savestate.h"
#include <algorithm>
using namespace gambatte;
Channel2::Channel2()
: staticOutputTest_(*this, dutyUnit_)
, disableMaster_(master_, dutyUnit_)
, lengthCounter_(disableMaster_, 0x3F)
, envelopeUnit_(staticOutputTest_)
, soMask_(0)
, prevOut_(0)
, nr4_(0)
, master_(false)
{
setEvent();
}
void Channel2::setEvent() {
nextEventUnit = &envelopeUnit_;
if (lengthCounter_.counter() < nextEventUnit->counter())
nextEventUnit = &lengthCounter_;
}
void Channel2::setNr1(unsigned data, unsigned long cc) {
lengthCounter_.nr1Change(data, nr4_, cc);
dutyUnit_.nr1Change(data, cc);
setEvent();
}
void Channel2::setNr2(unsigned data, unsigned long cc) {
if (envelopeUnit_.nr2Change(data))
disableMaster_();
else
staticOutputTest_(cc);
setEvent();
}
void Channel2::setNr3(unsigned data, unsigned long cc) {
dutyUnit_.nr3Change(data, cc);
setEvent();
}
void Channel2::setNr4(unsigned data, unsigned long cc, unsigned long ref) {
lengthCounter_.nr4Change(nr4_, data, cc);
nr4_ = data;
if (nr4_ & psg_nr4_init) {
nr4_ -= psg_nr4_init;
master_ = !envelopeUnit_.nr4Init(cc);
staticOutputTest_(cc);
}
dutyUnit_.nr4Change(data, cc, ref, master_);
setEvent();
}
void Channel2::setSo(unsigned long soMask, unsigned long cc) {
soMask_ = soMask;
staticOutputTest_(cc);
setEvent();
}
void Channel2::reset() {
dutyUnit_.reset();
envelopeUnit_.reset();
setEvent();
}
void Channel2::loadState(SaveState const &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);
nr4_ = state.spu.ch2.nr4;
master_ = state.spu.ch2.master;
}
void Channel2::update(uint_least32_t* buf, unsigned long const soBaseVol, unsigned long cc, unsigned long const end) {
unsigned long const outBase = envelopeUnit_.dacIsOn() ? soBaseVol & soMask_ : 0;
unsigned long const outLow = outBase * -15;
while (cc < end) {
unsigned long const outHigh = master_
? outBase * (envelopeUnit_.getVolume() * 2l - 15)
: outLow;
unsigned long const nextMajorEvent = std::min(nextEventUnit->counter(), end);
unsigned long out = dutyUnit_.isHighState() ? outHigh : outLow;
while (dutyUnit_.counter() <= nextMajorEvent) {
*buf += out - prevOut_;
prevOut_ = out;
buf += dutyUnit_.counter() - cc;
cc = dutyUnit_.counter();
dutyUnit_.event();
out = dutyUnit_.isHighState() ? outHigh : outLow;
}
if (cc < nextMajorEvent) {
*buf += out - prevOut_;
prevOut_ = out;
buf += nextMajorEvent - cc;
cc = nextMajorEvent;
}
if (nextEventUnit->counter() == nextMajorEvent) {
nextEventUnit->event();
setEvent();
}
}
if (cc >= SoundUnit::counter_max) {
dutyUnit_.resetCounters(cc);
lengthCounter_.resetCounters(cc);
envelopeUnit_.resetCounters(cc);
}
}
SYNCFUNC(Channel2)
{
SSS(lengthCounter_);
SSS(dutyUnit_);
SSS(envelopeUnit_);
EBS(nextEventUnit, 0);
EVS(nextEventUnit, &dutyUnit_, 1);
EVS(nextEventUnit, &envelopeUnit_, 2);
EVS(nextEventUnit, &lengthCounter_, 3);
EES(nextEventUnit, NULL);
NSS(soMask_);
NSS(prevOut_);
NSS(nr4_);
NSS(master_);
}

View File

@ -1,69 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef SOUND_CHANNEL2_H
#define SOUND_CHANNEL2_H
#include "duty_unit.h"
#include "envelope_unit.h"
#include "gbint.h"
#include "length_counter.h"
#include "static_output_tester.h"
#include "newstate.h"
namespace gambatte {
struct SaveState;
class Channel2 {
public:
Channel2();
void setNr1(unsigned data, unsigned long cc);
void setNr2(unsigned data, unsigned long cc);
void setNr3(unsigned data, unsigned long cc);
void setNr4(unsigned data, unsigned long cc, unsigned long ref);
void setSo(unsigned long soMask, unsigned long cc);
bool isActive() const { return master_; }
void update(uint_least32_t* buf, unsigned long soBaseVol, unsigned long cc, unsigned long end);
void reset();
void resetCc(unsigned long cc, unsigned long ncc) { dutyUnit_.resetCc(cc, ncc); }
void loadState(SaveState const &state);
private:
friend class StaticOutputTester<Channel2, DutyUnit>;
StaticOutputTester<Channel2, DutyUnit> staticOutputTest_;
DutyMasterDisabler disableMaster_;
LengthCounter lengthCounter_;
DutyUnit dutyUnit_;
EnvelopeUnit envelopeUnit_;
SoundUnit *nextEventUnit;
unsigned long soMask_;
unsigned long prevOut_;
unsigned char nr4_;
bool master_;
void setEvent();
public:
template<bool isReader>void SyncState(NewState *ns);
};
}
#endif

View File

@ -1,222 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "channel3.h"
#include "psgdef.h"
#include "../savestate.h"
#include <algorithm>
#include <cstring>
using namespace gambatte;
namespace {
unsigned toPeriod(unsigned nr3, unsigned nr4) {
return 0x800 - ((nr4 << 8 & 0x700) | nr3);
}
}
Channel3::Channel3()
: disableMaster_(master_, waveCounter_)
, lengthCounter_(disableMaster_, 0xFF)
, 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(unsigned data) {
nr0_ = data & psg_nr4_init;
if (!nr0_)
disableMaster_();
}
void Channel3::setNr2(unsigned data) {
rshift_ = std::min((data >> 5 & 3) - 1, 4u);
}
void Channel3::setNr4(unsigned const data, unsigned long const cc) {
lengthCounter_.nr4Change(nr4_, data, cc);
nr4_ = data & ~(1u * psg_nr4_init);
if (data & nr0_) {
if (!cgb_ && waveCounter_ == cc + 1) {
int const pos = (wavePos_ + 1) / 2 % sizeof waveRam_;
if (pos < 4)
waveRam_[0] = waveRam_[pos];
else
std::memcpy(waveRam_, waveRam_ + (pos & ~3), 4);
}
master_ = true;
wavePos_ = 0;
lastReadTime_ = waveCounter_ = cc + toPeriod(nr3_, data) + 3;
}
}
void Channel3::setSo(unsigned long soMask) {
soMask_ = soMask;
}
void Channel3::reset() {
sampleBuf_ = 0;
}
void Channel3::resetCc(unsigned long cc, unsigned long newCc) {
lastReadTime_ -= cc - newCc;
if (waveCounter_ != SoundUnit::counter_disabled)
waveCounter_ -= cc - newCc;
}
void Channel3::init(bool cgb) {
cgb_ = cgb;
}
void Channel3::setStatePtrs(SaveState &state) {
state.spu.ch3.waveRam.set(waveRam_, sizeof waveRam_);
}
void Channel3::loadState(SaveState const &state) {
lengthCounter_.loadState(state.spu.ch3.lcounter, 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 % (2 * sizeof waveRam_);
sampleBuf_ = state.spu.ch3.sampleBuf;
master_ = state.spu.ch3.master;
nr0_ = state.mem.ioamhram.get()[0x11A] & psg_nr4_init;
setNr2(state.mem.ioamhram.get()[0x11C]);
}
void Channel3::updateWaveCounter(unsigned long const cc) {
if (cc >= waveCounter_) {
unsigned const period = toPeriod(nr3_, nr4_);
unsigned long const periods = (cc - waveCounter_) / period;
lastReadTime_ = waveCounter_ + periods * period;
waveCounter_ = lastReadTime_ + period;
wavePos_ = (wavePos_ + periods + 1) % (2 * sizeof waveRam_);
sampleBuf_ = waveRam_[wavePos_ / 2];
}
}
void Channel3::update(uint_least32_t* buf, unsigned long const soBaseVol, unsigned long cc, unsigned long const end) {
unsigned long const outBase = nr0_ ? soBaseVol & soMask_ : 0;
if (outBase && rshift_ != 4) {
while (std::min(waveCounter_, lengthCounter_.counter()) <= end) {
unsigned pos = wavePos_;
unsigned const period = toPeriod(nr3_, nr4_), rsh = rshift_;
unsigned long const nextMajorEvent =
std::min(lengthCounter_.counter(), end);
unsigned long cnt = waveCounter_, prevOut = prevOut_;
unsigned long out = master_
? ((pos % 2 ? sampleBuf_ & 0xF : sampleBuf_ >> 4) >> rsh) * 2l - 15
: -15;
out *= outBase;
while (cnt <= nextMajorEvent) {
*buf += out - prevOut;
prevOut = out;
buf += cnt - cc;
cc = cnt;
cnt += period;
++pos;
unsigned const s = waveRam_[pos / 2 % sizeof waveRam_];
out = ((pos % 2 ? s & 0xF : s >> 4) >> rsh) * 2l - 15;
out *= outBase;
}
if (cnt != waveCounter_) {
wavePos_ = pos % (2 * sizeof waveRam_);
sampleBuf_ = waveRam_[wavePos_ / 2];
prevOut_ = prevOut;
waveCounter_ = cnt;
lastReadTime_ = cc;
}
if (cc < nextMajorEvent) {
*buf += out - prevOut_;
prevOut_ = out;
buf += nextMajorEvent - cc;
cc = nextMajorEvent;
}
if (lengthCounter_.counter() == nextMajorEvent)
lengthCounter_.event();
}
if (cc < end) {
unsigned long out = master_
? ((wavePos_ % 2 ? sampleBuf_ & 0xF : sampleBuf_ >> 4) >> rshift_) * 2l - 15
: -15;
out *= outBase;
*buf += out - prevOut_;
prevOut_ = out;
cc = end;
}
}
else {
unsigned long const out = outBase * -15;
*buf += out - prevOut_;
prevOut_ = out;
cc = end;
while (lengthCounter_.counter() <= cc) {
updateWaveCounter(lengthCounter_.counter());
lengthCounter_.event();
}
updateWaveCounter(cc);
}
if (cc >= SoundUnit::counter_max) {
lengthCounter_.resetCounters(cc);
lastReadTime_ -= SoundUnit::counter_max;
if (waveCounter_ != SoundUnit::counter_disabled)
waveCounter_ -= SoundUnit::counter_max;
}
}
SYNCFUNC(Channel3)
{
NSS(waveRam_);
SSS(lengthCounter_);
NSS(soMask_);
NSS(prevOut_);
NSS(waveCounter_);
NSS(lastReadTime_);
NSS(nr0_);
NSS(nr3_);
NSS(nr4_);
NSS(wavePos_);
NSS(rshift_);
NSS(sampleBuf_);
NSS(master_);
NSS(cgb_);
}

View File

@ -1,110 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef SOUND_CHANNEL3_H
#define SOUND_CHANNEL3_H
#include "gbint.h"
#include "length_counter.h"
#include "master_disabler.h"
#include "newstate.h"
namespace gambatte {
struct SaveState;
class Channel3 {
public:
Channel3();
bool isActive() const { return master_; }
bool isCgb() const { return cgb_; }
void reset();
void resetCc(unsigned long cc, unsigned long newCc);
void init(bool cgb);
void setStatePtrs(SaveState &state);
void loadState(SaveState const &state);
void setNr0(unsigned data);
void setNr1(unsigned data, unsigned long cc) { lengthCounter_.nr1Change(data, nr4_, cc); }
void setNr2(unsigned data);
void setNr3(unsigned data) { nr3_ = data; }
void setNr4(unsigned data, unsigned long cc);
void setSo(unsigned long soMask);
void update(uint_least32_t* buf, unsigned long soBaseVol, unsigned long cc, unsigned long end);
unsigned waveRamRead(unsigned index, unsigned long cc) const {
if (master_) {
if (!cgb_ && cc != lastReadTime_)
return 0xFF;
index = wavePos_ / 2;
}
return waveRam_[index];
}
void waveRamWrite(unsigned index, unsigned data, unsigned long cc) {
if (master_) {
if (!cgb_ && cc != lastReadTime_)
return;
index = wavePos_ / 2;
}
waveRam_[index] = data;
}
private:
class Ch3MasterDisabler : public MasterDisabler {
public:
Ch3MasterDisabler(bool &m, unsigned long &wC) : MasterDisabler(m), waveCounter_(wC) {}
virtual void operator()() {
MasterDisabler::operator()();
waveCounter_ = SoundUnit::counter_disabled;
}
private:
unsigned long &waveCounter_;
};
unsigned char waveRam_[0x10];
Ch3MasterDisabler disableMaster_;
LengthCounter lengthCounter_;
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:
template<bool isReader>void SyncState(NewState *ns);
};
}
#endif

View File

@ -1,274 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "channel4.h"
#include "psgdef.h"
#include "../savestate.h"
#include <algorithm>
using namespace gambatte;
namespace {
static unsigned long toPeriod(unsigned const nr3) {
unsigned s = nr3 / (1u * psg_nr43_s & -psg_nr43_s) + 3;
unsigned r = nr3 & psg_nr43_r;
if (!r) {
r = 1;
--s;
}
return r << s;
}
}
Channel4::Lfsr::Lfsr()
: backupCounter_(counter_disabled)
, reg_(0x7FFF)
, nr3_(0)
, master_(false)
{
}
void Channel4::Lfsr::updateBackupCounter(unsigned long const cc) {
if (backupCounter_ <= cc) {
unsigned long const period = toPeriod(nr3_);
unsigned long periods = (cc - backupCounter_) / period + 1;
backupCounter_ += periods * period;
if (master_ && nr3_ < 0xE * (1u * psg_nr43_s & -psg_nr43_s)) {
if (nr3_ & psg_nr43_7biten) {
while (periods > 6) {
unsigned const xored = (reg_ << 1 ^ reg_) & 0x7E;
reg_ = (reg_ >> 6 & ~0x7Eu) | xored | xored << 8;
periods -= 6;
}
unsigned const xored = ((reg_ ^ reg_ >> 1) << (7 - periods)) & 0x7F;
reg_ = (reg_ >> periods & ~(0x80u - (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(unsigned long cc) {
updateBackupCounter(cc);
counter_ = backupCounter_;
}
inline void Channel4::Lfsr::event() {
if (nr3_ < 0xE * (1u * psg_nr43_s & -psg_nr43_s)) {
unsigned const shifted = reg_ >> 1;
unsigned const xored = (reg_ ^ shifted) & 1;
reg_ = shifted | xored << 14;
if (nr3_ & psg_nr43_7biten)
reg_ = (reg_ & ~0x40u) | xored << 6;
}
counter_ += toPeriod(nr3_);
backupCounter_ = counter_;
}
void Channel4::Lfsr::nr3Change(unsigned newNr3, unsigned long cc) {
updateBackupCounter(cc);
nr3_ = newNr3;
counter_ = cc;
}
void Channel4::Lfsr::nr4Init(unsigned long cc) {
disableMaster();
updateBackupCounter(cc);
master_ = true;
backupCounter_ += 4;
counter_ = backupCounter_;
}
void Channel4::Lfsr::reset(unsigned long cc) {
nr3_ = 0;
disableMaster();
backupCounter_ = cc + toPeriod(nr3_);
}
void Channel4::Lfsr::resetCc(unsigned long cc, unsigned long newCc) {
updateBackupCounter(cc);
backupCounter_ -= cc - newCc;
if (counter_ != counter_disabled)
counter_ -= cc - newCc;
}
void Channel4::Lfsr::resetCounters(unsigned long oldCc) {
updateBackupCounter(oldCc);
backupCounter_ -= counter_max;
SoundUnit::resetCounters(oldCc);
}
void Channel4::Lfsr::loadState(SaveState const &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];
}
template<bool isReader>
void Channel4::Lfsr::SyncState(NewState *ns)
{
NSS(counter_);
NSS(backupCounter_);
NSS(reg_);
NSS(nr3_);
NSS(master_);
}
Channel4::Channel4()
: staticOutputTest_(*this, lfsr_)
, disableMaster_(master_, lfsr_)
, lengthCounter_(disableMaster_, 0x3F)
, envelopeUnit_(staticOutputTest_)
, nextEventUnit_(0)
, soMask_(0)
, prevOut_(0)
, nr4_(0)
, master_(false)
{
setEvent();
}
void Channel4::setEvent() {
nextEventUnit_ = &envelopeUnit_;
if (lengthCounter_.counter() < nextEventUnit_->counter())
nextEventUnit_ = &lengthCounter_;
}
void Channel4::setNr1(unsigned data, unsigned long cc) {
lengthCounter_.nr1Change(data, nr4_, cc);
setEvent();
}
void Channel4::setNr2(unsigned data, unsigned long cc) {
if (envelopeUnit_.nr2Change(data))
disableMaster_();
else
staticOutputTest_(cc);
setEvent();
}
void Channel4::setNr4(unsigned const data, unsigned long const cc) {
lengthCounter_.nr4Change(nr4_, data, cc);
nr4_ = data;
if (nr4_ & psg_nr4_init) {
nr4_ -= psg_nr4_init;
master_ = !envelopeUnit_.nr4Init(cc);
if (master_)
lfsr_.nr4Init(cc);
staticOutputTest_(cc);
}
setEvent();
}
void Channel4::setSo(unsigned long soMask, unsigned long cc) {
soMask_ = soMask;
staticOutputTest_(cc);
setEvent();
}
void Channel4::reset(unsigned long cc) {
lfsr_.reset(cc);
envelopeUnit_.reset();
setEvent();
}
void Channel4::loadState(SaveState const &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);
nr4_ = state.spu.ch4.nr4;
master_ = state.spu.ch4.master;
}
void Channel4::update(uint_least32_t* buf, unsigned long const soBaseVol, unsigned long cc, unsigned long const end) {
unsigned long const outBase = envelopeUnit_.dacIsOn() ? soBaseVol & soMask_ : 0;
unsigned long const outLow = outBase * -15;
while (cc < end) {
unsigned long const outHigh = outBase * (envelopeUnit_.getVolume() * 2l - 15);
unsigned long const nextMajorEvent = std::min(nextEventUnit_->counter(), end);
unsigned long out = lfsr_.isHighState() ? outHigh : outLow;
if (lfsr_.counter() <= nextMajorEvent) {
Lfsr lfsr = lfsr_;
while (lfsr.counter() <= nextMajorEvent) {
*buf += out - prevOut_;
prevOut_ = out;
buf += lfsr.counter() - cc;
cc = lfsr.counter();
lfsr.event();
out = lfsr.isHighState() ? outHigh : outLow;
}
lfsr_ = lfsr;
}
if (cc < nextMajorEvent) {
*buf += out - prevOut_;
prevOut_ = out;
buf += nextMajorEvent - cc;
cc = nextMajorEvent;
}
if (nextEventUnit_->counter() == nextMajorEvent) {
nextEventUnit_->event();
setEvent();
}
}
if (cc >= SoundUnit::counter_max) {
lengthCounter_.resetCounters(cc);
lfsr_.resetCounters(cc);
envelopeUnit_.resetCounters(cc);
}
}
SYNCFUNC(Channel4)
{
SSS(lengthCounter_);
SSS(envelopeUnit_);
SSS(lfsr_);
EBS(nextEventUnit_, 0);
EVS(nextEventUnit_, &lfsr_, 1);
EVS(nextEventUnit_, &envelopeUnit_, 2);
EVS(nextEventUnit_, &lengthCounter_, 3);
EES(nextEventUnit_, NULL);
NSS(soMask_);
NSS(prevOut_);
NSS(nr4_);
NSS(master_);
}

View File

@ -1,105 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef SOUND_CHANNEL4_H
#define SOUND_CHANNEL4_H
#include "envelope_unit.h"
#include "gbint.h"
#include "length_counter.h"
#include "master_disabler.h"
#include "static_output_tester.h"
#include "newstate.h"
namespace gambatte {
struct SaveState;
class Channel4 {
public:
Channel4();
void setNr1(unsigned data, unsigned long cc);
void setNr2(unsigned data, unsigned long cc);
void setNr3(unsigned data, unsigned long cc) { lfsr_.nr3Change(data, cc); }
void setNr4(unsigned data, unsigned long cc);
void setSo(unsigned long soMask, unsigned long cc);
bool isActive() const { return master_; }
void update(uint_least32_t* buf, unsigned long soBaseVol, unsigned long cc, unsigned long end);
void reset(unsigned long cc);
void resetCc(unsigned long cc, unsigned long newCc) { lfsr_.resetCc(cc, newCc); }
void loadState(SaveState const &state);
private:
class Lfsr : public SoundUnit {
public:
Lfsr();
virtual void event();
virtual void resetCounters(unsigned long oldCc);
bool isHighState() const { return ~reg_ & 1; }
void nr3Change(unsigned newNr3, unsigned long cc);
void nr4Init(unsigned long cc);
void reset(unsigned long cc);
void resetCc(unsigned long cc, unsigned long newCc);
void loadState(SaveState const &state);
void disableMaster() { killCounter(); master_ = false; reg_ = 0x7FFF; }
void killCounter() { counter_ = counter_disabled; }
void reviveCounter(unsigned long cc);
private:
unsigned long backupCounter_;
unsigned short reg_;
unsigned char nr3_;
bool master_;
void updateBackupCounter(unsigned long cc);
public:
template<bool isReader>void SyncState(NewState *ns);
};
class Ch4MasterDisabler : public MasterDisabler {
public:
Ch4MasterDisabler(bool &m, Lfsr &lfsr) : MasterDisabler(m), lfsr_(lfsr) {}
virtual void operator()() { MasterDisabler::operator()(); lfsr_.disableMaster(); }
private:
Lfsr &lfsr_;
};
friend class StaticOutputTester<Channel4, Lfsr>;
StaticOutputTester<Channel4, Lfsr> staticOutputTest_;
Ch4MasterDisabler disableMaster_;
LengthCounter lengthCounter_;
EnvelopeUnit envelopeUnit_;
Lfsr lfsr_;
SoundUnit *nextEventUnit_;
unsigned long soMask_;
unsigned long prevOut_;
unsigned char nr4_;
bool master_;
void setEvent();
public:
template<bool isReader>void SyncState(NewState *ns);
};
}
#endif

View File

@ -1,169 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "duty_unit.h"
#include "psgdef.h"
#include <algorithm>
namespace {
int const duty_pattern_len = 8;
bool toOutState(unsigned duty, unsigned pos) {
return 0x7EE18180 >> (duty * duty_pattern_len + pos) & 1;
}
unsigned toPeriod(unsigned freq) {
return (2048 - freq) * 2;
}
}
using namespace gambatte;
DutyUnit::DutyUnit()
: nextPosUpdate_(counter_disabled)
, period_(4096)
, pos_(0)
, duty_(0)
, inc_(0)
, high_(false)
, enableEvents_(true)
{
}
void DutyUnit::updatePos(unsigned long const cc) {
if (cc >= nextPosUpdate_) {
unsigned long const inc = (cc - nextPosUpdate_) / period_ + 1;
nextPosUpdate_ += period_ * inc;
pos_ = (pos_ + inc) % duty_pattern_len;
high_ = toOutState(duty_, pos_);
}
}
void DutyUnit::setCounter() {
static unsigned char const nextStateDistance[][duty_pattern_len] = {
{ 7, 6, 5, 4, 3, 2, 1, 1 },
{ 1, 6, 5, 4, 3, 2, 1, 2 },
{ 1, 4, 3, 2, 1, 4, 3, 2 },
{ 1, 6, 5, 4, 3, 2, 1, 2 }
};
if (enableEvents_ && nextPosUpdate_ != counter_disabled) {
unsigned const npos = (pos_ + 1) % duty_pattern_len;
counter_ = nextPosUpdate_;
inc_ = nextStateDistance[duty_][npos];
if (toOutState(duty_, npos) == high_) {
counter_ += period_ * inc_;
inc_ = nextStateDistance[duty_][(npos + inc_) % duty_pattern_len];
}
} else
counter_ = counter_disabled;
}
void DutyUnit::setFreq(unsigned newFreq, unsigned long cc) {
updatePos(cc);
period_ = toPeriod(newFreq);
setCounter();
}
void DutyUnit::event() {
static unsigned char const inc[][2] = {
{ 1, 7 },
{ 2, 6 },
{ 4, 4 },
{ 6, 2 }
};
high_ ^= true;
counter_ += inc_ * period_;
inc_ = inc[duty_][high_];
}
void DutyUnit::nr1Change(unsigned newNr1, unsigned long cc) {
updatePos(cc);
duty_ = newNr1 >> 6;
setCounter();
}
void DutyUnit::nr3Change(unsigned newNr3, unsigned long cc) {
setFreq((freq() & 0x700) | newNr3, cc);
}
void DutyUnit::nr4Change(unsigned const newNr4, unsigned long const cc, unsigned long const ref, bool const master) {
setFreq((newNr4 << 8 & 0x700) | (freq() & 0xFF), cc);
if (newNr4 & psg_nr4_init) {
nextPosUpdate_ = cc - (cc - ref) % 2 + period_ + 4 - (master << 1);
setCounter();
}
}
void DutyUnit::reset() {
pos_ = 0;
high_ = false;
nextPosUpdate_ = counter_disabled;
setCounter();
}
void DutyUnit::resetCc(unsigned long cc, unsigned long newCc) {
if (nextPosUpdate_ == counter_disabled)
return;
updatePos(cc);
nextPosUpdate_ -= cc - newCc;
setCounter();
}
void DutyUnit::loadState(SaveState::SPU::Duty const &dstate,
unsigned const nr1, unsigned const nr4, unsigned long const cc) {
nextPosUpdate_ = std::max(dstate.nextPosUpdate, cc);
pos_ = dstate.pos & 7;
high_ = dstate.high;
duty_ = nr1 >> 6;
period_ = toPeriod((nr4 << 8 & 0x700) | dstate.nr3);
enableEvents_ = true;
setCounter();
}
void DutyUnit::resetCounters(unsigned long cc) {
resetCc(cc, cc - counter_max);
}
void DutyUnit::killCounter() {
enableEvents_ = false;
setCounter();
}
void DutyUnit::reviveCounter(unsigned long const cc) {
updatePos(cc);
enableEvents_ = true;
setCounter();
}
SYNCFUNC(DutyUnit)
{
NSS(counter_);
NSS(nextPosUpdate_);
NSS(period_);
NSS(pos_);
NSS(duty_);
NSS(inc_);
NSS(high_);
NSS(enableEvents_);
}

View File

@ -1,76 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef DUTY_UNIT_H
#define DUTY_UNIT_H
#include "sound_unit.h"
#include "master_disabler.h"
#include "../savestate.h"
#include "newstate.h"
namespace gambatte {
class DutyUnit : public SoundUnit {
public:
DutyUnit();
virtual void event();
virtual void resetCounters(unsigned long oldCc);
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, unsigned long ref, bool master);
void reset();
void resetCc(unsigned long cc, unsigned long newCc);
void loadState(SaveState::SPU::Duty const &dstate, unsigned nr1, unsigned nr4, unsigned long cc);
void killCounter();
void reviveCounter(unsigned long cc);
//intended for use by SweepUnit only.
unsigned freq() const { return 2048 - (period_ >> 1); }
void setFreq(unsigned newFreq, unsigned long cc);
private:
unsigned long nextPosUpdate_;
unsigned short period_;
unsigned char pos_;
unsigned char duty_;
unsigned char inc_;
bool high_;
bool enableEvents_;
void setCounter();
void setDuty(unsigned nr1);
void updatePos(unsigned long cc);
public:
template<bool isReader>void SyncState(NewState *ns);
};
class DutyMasterDisabler : public MasterDisabler {
public:
DutyMasterDisabler(bool &m, DutyUnit &dutyUnit) : MasterDisabler(m), dutyUnit_(dutyUnit) {}
virtual void operator()() { MasterDisabler::operator()(); dutyUnit_.killCounter(); }
private:
DutyUnit &dutyUnit_;
};
}
#endif

View File

@ -1,99 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "envelope_unit.h"
#include "psgdef.h"
#include <algorithm>
using namespace gambatte;
EnvelopeUnit::VolOnOffEvent EnvelopeUnit::nullEvent_;
EnvelopeUnit::EnvelopeUnit(VolOnOffEvent &volOnOffEvent)
: volOnOffEvent_(volOnOffEvent)
, nr2_(0)
, volume_(0)
{
}
void EnvelopeUnit::reset() {
counter_ = counter_disabled;
}
void EnvelopeUnit::loadState(SaveState::SPU::Env const &estate, unsigned nr2, unsigned long cc) {
counter_ = std::max(estate.counter, cc);
volume_ = estate.volume;
nr2_ = nr2;
}
void EnvelopeUnit::event() {
unsigned long const period = nr2_ & psg_nr2_step;
if (period) {
unsigned newVol = volume_;
if (nr2_ & psg_nr2_inc)
++newVol;
else
--newVol;
if (newVol < 0x10) {
volume_ = newVol;
if (volume_ < 2)
volOnOffEvent_(counter_);
counter_ += period << 15;
}
else
counter_ = counter_disabled;
}
else
counter_ += 8ul << 15;
}
bool EnvelopeUnit::nr2Change(unsigned const newNr2) {
if (!(nr2_ & psg_nr2_step) && counter_ != counter_disabled)
++volume_;
else if (!(nr2_ & psg_nr2_inc))
volume_ += 2;
if ((nr2_ ^ newNr2) & psg_nr2_inc)
volume_ = 0x10 - volume_;
volume_ &= 0xF;
nr2_ = newNr2;
return !(newNr2 & (psg_nr2_initvol | psg_nr2_inc));
}
bool EnvelopeUnit::nr4Init(unsigned long const cc) {
unsigned long period = nr2_ & psg_nr2_step ? nr2_ & psg_nr2_step : 8;
if (((cc + 2) & 0x7000) == 0x0000)
++period;
counter_ = cc - ((cc - 0x1000) & 0x7FFF) + period * 0x8000;
volume_ = nr2_ / (1u * psg_nr2_initvol & -psg_nr2_initvol);
return !(nr2_ & (psg_nr2_initvol | psg_nr2_inc));
}
SYNCFUNC(EnvelopeUnit)
{
NSS(counter_);
NSS(nr2_);
NSS(volume_);
}

View File

@ -1,56 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef ENVELOPE_UNIT_H
#define ENVELOPE_UNIT_H
#include "sound_unit.h"
#include "../savestate.h"
#include "newstate.h"
namespace gambatte {
class EnvelopeUnit : public SoundUnit {
public:
struct VolOnOffEvent {
virtual ~VolOnOffEvent() {}
virtual void operator()(unsigned long /*cc*/) {}
};
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 loadState(SaveState::SPU::Env const &estate, unsigned nr2, unsigned long cc);
private:
static VolOnOffEvent nullEvent_;
VolOnOffEvent &volOnOffEvent_;
unsigned char nr2_;
unsigned char volume_;
public:
template<bool isReader>void SyncState(NewState *ns);
};
}
#endif

View File

@ -1,77 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "length_counter.h"
#include "master_disabler.h"
#include "psgdef.h"
#include <algorithm>
using namespace gambatte;
LengthCounter::LengthCounter(MasterDisabler &disabler, unsigned const mask)
: disableMaster_(disabler)
, lengthCounter_(0)
, lengthMask_(mask)
{
nr1Change(0, 0, 0);
}
void LengthCounter::event() {
counter_ = counter_disabled;
lengthCounter_ = 0;
disableMaster_();
}
void LengthCounter::nr1Change(unsigned const newNr1, unsigned const nr4, unsigned long const cc) {
lengthCounter_ = (~newNr1 & lengthMask_) + 1;
counter_ = nr4 & psg_nr4_lcen
? ((cc >> 13) + lengthCounter_) << 13
: 1 * counter_disabled;
}
void LengthCounter::nr4Change(unsigned const oldNr4, unsigned const newNr4, unsigned long const cc) {
if (counter_ != counter_disabled)
lengthCounter_ = (counter_ >> 13) - (cc >> 13);
unsigned dec = 0;
if (newNr4 & psg_nr4_lcen) {
dec = ~cc >> 12 & 1;
if (!(oldNr4 & psg_nr4_lcen) && lengthCounter_) {
if (!(lengthCounter_ -= dec))
disableMaster_();
}
}
if (newNr4 & psg_nr4_init && !lengthCounter_)
lengthCounter_ = lengthMask_ + 1 - dec;
counter_ = newNr4 & psg_nr4_lcen && lengthCounter_
? ((cc >> 13) + lengthCounter_) << 13
: 1 * counter_disabled;
}
void LengthCounter::loadState(SaveState::SPU::LCounter const &lstate, unsigned long const cc) {
counter_ = std::max(lstate.counter, cc);
lengthCounter_ = lstate.lengthCounter;
}
SYNCFUNC(LengthCounter)
{
NSS(counter_);
NSS(lengthCounter_);
}

View File

@ -1,49 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef LENGTH_COUNTER_H
#define LENGTH_COUNTER_H
#include "sound_unit.h"
#include "../savestate.h"
#include "newstate.h"
namespace gambatte {
class MasterDisabler;
class LengthCounter : public SoundUnit {
public:
LengthCounter(MasterDisabler &disabler, unsigned lengthMask);
virtual void event();
void nr1Change(unsigned newNr1, unsigned nr4, unsigned long cc);
void nr4Change(unsigned oldNr4, unsigned newNr4, unsigned long cc);
void loadState(SaveState::SPU::LCounter const &lstate, unsigned long cc);
private:
MasterDisabler &disableMaster_;
unsigned short lengthCounter_;
unsigned char const lengthMask_;
public:
template<bool isReader>void SyncState(NewState *ns);
};
}
#endif

View File

@ -1,36 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef MASTER_DISABLER_H
#define MASTER_DISABLER_H
namespace gambatte {
class MasterDisabler {
public:
explicit MasterDisabler(bool &master) : master_(master) {}
virtual ~MasterDisabler() {}
virtual void operator()() { master_ = false; }
private:
bool &master_;
};
}
#endif

View File

@ -1,31 +0,0 @@
#ifndef PSGDEF_H
#define PSGDEF_H
namespace gambatte {
enum {
psg_nr10_rsh = 0x07,
psg_nr10_neg = 0x08,
psg_nr10_time = 0x70
};
enum {
psg_nr2_step = 0x07,
psg_nr2_inc = 0x08,
psg_nr2_initvol = 0xF0
};
enum {
psg_nr43_r = 0x07,
psg_nr43_7biten = 0x08,
psg_nr43_s = 0xF0
};
enum {
psg_nr4_lcen = 0x40,
psg_nr4_init = 0x80
};
}
#endif

View File

@ -1,45 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef SOUND_UNIT_H
#define SOUND_UNIT_H
namespace gambatte {
class SoundUnit {
public:
enum { counter_max = 0x80000000u, counter_disabled = 0xFFFFFFFFu };
virtual ~SoundUnit() {}
virtual void event() = 0;
virtual void resetCounters(unsigned long /*oldCc*/) {
if (counter_ != counter_disabled)
counter_ -= counter_max;
}
unsigned long counter() const { return counter_; }
protected:
SoundUnit() : counter_(counter_disabled) {}
unsigned long counter_;
};
}
#endif

View File

@ -1,47 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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 {
public:
StaticOutputTester(Channel const &ch, Unit &unit) : ch_(ch), unit_(unit) {}
void operator()(unsigned long cc);
private:
Channel const &ch_;
Unit &unit_;
};
template<class Channel, class Unit>
void StaticOutputTester<Channel, Unit>::operator()(unsigned long cc) {
if (ch_.soMask_ && ch_.master_ && ch_.envelopeUnit_.getVolume())
unit_.reviveCounter(cc);
else
unit_.killCounter();
}
}
#endif

View File

@ -1,203 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "tima.h"
#include "savestate.h"
using namespace gambatte;
namespace {
unsigned char const timaClock[4] = { 10, 4, 6, 8 };
}
namespace gambatte {
Tima::Tima()
: divLastUpdate_(0)
, lastUpdate_(0)
, tmatime_(disabled_time)
, tima_(0)
, tma_(0)
, tac_(0)
{
}
void Tima::loadState(SaveState const &state, TimaInterruptRequester timaIrq) {
divLastUpdate_ = state.mem.divLastUpdate - 0x100l * state.mem.ioamhram.get()[0x104];
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];
unsigned long nextIrqEventTime = disabled_time;
if (tac_ & 4) {
nextIrqEventTime = tmatime_ != disabled_time && tmatime_ > state.cpu.cycleCounter
? tmatime_
: lastUpdate_ + ((256l - tima_) << timaClock[tac_ & 3]) + 3;
}
timaIrq.setNextIrqEventTime(nextIrqEventTime);
}
void Tima::resetCc(unsigned long const oldCc, unsigned long const newCc, TimaInterruptRequester timaIrq) {
if (tac_ & 0x04) {
updateIrq(oldCc, timaIrq);
updateTima(oldCc);
unsigned long const dec = oldCc - newCc;
lastUpdate_ -= dec;
timaIrq.setNextIrqEventTime(timaIrq.nextIrqEventTime() - dec);
if (tmatime_ != disabled_time)
tmatime_ -= dec;
}
}
void Tima::updateTima(unsigned long const cc) {
unsigned long const ticks = (cc - lastUpdate_) >> timaClock[tac_ & 3];
lastUpdate_ += ticks << timaClock[tac_ & 3];
if (cc >= tmatime_) {
if (cc >= 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 (cc >= tmatime_) {
if (cc >= tmatime_ + 4)
tmatime_ = disabled_time;
tmp = tma_;
}
}
tima_ = tmp;
}
void Tima::setTima(unsigned const data, unsigned long const cc, TimaInterruptRequester timaIrq) {
if (tac_ & 0x04) {
updateIrq(cc, timaIrq);
updateTima(cc);
if (tmatime_ - cc < 4)
tmatime_ = disabled_time;
timaIrq.setNextIrqEventTime(lastUpdate_ + ((256l - data) << timaClock[tac_ & 3]) + 3);
}
tima_ = data;
}
void Tima::setTma(unsigned const data, unsigned long const cc, TimaInterruptRequester timaIrq) {
if (tac_ & 0x04) {
updateIrq(cc, timaIrq);
updateTima(cc);
}
tma_ = data;
}
void Tima::setTac(unsigned const data, unsigned long const cc, TimaInterruptRequester timaIrq, bool agbFlag) {
if (tac_ ^ data) {
unsigned long nextIrqEventTime = timaIrq.nextIrqEventTime();
if (tac_ & 0x04) {
unsigned const inc = ~(data >> 2 & (cc - divLastUpdate_) >> (timaClock[data & 3] - 1)) & 1;
lastUpdate_ -= (inc << (timaClock[tac_ & 3] - 1)) + 3;
nextIrqEventTime -= (inc << (timaClock[tac_ & 3] - 1)) + 3;
if (cc >= nextIrqEventTime)
timaIrq.flagIrq();
updateTima(cc);
tmatime_ = disabled_time;
nextIrqEventTime = disabled_time;
}
if (data & 4) {
if (agbFlag) {
unsigned long diff = cc - divLastUpdate_;
if (((diff >> (timaClock[tac_ & 3] - 1)) & 1) == 1 && ((diff >> (timaClock[data & 3] - 1)) & 1) == 0)
tima_++;
}
lastUpdate_ = cc - ((cc - divLastUpdate_) & ((1u << timaClock[data & 3]) - 1));
nextIrqEventTime = lastUpdate_ + ((256l - tima_) << timaClock[data & 3]) + 3;
}
timaIrq.setNextIrqEventTime(nextIrqEventTime);
}
tac_ = data;
}
void Tima::divReset(unsigned long cc, TimaInterruptRequester timaIrq) {
if (tac_ & 0x04) {
unsigned long nextIrqEventTime = timaIrq.nextIrqEventTime();
lastUpdate_ -= (1u << (timaClock[tac_ & 3] - 1)) + 3;
nextIrqEventTime -= (1u << (timaClock[tac_ & 3] - 1)) + 3;
if (cc >= nextIrqEventTime)
timaIrq.flagIrq();
updateTima(cc);
lastUpdate_ = cc;
timaIrq.setNextIrqEventTime(lastUpdate_ + ((256l - tima_) << timaClock[tac_ & 3]) + 3);
}
divLastUpdate_ = cc;
}
void Tima::speedChange(TimaInterruptRequester timaIrq) {
if ((tac_ & 0x07) >= 0x05) {
lastUpdate_ -= 4;
timaIrq.setNextIrqEventTime(timaIrq.nextIrqEventTime() - 4);
}
}
unsigned Tima::tima(unsigned long cc) {
if (tac_ & 0x04)
updateTima(cc);
return tima_;
}
void Tima::doIrqEvent(TimaInterruptRequester timaIrq) {
timaIrq.flagIrq(timaIrq.nextIrqEventTime());
timaIrq.setNextIrqEventTime(timaIrq.nextIrqEventTime()
+ ((256l - tma_) << timaClock[tac_ & 3]));
}
SYNCFUNC(Tima)
{
NSS(lastUpdate_);
NSS(divLastUpdate_);
NSS(tmatime_);
NSS(tima_);
NSS(tma_);
NSS(tac_);
}
}

View File

@ -1,73 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef TIMA_H
#define TIMA_H
#include "interruptrequester.h"
namespace gambatte {
class TimaInterruptRequester {
public:
explicit TimaInterruptRequester(InterruptRequester &intreq) : intreq_(intreq) {}
void flagIrq() const { intreq_.flagIrq(4); }
void flagIrq(unsigned long cc) const { intreq_.flagIrq(4, cc); }
unsigned long nextIrqEventTime() const { return intreq_.eventTime(intevent_tima); }
void setNextIrqEventTime(unsigned long time) const { intreq_.setEventTime<intevent_tima>(time); }
private:
InterruptRequester &intreq_;
};
class Tima {
public:
Tima();
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, bool agbFlag);
void divReset(unsigned long cc, TimaInterruptRequester);
void speedChange(TimaInterruptRequester);
unsigned long divLastUpdate() const { return divLastUpdate_; }
unsigned tima(unsigned long cc);
void doIrqEvent(TimaInterruptRequester timaIrq);
private:
unsigned long divLastUpdate_;
unsigned long lastUpdate_;
unsigned long tmatime_;
unsigned char tima_;
unsigned char tma_;
unsigned char tac_;
void updateIrq(unsigned long const cc, TimaInterruptRequester timaIrq) {
while (cc >= timaIrq.nextIrqEventTime())
doIrqEvent(timaIrq);
}
void updateTima(unsigned long cc);
public:
template<bool isReader>void SyncState(NewState *ns);
};
}
#endif

View File

@ -1,862 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "video.h"
#include "savestate.h"
#include <algorithm>
#include <cstring>
using namespace gambatte;
unsigned long LCD::gbcToRgb32(const unsigned bgr15) {
unsigned long const r = bgr15 & 0x1F;
unsigned long const g = bgr15 >> 5 & 0x1F;
unsigned long const b = bgr15 >> 10 & 0x1F;
return cgbColorsRgb32_[bgr15 & 0x7FFF];
}
namespace {
// TODO: simplify cycle offsets.
long const mode1_irq_frame_cycle = 1l * lcd_vres * lcd_cycles_per_line - 2;
int const mode2_irq_line_cycle = lcd_cycles_per_line - 4;
int const mode2_irq_line_cycle_ly0 = lcd_cycles_per_line - 2;
unsigned long mode1IrqSchedule(LyCounter const& lyCounter, unsigned long cc) {
return lyCounter.nextFrameCycle(mode1_irq_frame_cycle, cc);
}
unsigned long mode2IrqSchedule(unsigned const statReg,
LyCounter const& lyCounter, unsigned long const cc) {
if (!(statReg & lcdstat_m2irqen))
return disabled_time;
unsigned long const lastM2Fc = (lcd_vres - 1l) * lcd_cycles_per_line + mode2_irq_line_cycle;
unsigned long const ly0M2Fc = (lcd_lines_per_frame - 1l) * lcd_cycles_per_line + mode2_irq_line_cycle_ly0;
return lyCounter.frameCycles(cc) - lastM2Fc < ly0M2Fc - lastM2Fc || (statReg & lcdstat_m0irqen)
? lyCounter.nextFrameCycle(ly0M2Fc, cc)
: lyCounter.nextLineCycle(mode2_irq_line_cycle, cc);
}
unsigned long m0TimeOfCurrentLine(
unsigned long nextLyTime,
unsigned long lastM0Time,
unsigned long nextM0Time) {
return nextM0Time < nextLyTime ? nextM0Time : lastM0Time;
}
bool isHdmaPeriod(LyCounter const& lyCounter,
unsigned long m0TimeOfCurrentLy, unsigned long cc) {
return lyCounter.ly() < lcd_vres
&& cc + 3 + 3 * lyCounter.isDoubleSpeed() < lyCounter.time()
&& cc >= m0TimeOfCurrentLy;
}
} // unnamed namespace.
void LCD::setDmgPalette(unsigned long palette[], const unsigned long dmgColors[], 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];
}
void LCD::setCgbPalette(unsigned *lut) {
for (int i = 0; i < 32768; i++)
cgbColorsRgb32_[i] = lut[i];
refreshPalettes();
}
LCD::LCD(unsigned char const *oamram, unsigned char const *vram,
VideoInterruptRequester memEventRequester)
: ppu_(nextM0Time_, oamram, vram)
, bgpData_()
, objpData_()
, eventTimes_(memEventRequester)
, statReg_(0)
, scanlinecallback(0)
, scanlinecallbacksl(0)
{
for (std::size_t i = 0; i < sizeof dmgColorsRgb32_ / sizeof dmgColorsRgb32_[0]; ++i)
dmgColorsRgb32_[i] = (3 - (i & 3)) * 85 * 0x010101ul;
std::memset( bgpData_, 0, sizeof bgpData_);
std::memset(objpData_, 0, sizeof objpData_);
reset(oamram, vram, false);
setVideoBuffer(0, lcd_hres);
}
void LCD::reset(unsigned char const *oamram, unsigned char const *vram, bool cgb) {
ppu_.reset(oamram, vram, cgb);
lycIrq_.setCgb(cgb);
refreshPalettes();
}
void LCD::setStatePtrs(SaveState &state) {
state.ppu.bgpData.set( bgpData_, sizeof bgpData_);
state.ppu.objpData.set(objpData_, sizeof objpData_);
ppu_.setStatePtrs(state);
}
void LCD::loadState(SaveState const &state, unsigned char const *const oamram) {
statReg_ = state.mem.ioamhram.get()[0x141];
ppu_.loadState(state, oamram);
lycIrq_.loadState(state);
mstatIrq_.loadState(state);
if (ppu_.lcdc() & lcdc_en) {
nextM0Time_.predictNextM0Time(ppu_);
lycIrq_.reschedule(ppu_.lyCounter(), ppu_.now());
eventTimes_.setm<memevent_oneshot_statirq>(state.ppu.pendingLcdstatIrq
? ppu_.now() + 1
: 1 * disabled_time);
eventTimes_.setm<memevent_oneshot_updatewy2>(
state.ppu.oldWy != state.mem.ioamhram.get()[0x14A]
? ppu_.now() + 2 - isDoubleSpeed()
: 1 * disabled_time);
eventTimes_.set<event_ly>(ppu_.lyCounter().time());
eventTimes_.setm<memevent_spritemap>(
SpriteMapper::schedule(ppu_.lyCounter(), ppu_.now()));
eventTimes_.setm<memevent_lycirq>(lycIrq_.time());
eventTimes_.setm<memevent_m1irq>(mode1IrqSchedule(ppu_.lyCounter(), ppu_.now()));
eventTimes_.setm<memevent_m2irq>(
mode2IrqSchedule(statReg_, ppu_.lyCounter(), ppu_.now()));
eventTimes_.setm<memevent_m0irq>(statReg_ & lcdstat_m0irqen
? ppu_.now() + state.ppu.nextM0Irq
: 1 * disabled_time);
eventTimes_.setm<memevent_hdma>(state.mem.hdmaTransfer
? nextM0Time_.predictedNextM0Time()
: 1 * disabled_time);
} else for (int i = 0; i < num_memevents; ++i)
eventTimes_.set(MemEvent(i), disabled_time);
refreshPalettes();
}
void LCD::refreshPalettes() {
if (isCgb() && !isCgbDmg()) {
for (int i = 0; i < max_num_palettes * num_palette_entries; ++i) {
ppu_.bgPalette()[i] = gbcToRgb32(bgpData_[2 * i] | bgpData_[2 * i + 1] * 0x100l);
ppu_.spPalette()[i] = gbcToRgb32(objpData_[2 * i] | objpData_[2 * i + 1] * 0x100l);
}
} else {
setDmgPalette(ppu_.bgPalette() , dmgColorsRgb32_ , bgpData_[0]);
setDmgPalette(ppu_.spPalette() , dmgColorsRgb32_ + 4, objpData_[0]);
setDmgPalette(ppu_.spPalette() + 4, dmgColorsRgb32_ + 8, objpData_[1]);
}
}
void LCD::copyCgbPalettesToDmg() {
for(unsigned i = 0; i < 4; i++) {
dmgColorsRgb32_[i] = gbcToRgb32(bgpData_[i * 2] | bgpData_[i * 2 + 1] << 8);
}
for(unsigned i = 0; i < 8; i++) {
dmgColorsRgb32_[i + 4] = gbcToRgb32(objpData_[i * 2] | objpData_[i * 2 + 1] << 8);
}
}
namespace {
template<typename T>
void clear(T *buf, unsigned long color, std::ptrdiff_t dpitch) {
unsigned lines = 144;
while (lines--) {
std::fill_n(buf, 160, color);
buf += dpitch;
}
}
}
void LCD::updateScreen(bool const blanklcd, unsigned long const cycleCounter) {
update(cycleCounter);
if (blanklcd && ppu_.frameBuf().fb()) {
unsigned long color = ppu_.cgb() ? gbcToRgb32(0xFFFF) : dmgColorsRgb32_[0];
clear(ppu_.frameBuf().fb(), color, ppu_.frameBuf().pitch());
}
}
void LCD::blackScreen() {
if (ppu_.frameBuf().fb()) {
clear(ppu_.frameBuf().fb(), gbcToRgb32(0x0000), ppu_.frameBuf().pitch());
}
}
void LCD::resetCc(unsigned long const oldCc, unsigned long const newCc) {
update(oldCc);
ppu_.resetCc(oldCc, newCc);
if (ppu_.lcdc() & lcdc_en) {
unsigned long const dec = oldCc - newCc;
nextM0Time_.invalidatePredictedNextM0Time();
lycIrq_.reschedule(ppu_.lyCounter(), newCc);
for (int i = 0; i < num_memevents; ++i) {
if (eventTimes_(MemEvent(i)) != disabled_time)
eventTimes_.set(MemEvent(i), eventTimes_(MemEvent(i)) - dec);
}
eventTimes_.set<event_ly>(ppu_.lyCounter().time());
}
}
void LCD::speedChange(unsigned long const cc) {
update(cc);
ppu_.speedChange();
if (ppu_.lcdc() & lcdc_en) {
nextM0Time_.predictNextM0Time(ppu_);
lycIrq_.reschedule(ppu_.lyCounter(), ppu_.now());
eventTimes_.set<event_ly>(ppu_.lyCounter().time());
eventTimes_.setm<memevent_spritemap>(SpriteMapper::schedule(ppu_.lyCounter(), ppu_.now()));
eventTimes_.setm<memevent_lycirq>(lycIrq_.time());
eventTimes_.setm<memevent_m1irq>(mode1IrqSchedule(ppu_.lyCounter(), ppu_.now()));
eventTimes_.setm<memevent_m2irq>(mode2IrqSchedule(statReg_, ppu_.lyCounter(), ppu_.now()));
if (eventTimes_(memevent_m0irq) != disabled_time) {
eventTimes_.setm<memevent_m0irq>(ppu_.predictedNextXposTime(lcd_hres + 6));
}
if (hdmaIsEnabled()) {
eventTimes_.setm<memevent_hdma>(nextM0Time_.predictedNextM0Time());
}
}
}
unsigned long LCD::m0TimeOfCurrentLine(unsigned long const cc) {
if (cc >= nextM0Time_.predictedNextM0Time()) {
update(cc);
nextM0Time_.predictNextM0Time(ppu_);
}
return ::m0TimeOfCurrentLine(ppu_.lyCounter().time(), ppu_.lastM0Time(),
nextM0Time_.predictedNextM0Time());
}
void LCD::enableHdma(unsigned long const cc) {
if (cc >= eventTimes_.nextEventTime())
update(cc);
if (::isHdmaPeriod(ppu_.lyCounter(), m0TimeOfCurrentLine(cc), cc + 4))
eventTimes_.flagHdmaReq();
eventTimes_.setm<memevent_hdma>(nextM0Time_.predictedNextM0Time());
}
void LCD::disableHdma(unsigned long const cycleCounter) {
if (cycleCounter >= eventTimes_.nextEventTime())
update(cycleCounter);
eventTimes_.setm<memevent_hdma>(disabled_time);
}
bool LCD::isHdmaPeriod(unsigned long const cc) {
if (cc >= eventTimes_.nextEventTime())
update(cc);
return ::isHdmaPeriod(ppu_.lyCounter(), m0TimeOfCurrentLine(cc), cc);
}
bool LCD::vramReadable(unsigned long const cc) {
if (cc >= eventTimes_.nextEventTime())
update(cc);
return !(ppu_.lcdc() & lcdc_en)
|| ppu_.lyCounter().ly() >= lcd_vres
|| ppu_.inactivePeriodAfterDisplayEnable(cc + 1 - ppu_.cgb() + isDoubleSpeed())
|| ppu_.lyCounter().lineCycles(cc) + isDoubleSpeed() < 76u + 3 * ppu_.cgb()
|| cc + 2 >= m0TimeOfCurrentLine(cc);
}
bool LCD::vramExactlyReadable(unsigned long const cc) {
if (vramHasBeenExactlyRead) {
return false;
}
if (cc + 2 + isDoubleSpeed() == m0TimeOfCurrentLine(cc)) {
vramHasBeenExactlyRead = true;
}
return cc + 2 + isDoubleSpeed() == m0TimeOfCurrentLine(cc);
}
bool LCD::vramWritable(unsigned long const cc) {
if (cc >= eventTimes_.nextEventTime())
update(cc);
return !(ppu_.lcdc() & lcdc_en)
|| ppu_.lyCounter().ly() >= lcd_vres
|| ppu_.inactivePeriodAfterDisplayEnable(cc + 1 - ppu_.cgb() + isDoubleSpeed())
|| ppu_.lyCounter().lineCycles(cc) + isDoubleSpeed() < 79
|| cc + 2 >= m0TimeOfCurrentLine(cc);
}
bool LCD::cgbpAccessible(unsigned long const cc) {
if (cc >= eventTimes_.nextEventTime())
update(cc);
return !(ppu_.lcdc() & lcdc_en)
|| ppu_.lyCounter().ly() >= lcd_vres
|| ppu_.inactivePeriodAfterDisplayEnable(cc)
|| ppu_.lyCounter().lineCycles(cc) + isDoubleSpeed() < 80
|| cc >= m0TimeOfCurrentLine(cc) + 2;
}
void LCD::doCgbColorChange(unsigned char *pdata,
unsigned long *palette, unsigned index, unsigned data) {
pdata[index] = data;
index >>= 1;
palette[index] = gbcToRgb32(pdata[index * 2] | pdata[index * 2 + 1] << 8);
}
void LCD::doCgbBgColorChange(unsigned index, unsigned data, unsigned long cc) {
if (cgbpAccessible(cc)) {
update(cc);
doCgbColorChange(bgpData_, ppu_.bgPalette(), index, data);
}
}
void LCD::doCgbSpColorChange(unsigned index, unsigned data, unsigned long cc) {
if (cgbpAccessible(cc)) {
update(cc);
doCgbColorChange(objpData_, ppu_.spPalette(), index, data);
}
}
bool LCD::oamReadable(unsigned long const cc) {
if (!(ppu_.lcdc() & lcdc_en) || ppu_.inactivePeriodAfterDisplayEnable(cc + 4))
return true;
if (cc >= eventTimes_.nextEventTime())
update(cc);
if (ppu_.lyCounter().lineCycles(cc) + 4 - isDoubleSpeed() >= lcd_cycles_per_line)
return ppu_.lyCounter().ly() >= lcd_vres - 1 && ppu_.lyCounter().ly() < lcd_lines_per_frame - 1;
return ppu_.lyCounter().ly() >= lcd_vres || cc + 2 >= m0TimeOfCurrentLine(cc);
}
bool LCD::oamWritable(unsigned long const cc) {
if (!(ppu_.lcdc() & lcdc_en) || ppu_.inactivePeriodAfterDisplayEnable(cc + 4 + isDoubleSpeed()))
return true;
if (cc >= eventTimes_.nextEventTime())
update(cc);
if (ppu_.lyCounter().lineCycles(cc) + 3 + ppu_.cgb() >= lcd_cycles_per_line)
return ppu_.lyCounter().ly() >= lcd_vres - 1 && ppu_.lyCounter().ly() < lcd_lines_per_frame - 1;
return ppu_.lyCounter().ly() >= lcd_vres || cc + 2 >= m0TimeOfCurrentLine(cc)
|| (ppu_.lyCounter().lineCycles(cc) == 76 && !ppu_.cgb());
}
void LCD::mode3CyclesChange() {
nextM0Time_.invalidatePredictedNextM0Time();
if (eventTimes_(memevent_m0irq) != disabled_time
&& eventTimes_(memevent_m0irq) > ppu_.now()) {
unsigned long t = ppu_.predictedNextXposTime(lcd_hres + 6);
eventTimes_.setm<memevent_m0irq>(t);
}
if (eventTimes_(memevent_hdma) != disabled_time
&& eventTimes_(memevent_hdma) > ppu_.lastM0Time()) {
nextM0Time_.predictNextM0Time(ppu_);
eventTimes_.setm<memevent_hdma>(nextM0Time_.predictedNextM0Time());
}
}
void LCD::wxChange(unsigned newValue, unsigned long cycleCounter) {
update(cycleCounter + 1 + ppu_.cgb());
ppu_.setWx(newValue);
mode3CyclesChange();
}
void LCD::wyChange(unsigned const newValue, unsigned long const cc) {
update(cc + 1 + ppu_.cgb());
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 for convenience (is this really simpler?).
if (ppu_.cgb() && (ppu_.lcdc() & lcdc_en)) {
eventTimes_.setm<memevent_oneshot_updatewy2>(cc + 6 - isDoubleSpeed());
}
else {
update(cc + 2);
ppu_.updateWy2();
mode3CyclesChange();
}
}
void LCD::scxChange(unsigned newScx, unsigned long cycleCounter) {
update(cycleCounter + 2 * ppu_.cgb());
ppu_.setScx(newScx);
mode3CyclesChange();
}
void LCD::scyChange(unsigned newValue, unsigned long cycleCounter) {
update(cycleCounter + 2 * ppu_.cgb());
ppu_.setScy(newValue);
}
void LCD::oamChange(unsigned long cc) {
if (ppu_.lcdc() & lcdc_en) {
update(cc);
ppu_.oamChange(cc);
eventTimes_.setm<memevent_spritemap>(SpriteMapper::schedule(ppu_.lyCounter(), cc));
}
}
void LCD::oamChange(unsigned char const *oamram, unsigned long cc) {
update(cc);
ppu_.oamChange(oamram, cc);
if (ppu_.lcdc() & lcdc_en)
eventTimes_.setm<memevent_spritemap>(SpriteMapper::schedule(ppu_.lyCounter(), cc));
}
void LCD::lcdcChange(unsigned const data, unsigned long const cc) {
unsigned const oldLcdc = ppu_.lcdc();
if ((oldLcdc ^ data) & lcdc_en) {
update(cc);
ppu_.setLcdc(data, cc);
if (data & lcdc_en) {
lycIrq_.lcdReset();
mstatIrq_.lcdReset(lycIrq_.lycReg());
nextM0Time_.predictNextM0Time(ppu_);
lycIrq_.reschedule(ppu_.lyCounter(), cc);
eventTimes_.set<event_ly>(ppu_.lyCounter().time());
eventTimes_.setm<memevent_spritemap>(
SpriteMapper::schedule(ppu_.lyCounter(), cc));
eventTimes_.setm<memevent_lycirq>(lycIrq_.time());
eventTimes_.setm<memevent_m1irq>(mode1IrqSchedule(ppu_.lyCounter(), cc));
eventTimes_.setm<memevent_m2irq>(
mode2IrqSchedule(statReg_, ppu_.lyCounter(), cc));
if (statReg_ & lcdstat_m0irqen) {
eventTimes_.setm<memevent_m0irq>(ppu_.predictedNextXposTime(lcd_hres + 6));
}
if (hdmaIsEnabled()) {
eventTimes_.setm<memevent_hdma>(nextM0Time_.predictedNextM0Time());
}
}
else for (int i = 0; i < num_memevents; ++i)
eventTimes_.set(MemEvent(i), disabled_time);
}
else if (data & lcdc_en) {
if (ppu_.cgb()) {
update(cc + 1);
ppu_.setLcdc((oldLcdc & ~(1u * lcdc_tdsel)) | (data & lcdc_tdsel), cc + 1);
update(cc + 2);
ppu_.setLcdc(data, cc + 2);
if ((oldLcdc ^ data) & lcdc_obj2x) {
unsigned long t = SpriteMapper::schedule(ppu_.lyCounter(), cc + 2);
eventTimes_.setm<memevent_spritemap>(t);
}
if ((oldLcdc ^ data) & lcdc_we)
mode3CyclesChange();
}
else {
update(cc);
ppu_.setLcdc((oldLcdc & lcdc_obj2x) | (data & ~(1u * lcdc_obj2x)), cc);
if ((oldLcdc ^ data) & lcdc_obj2x) {
update(cc + 2);
ppu_.setLcdc(data, cc + 2);
unsigned long t = SpriteMapper::schedule(ppu_.lyCounter(), cc + 2);
eventTimes_.setm<memevent_spritemap>(t);
}
if ((oldLcdc ^ data) & (lcdc_we | lcdc_objen))
mode3CyclesChange();
}
}
else {
update(cc);
ppu_.setLcdc(data, cc);
}
}
namespace {
struct LyCnt {
unsigned ly; int timeToNextLy;
LyCnt(unsigned ly, int timeToNextLy) : ly(ly), timeToNextLy(timeToNextLy) {}
};
LyCnt const getLycCmpLy(LyCounter const &lyCounter, unsigned long cc) {
unsigned ly = lyCounter.ly();
int timeToNextLy = lyCounter.time() - cc;
if (ly == lcd_lines_per_frame - 1) {
int const lineTime = lyCounter.lineTime();
if ((timeToNextLy -= (lineTime - 6 - 6 * lyCounter.isDoubleSpeed())) <= 0)
ly = 0, timeToNextLy += lineTime;
}
else if ((timeToNextLy -= (2 + 2 * lyCounter.isDoubleSpeed())) <= 0)
++ly, timeToNextLy += lyCounter.lineTime();
return LyCnt(ly, timeToNextLy);
}
bool statChangeTriggersM2IrqCgb(unsigned const old,
unsigned const data, int const ly, int const timeToNextLy, bool const ds) {
if ((old & lcdstat_m2irqen)
|| (data & (lcdstat_m2irqen | lcdstat_m0irqen)) != lcdstat_m2irqen) {
return false;
}
if (ly < lcd_vres - 1)
return timeToNextLy <= (lcd_cycles_per_line - mode2_irq_line_cycle) * (1 + ds) && timeToNextLy > 2;
if (ly == lcd_vres - 1)
return timeToNextLy <= (lcd_cycles_per_line - mode2_irq_line_cycle) * (1 + ds) && timeToNextLy > 4 + 2 * ds;
if (ly == lcd_lines_per_frame - 1)
return timeToNextLy <= (lcd_cycles_per_line - mode2_irq_line_cycle_ly0) * (1 + ds) && timeToNextLy > 2;
return false;
}
unsigned incLy(unsigned ly) { return ly == lcd_lines_per_frame - 1 ? 0 : ly + 1; }
} // unnamed namespace.
inline bool LCD::statChangeTriggersStatIrqDmg(unsigned const old, unsigned long const cc) {
LyCnt const lycCmp = getLycCmpLy(ppu_.lyCounter(), cc);
if (ppu_.lyCounter().ly() < lcd_vres) {
int const m0_cycles_upper_bound = lcd_cycles_per_line - 80 - 160;
unsigned long m0IrqTime = eventTimes_(memevent_m0irq);
if (m0IrqTime == disabled_time && ppu_.lyCounter().time() - cc < m0_cycles_upper_bound) {
update(cc);
m0IrqTime = ppu_.predictedNextXposTime(lcd_hres + 6);
}
if (m0IrqTime == disabled_time || m0IrqTime < ppu_.lyCounter().time())
return lycCmp.ly == lycIrq_.lycReg() && !(old & lcdstat_lycirqen);
return !(old & lcdstat_m0irqen)
&& !(lycCmp.ly == lycIrq_.lycReg() && (old & lcdstat_lycirqen));
}
return !(old & lcdstat_m1irqen)
&& !(lycCmp.ly == lycIrq_.lycReg() && (old & lcdstat_lycirqen));
}
inline bool LCD::statChangeTriggersM0LycOrM1StatIrqCgb(
unsigned const old, unsigned const data, bool const lycperiod,
unsigned long const cc) {
int const ly = ppu_.lyCounter().ly();
int const timeToNextLy = ppu_.lyCounter().time() - cc;
bool const ds = isDoubleSpeed();
int const m1_irq_lc_inv = lcd_cycles_per_line - mode1_irq_frame_cycle % lcd_cycles_per_line;
if (ly < lcd_vres - 1 || (ly == lcd_vres - 1 && timeToNextLy > m1_irq_lc_inv* (1 + ds))) {
if (eventTimes_(memevent_m0irq) < ppu_.lyCounter().time()
|| timeToNextLy <= (ly < lcd_vres - 1 ? 4 + 4 * ds : 4 + 2 * ds)) {
return lycperiod && (data & lcdstat_lycirqen);
}
if (old & lcdstat_m0irqen)
return false;
return (data & lcdstat_m0irqen)
|| (lycperiod && (data & lcdstat_lycirqen));
}
if (old & lcdstat_m1irqen && (ly < lcd_lines_per_frame - 1 || timeToNextLy > 3 + 3 * ds))
return false;
return ((data & lcdstat_m1irqen)
&& (ly < lcd_lines_per_frame - 1 || timeToNextLy > 4 + 2 * ds))
|| (lycperiod && (data & lcdstat_lycirqen));
}
inline bool LCD::statChangeTriggersStatIrqCgb(
unsigned const old, unsigned const data, unsigned long const cc) {
if (!(data & ~old & (lcdstat_lycirqen
| lcdstat_m2irqen
| lcdstat_m1irqen
| lcdstat_m0irqen))) {
return false;
}
int const ly = ppu_.lyCounter().ly();
int const timeToNextLy = ppu_.lyCounter().time() - cc;
LyCnt const lycCmp = getLycCmpLy(ppu_.lyCounter(), cc);
bool const lycperiod = lycCmp.ly == lycIrq_.lycReg()
&& lycCmp.timeToNextLy > 2;
if (lycperiod && (old & lcdstat_lycirqen))
return false;
return statChangeTriggersM0LycOrM1StatIrqCgb(old, data, lycperiod, cc)
|| statChangeTriggersM2IrqCgb(old, data, ly, timeToNextLy, isDoubleSpeed());
}
inline bool LCD::statChangeTriggersStatIrq(unsigned old, unsigned data, unsigned long cc) {
return ppu_.cgb()
? statChangeTriggersStatIrqCgb(old, data, cc)
: statChangeTriggersStatIrqDmg(old, cc);
}
void LCD::lcdstatChange(unsigned const data, unsigned long const cc) {
if (cc >= eventTimes_.nextEventTime())
update(cc);
unsigned const old = statReg_;
statReg_ = data;
lycIrq_.statRegChange(data, ppu_.lyCounter(), cc);
if (ppu_.lcdc() & lcdc_en) {
if ((data & lcdstat_m0irqen) && eventTimes_(memevent_m0irq) == disabled_time) {
update(cc);
eventTimes_.setm<memevent_m0irq>(ppu_.predictedNextXposTime(lcd_hres + 6));
}
eventTimes_.setm<memevent_m2irq>(mode2IrqSchedule(data, ppu_.lyCounter(), cc));
eventTimes_.setm<memevent_lycirq>(lycIrq_.time());
if (statChangeTriggersStatIrq(old, data, cc))
eventTimes_.flagIrq(2);
}
mstatIrq_.statRegChange(data, eventTimes_(memevent_m0irq), eventTimes_(memevent_m1irq),
eventTimes_(memevent_m2irq), cc, ppu_.cgb());
}
inline bool LCD::lycRegChangeStatTriggerBlockedByM0OrM1Irq(unsigned data, unsigned long cc) {
int const timeToNextLy = ppu_.lyCounter().time() - cc;
if (ppu_.lyCounter().ly() < lcd_vres) {
return (statReg_ & lcdstat_m0irqen)
&& eventTimes_(memevent_m0irq) > ppu_.lyCounter().time()
&& data == ppu_.lyCounter().ly();
}
return (statReg_ & lcdstat_m1irqen)
&& !(ppu_.lyCounter().ly() == lcd_lines_per_frame - 1
&& timeToNextLy <= 2 + 2 * isDoubleSpeed() + 2 * ppu_.cgb());
}
bool LCD::lycRegChangeTriggersStatIrq(
unsigned const old, unsigned const data, unsigned long const cc) {
if (!(statReg_ & lcdstat_lycirqen) || data >= lcd_lines_per_frame
|| lycRegChangeStatTriggerBlockedByM0OrM1Irq(data, cc)) {
return false;
}
LyCnt lycCmp = getLycCmpLy(ppu_.lyCounter(), cc);
if (lycCmp.timeToNextLy <= 4 + 4 * isDoubleSpeed() + 2 * ppu_.cgb()) {
if (old == lycCmp.ly && lycCmp.timeToNextLy > 2 * ppu_.cgb())
return false; // simultaneous ly/lyc inc. lyc flag never goes low -> no trigger.
lycCmp.ly = incLy(lycCmp.ly);
}
return data == lycCmp.ly;
}
void LCD::lycRegChange(unsigned const data, unsigned long const cc) {
unsigned const old = lycIrq_.lycReg();
if (data == old)
return;
if (cc >= eventTimes_.nextEventTime())
update(cc);
lycIrq_.lycRegChange(data, ppu_.lyCounter(), cc);
mstatIrq_.lycRegChange(data, eventTimes_(memevent_m0irq),
eventTimes_(memevent_m2irq), cc, isDoubleSpeed(), ppu_.cgb());
if (ppu_.lcdc() & lcdc_en) {
eventTimes_.setm<memevent_lycirq>(lycIrq_.time());
if (lycRegChangeTriggersStatIrq(old, data, cc)) {
if (ppu_.cgb() && !isDoubleSpeed()) {
eventTimes_.setm<memevent_oneshot_statirq>(cc + 5);
}
else
eventTimes_.flagIrq(2);
}
}
}
unsigned LCD::getStat(unsigned const lycReg, unsigned long const cc) {
unsigned stat = 0;
if (ppu_.lcdc() & lcdc_en) {
if (cc >= eventTimes_.nextEventTime())
update(cc);
unsigned const ly = ppu_.lyCounter().ly();
int const timeToNextLy = ppu_.lyCounter().time() - cc;
int const lineCycles = lcd_cycles_per_line - (timeToNextLy >> isDoubleSpeed());
long const frameCycles = 1l * ly * lcd_cycles_per_line + lineCycles;
if (frameCycles >= lcd_vres * lcd_cycles_per_line - 3 && frameCycles < lcd_cycles_per_frame - 3) {
if (frameCycles >= lcd_vres * lcd_cycles_per_line - 2
&& frameCycles < lcd_cycles_per_frame - 4 + isDoubleSpeed()) {
stat = 1;
}
}
else if (lineCycles < 77 || lineCycles >= lcd_cycles_per_line - 3) {
if (!ppu_.inactivePeriodAfterDisplayEnable(cc + 1))
stat = 2;
}
else if (cc + 2 < m0TimeOfCurrentLine(cc)) {
if (!ppu_.inactivePeriodAfterDisplayEnable(cc + 1))
stat = 3;
}
LyCnt const lycCmp = getLycCmpLy(ppu_.lyCounter(), cc);
if (lycReg == lycCmp.ly && lycCmp.timeToNextLy > 2)
stat |= lcdstat_lycflag;
}
return stat;
}
inline void LCD::doMode2IrqEvent() {
unsigned const ly = eventTimes_(event_ly) - eventTimes_(memevent_m2irq) < 16
? incLy(ppu_.lyCounter().ly())
: ppu_.lyCounter().ly();
if (mstatIrq_.doM2Event(ly, statReg_, lycIrq_.lycReg()))
eventTimes_.flagIrq(2, eventTimes_(memevent_m2irq));
bool const ds = isDoubleSpeed();
unsigned long next = lcd_cycles_per_frame;
if (!(statReg_ & lcdstat_m0irqen)) {
next = lcd_cycles_per_line;
if (ly == 0) {
next -= mode2_irq_line_cycle_ly0 - mode2_irq_line_cycle;
}
else if (ly == lcd_vres) {
next += lcd_cycles_per_line * (lcd_lines_per_frame - lcd_vres - 1)
+ mode2_irq_line_cycle_ly0 - mode2_irq_line_cycle;
}
}
eventTimes_.setm<memevent_m2irq>(eventTimes_(memevent_m2irq) + (next << ds));
}
inline void LCD::event() {
switch (eventTimes_.nextEvent()) {
case event_mem:
switch (eventTimes_.nextMemEvent()) {
case memevent_m1irq:
eventTimes_.flagIrq(mstatIrq_.doM1Event(statReg_) ? 3 : 1,
eventTimes_(memevent_m1irq));
eventTimes_.setm<memevent_m1irq>(eventTimes_(memevent_m1irq)
+ (lcd_cycles_per_frame << isDoubleSpeed()));
break;
case memevent_lycirq:
if (lycIrq_.doEvent(ppu_.lyCounter()))
eventTimes_.flagIrq(2, eventTimes_(memevent_lycirq));
eventTimes_.setm<memevent_lycirq>(lycIrq_.time());
break;
case memevent_spritemap:
eventTimes_.setm<memevent_spritemap>(
ppu_.doSpriteMapEvent(eventTimes_(memevent_spritemap)));
mode3CyclesChange();
break;
case memevent_hdma:
eventTimes_.flagHdmaReq();
nextM0Time_.predictNextM0Time(ppu_);
eventTimes_.setm<memevent_hdma>(nextM0Time_.predictedNextM0Time());
break;
case memevent_m2irq:
doMode2IrqEvent();
break;
case memevent_m0irq:
if (mstatIrq_.doM0Event(ppu_.lyCounter().ly(), statReg_, lycIrq_.lycReg()))
eventTimes_.flagIrq(2, eventTimes_(memevent_m0irq));
eventTimes_.setm<memevent_m0irq>(statReg_ & lcdstat_m0irqen
? ppu_.predictedNextXposTime(lcd_hres + 6)
: 1 * disabled_time);
break;
case memevent_oneshot_statirq:
eventTimes_.flagIrq(2);
eventTimes_.setm<memevent_oneshot_statirq>(disabled_time);
break;
case memevent_oneshot_updatewy2:
ppu_.updateWy2();
mode3CyclesChange();
eventTimes_.setm<memevent_oneshot_updatewy2>(disabled_time);
break;
}
break;
case event_ly:
ppu_.doLyCountEvent();
eventTimes_.set<event_ly>(ppu_.lyCounter().time());
break;
}
}
void LCD::update(unsigned long const cycleCounter) {
if (!(ppu_.lcdc() & lcdc_en))
return;
while (cycleCounter >= eventTimes_.nextEventTime()) {
ppu_.update(eventTimes_.nextEventTime());
event();
}
ppu_.update(cycleCounter);
}
void LCD::setVideoBuffer(uint_least32_t *videoBuf, std::ptrdiff_t pitch) {
ppu_.setFrameBuf(videoBuf, pitch);
}
void LCD::setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned long rgb32) {
if (palNum > 2 || colorNum > 3)
return;
dmgColorsRgb32_[palNum * 4 + colorNum] = rgb32;
refreshPalettes();
}
// don't need to save or load rgb32 color data
SYNCFUNC(LCD)
{
SSS(ppu_);
NSS(dmgColorsRgb32_);
NSS(cgbColorsRgb32_);
NSS(bgpData_);
NSS(objpData_);
SSS(eventTimes_);
SSS(mstatIrq_);
SSS(lycIrq_);
SSS(nextM0Time_);
NSS(statReg_);
NSS(vramHasBeenExactlyRead);
}

View File

@ -1,266 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef VIDEO_H
#define VIDEO_H
#include "interruptrequester.h"
#include "minkeeper.h"
#include "video/lyc_irq.h"
#include "video/mstat_irq.h"
#include "video/next_m0_time.h"
#include "video/ppu.h"
#include "newstate.h"
namespace gambatte {
class VideoInterruptRequester {
public:
explicit VideoInterruptRequester(InterruptRequester &intreq)
: intreq_(intreq)
{
}
void flagHdmaReq() const { if (!intreq_.halted()) gambatte::flagHdmaReq(intreq_); }
void flagIrq(unsigned bit) const { intreq_.flagIrq(bit); }
void flagIrq(unsigned bit, unsigned long cc) const { intreq_.flagIrq(bit, cc); }
void setNextEventTime(unsigned long time) const { intreq_.setEventTime<intevent_video>(time); }
private:
InterruptRequester &intreq_;
};
class LCD {
public:
LCD(unsigned char const *oamram, unsigned char const *vram,
VideoInterruptRequester memEventRequester);
void reset(unsigned char const *oamram, unsigned char const *vram, bool cgb);
void setCgbDmg(bool enabled) { ppu_.setCgbDmg(enabled); }
void setStatePtrs(SaveState &state);
void loadState(SaveState const &state, unsigned char const *oamram);
void setDmgPaletteColor(unsigned palNum, unsigned colorNum, unsigned long rgb32);
void setCgbPalette(unsigned *lut);
void setVideoBuffer(uint_least32_t *videoBuf, std::ptrdiff_t pitch);
void setLayers(unsigned mask) { ppu_.setLayers(mask); }
void copyCgbPalettesToDmg();
int debugGetLY() const { return ppu_.lyCounter().ly(); }
void dmgBgPaletteChange(unsigned data, unsigned long cycleCounter) {
update(cycleCounter);
bgpData_[0] = data;
setDmgPalette(ppu_.bgPalette(), dmgColorsRgb32_, data);
}
void dmgSpPalette1Change(unsigned data, unsigned long cycleCounter) {
update(cycleCounter);
objpData_[0] = data;
setDmgPalette(ppu_.spPalette(), dmgColorsRgb32_ + 4, data);
}
void dmgSpPalette2Change(unsigned data, unsigned long cycleCounter) {
update(cycleCounter);
objpData_[1] = data;
setDmgPalette(ppu_.spPalette() + 4, dmgColorsRgb32_ + 8, data);
}
void cgbBgColorChange(unsigned index, unsigned data, unsigned long cycleCounter) {
if (bgpData_[index] != data)
doCgbBgColorChange(index, data, cycleCounter);
}
void cgbSpColorChange(unsigned index, unsigned data, unsigned long cycleCounter) {
if (objpData_[index] != data)
doCgbSpColorChange(index, data, cycleCounter);
}
unsigned cgbBgColorRead(unsigned index, unsigned long cycleCounter) {
return ppu_.cgb() && cgbpAccessible(cycleCounter) ? bgpData_[index] : 0xFF;
}
unsigned cgbSpColorRead(unsigned index, unsigned long cycleCounter) {
return ppu_.cgb() && cgbpAccessible(cycleCounter) ? objpData_[index] : 0xFF;
}
void updateScreen(bool blanklcd, unsigned long cc);
void blackScreen();
void resetCc(unsigned long oldCC, unsigned long newCc);
void speedChange(unsigned long cycleCounter);
bool vramReadable(unsigned long cycleCounter);
bool vramExactlyReadable(unsigned long cycleCounter);
bool vramWritable(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(unsigned long cycleCounter) { update(cycleCounter); }
unsigned getStat(unsigned lycReg, unsigned long cycleCounter);
unsigned getLyReg(unsigned long const cc) {
unsigned lyReg = 0;
if (ppu_.lcdc() & lcdc_en) {
if (cc >= ppu_.lyCounter().time())
update(cc);
lyReg = ppu_.lyCounter().ly();
if (lyReg == lcd_lines_per_frame - 1) {
if (ppu_.lyCounter().time() - cc <= 2 * lcd_cycles_per_line - 2)
lyReg = 0;
}
else if (ppu_.lyCounter().time() - cc <= 10
&& ppu_.lyCounter().time() - cc <= 6u + 4 * isDoubleSpeed()) {
lyReg = ppu_.lyCounter().time() - cc == 6u + 4 * isDoubleSpeed()
? lyReg & (lyReg + 1)
: lyReg + 1;
}
}
return lyReg;
}
unsigned long nextMode1IrqTime() const { return eventTimes_(memevent_m1irq); }
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 isHdmaPeriod(unsigned long cycleCounter);
bool hdmaIsEnabled() const { return eventTimes_(memevent_hdma) != disabled_time; }
void update(unsigned long cycleCounter);
bool isCgb() const { return ppu_.cgb(); }
bool isCgbDmg() const { return ppu_.cgbDmg(); }
bool isDoubleSpeed() const { return ppu_.lyCounter().isDoubleSpeed(); }
unsigned long *bgPalette() { return ppu_.bgPalette(); }
unsigned long *spPalette() { return ppu_.spPalette(); }
void setScanlineCallback(void (*callback)(), int sl) { scanlinecallback = callback; scanlinecallbacksl = sl; }
private:
enum Event { event_mem,
event_ly, event_last = event_ly };
enum MemEvent { memevent_oneshot_statirq,
memevent_oneshot_updatewy2,
memevent_m1irq,
memevent_lycirq,
memevent_spritemap,
memevent_hdma,
memevent_m2irq,
memevent_m0irq, memevent_last = memevent_m0irq };
enum { num_events = event_last + 1 };
enum { num_memevents = memevent_last + 1 };
class EventTimes {
public:
explicit EventTimes(VideoInterruptRequester memEventRequester)
: eventMin_(disabled_time)
, memEventMin_(disabled_time)
, memEventRequester_(memEventRequester)
{
}
Event nextEvent() const { return static_cast<Event>(eventMin_.min()); }
unsigned long nextEventTime() const { return eventMin_.minValue(); }
unsigned long operator()(Event e) const { return eventMin_.value(e); }
template<Event e> void set(unsigned long time) { eventMin_.setValue<e>(time); }
void set(Event e, 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()(MemEvent e) const { return memEventMin_.value(e); }
template<MemEvent e>
void setm(unsigned long time) { memEventMin_.setValue<e>(time); setMemEvent(); }
void set(MemEvent e, unsigned long time) { memEventMin_.setValue(e, time); setMemEvent(); }
void flagIrq(unsigned bit) { memEventRequester_.flagIrq(bit); }
void flagIrq(unsigned bit, unsigned long cc) { memEventRequester_.flagIrq(bit, cc); }
void flagHdmaReq() { memEventRequester_.flagHdmaReq(); }
private:
MinKeeper<num_events> eventMin_;
MinKeeper<num_memevents> memEventMin_;
VideoInterruptRequester memEventRequester_;
void setMemEvent() {
unsigned long nmet = nextMemEventTime();
eventMin_.setValue<event_mem>(nmet);
memEventRequester_.setNextEventTime(nmet);
}
public:
template<bool isReader>
void SyncState(NewState *ns)
{
SSS(eventMin_);
SSS(memEventMin_);
}
};
PPU ppu_;
unsigned long dmgColorsRgb32_[3 * 4];
unsigned long cgbColorsRgb32_[32768];
unsigned char bgpData_[2 * max_num_palettes * num_palette_entries];
unsigned char objpData_[2 * max_num_palettes * num_palette_entries];
EventTimes eventTimes_;
MStatIrqEvent mstatIrq_;
LycIrq lycIrq_;
NextM0Time nextM0Time_;
unsigned char statReg_;
bool vramHasBeenExactlyRead = false;
static void setDmgPalette(unsigned long palette[],
unsigned long const dmgColors[],
unsigned data);
unsigned long gbcToRgb32(const unsigned bgr15);
void doCgbColorChange(unsigned char *const pdata, unsigned long *const palette, unsigned index, const unsigned data);
void refreshPalettes();
void setDBuffer();
void doMode2IrqEvent();
void event();
unsigned long m0TimeOfCurrentLine(unsigned long cc);
bool cgbpAccessible(unsigned long cycleCounter);
bool lycRegChangeStatTriggerBlockedByM0OrM1Irq(unsigned data, unsigned long cc);
bool lycRegChangeTriggersStatIrq(unsigned old, unsigned data, unsigned long cc);
bool statChangeTriggersM0LycOrM1StatIrqCgb(unsigned old, unsigned data, bool lycperiod, unsigned long cc);
bool statChangeTriggersStatIrqCgb(unsigned old, unsigned data, unsigned long cc);
bool statChangeTriggersStatIrqDmg(unsigned old, unsigned long cc);
bool statChangeTriggersStatIrq(unsigned old, unsigned data, unsigned long cc);
void mode3CyclesChange();
void doCgbBgColorChange(unsigned index, unsigned data, unsigned long cycleCounter);
void doCgbSpColorChange(unsigned index, unsigned data, unsigned long cycleCounter);
void (*scanlinecallback)();
int scanlinecallbacksl;
public:
template<bool isReader>void SyncState(NewState *ns);
};
}
#endif

View File

@ -1,41 +0,0 @@
#ifndef LCDDEF_H
#define LCDDEF_H
namespace gambatte {
enum {
lcdc_bgen = 0x01,
lcdc_objen = 0x02,
lcdc_obj2x = 0x04,
lcdc_bgtmsel = 0x08,
lcdc_tdsel = 0x10,
lcdc_we = 0x20,
lcdc_wtmsel = 0x40,
lcdc_en = 0x80
};
enum {
lcdstat_lycflag = 0x04,
lcdstat_m0irqen = 0x08,
lcdstat_m1irqen = 0x10,
lcdstat_m2irqen = 0x20,
lcdstat_lycirqen = 0x40
};
enum {
lcd_hres = 160,
lcd_vres = 144,
lcd_lines_per_frame = 154,
lcd_max_num_sprites_per_line = 10,
lcd_num_oam_entries = 40,
lcd_cycles_per_line = 456,
lcd_force_signed_enum1 = -1
};
enum {
lcd_cycles_per_frame = 1l * lcd_lines_per_frame * lcd_cycles_per_line,
lcd_force_signed_enum2 = -1
};
}
#endif

View File

@ -1,77 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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_ == lcd_lines_per_frame)
ly_ = 0;
time_ = time_ + lineTime_;
}
unsigned long LyCounter::nextLineCycle(unsigned const lineCycle, unsigned long const cc) const {
unsigned long tmp = time_ + (lineCycle << ds_);
if (tmp - cc > lineTime_)
tmp -= lineTime_;
return tmp;
}
unsigned long LyCounter::nextFrameCycle(unsigned long const frameCycle, unsigned long const cc) const {
unsigned long tmp = time_ + (((lcd_lines_per_frame - 1l - ly()) * lcd_cycles_per_line + frameCycle) << ds_);
if (tmp - cc > 1ul * lcd_cycles_per_frame << ds_)
tmp -= 1ul * lcd_cycles_per_frame << ds_;
return tmp;
}
void LyCounter::reset(unsigned long videoCycles, unsigned long lastUpdate) {
ly_ = videoCycles / lcd_cycles_per_line;
time_ = lastUpdate + ((lcd_cycles_per_line
- (videoCycles - 1l * ly_ * lcd_cycles_per_line)) << isDoubleSpeed());
}
void LyCounter::setDoubleSpeed(bool ds) {
ds_ = ds;
lineTime_ = lcd_cycles_per_line << ds;
}
SYNCFUNC(LyCounter)
{
NSS(time_);
NSS(lineTime_);
NSS(ly_);
NSS(ds_);
}
}

View File

@ -1,63 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef LY_COUNTER_H
#define LY_COUNTER_H
#include "newstate.h"
#include "lcddef.h"
namespace gambatte {
struct SaveState;
class LyCounter {
public:
LyCounter();
void doEvent();
bool isDoubleSpeed() const { return ds_; }
unsigned long frameCycles(unsigned long cc) const {
return 1l * ly_ * lcd_cycles_per_line + lineCycles(cc);
}
unsigned lineCycles(unsigned long cc) const {
return lcd_cycles_per_line - ((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);
unsigned long time() const { return time_; }
private:
unsigned long time_;
unsigned short lineTime_;
unsigned char ly_;
bool ds_;
public:
template<bool isReader>void SyncState(NewState *ns);
};
}
#endif

View File

@ -1,118 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "lyc_irq.h"
#include "counterdef.h"
#include "lcddef.h"
#include "ly_counter.h"
#include "savestate.h"
#include <algorithm>
using namespace gambatte;
namespace {
unsigned long schedule(unsigned statReg,
unsigned lycReg, LyCounter const& lyCounter, unsigned long cc) {
return (statReg & lcdstat_lycirqen) && lycReg < lcd_lines_per_frame
? lyCounter.nextFrameCycle(lycReg
? 1l * lycReg * lcd_cycles_per_line - 2
: (lcd_lines_per_frame - 1l) * lcd_cycles_per_line + 6, cc)
: 1 * disabled_time;
}
bool lycIrqBlockedByM2OrM1StatIrq(unsigned ly, unsigned statreg) {
return ly <= lcd_vres && ly > 0
? statreg & lcdstat_m2irqen
: statreg & lcdstat_m1irqen;
}
}
LycIrq::LycIrq()
: time_(disabled_time)
, lycRegSrc_(0)
, statRegSrc_(0)
, lycReg_(0)
, statReg_(0)
, cgb_(false)
{
}
void LycIrq::regChange(unsigned const statReg,
unsigned const lycReg, LyCounter const& lyCounter, unsigned long const cc) {
unsigned long const timeSrc = schedule(statReg, lycReg, lyCounter, cc);
statRegSrc_ = statReg;
lycRegSrc_ = lycReg;
time_ = std::min(time_, timeSrc);
if (cgb_) {
if (time_ - cc > 6u + 4 * lyCounter.isDoubleSpeed() || (timeSrc != time_ && time_ - cc > 2))
lycReg_ = lycReg;
if (time_ - cc > 2)
statReg_ = statReg;
}
else {
if (time_ - cc > 4 || timeSrc != time_)
lycReg_ = lycReg;
statReg_ = statReg;
}
}
bool LycIrq::doEvent(LyCounter const& lyCounter) {
bool flagIrq = false;
if ((statReg_ | statRegSrc_) & lcdstat_lycirqen) {
unsigned const cmpLy = lyCounter.ly() == lcd_lines_per_frame - 1
? 0
: lyCounter.ly() + 1;
flagIrq = lycReg_ == cmpLy && !lycIrqBlockedByM2OrM1StatIrq(lycReg_, statReg_);
}
lycReg_ = lycRegSrc_;
statReg_ = statRegSrc_;
time_ = schedule(statReg_, lycReg_, lyCounter, time_);
return flagIrq;
}
void LycIrq::loadState(SaveState const &state) {
lycRegSrc_ = state.mem.ioamhram.get()[0x145];
statRegSrc_ = state.mem.ioamhram.get()[0x141];
lycReg_ = state.ppu.lyc;
statReg_ = statRegSrc_;
}
void LycIrq::reschedule(LyCounter const &lyCounter, unsigned long cc) {
time_ = std::min(schedule(statReg_ , lycReg_ , lyCounter, cc),
schedule(statRegSrc_, lycRegSrc_, lyCounter, cc));
}
void LycIrq::lcdReset() {
statReg_ = statRegSrc_;
lycReg_ = lycRegSrc_;
}
SYNCFUNC(LycIrq)
{
NSS(time_);
NSS(lycRegSrc_);
NSS(statRegSrc_);
NSS(lycReg_);
NSS(statReg_);
NSS(cgb_);
}

View File

@ -1,65 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef VIDEO_LYC_IRQ_H
#define VIDEO_LYC_IRQ_H
#include "newstate.h"
namespace gambatte {
struct SaveState;
class LyCounter;
class LycIrq {
public:
LycIrq();
bool doEvent(LyCounter const& lyCounter);
unsigned lycReg() const { return lycRegSrc_; }
void loadState(SaveState const &state);
unsigned long time() const { return time_; }
void setCgb(bool cgb) { cgb_ = cgb; }
void lcdReset();
void reschedule(LyCounter const &lyCounter, unsigned long cc);
void statRegChange(unsigned statReg, LyCounter const &lyCounter, unsigned long cc) {
regChange(statReg, lycRegSrc_, lyCounter, cc);
}
void lycRegChange(unsigned lycReg, LyCounter const &lyCounter, unsigned long cc) {
regChange(statRegSrc_, lycReg, lyCounter, cc);
}
private:
unsigned long time_;
unsigned char lycRegSrc_;
unsigned char statRegSrc_;
unsigned char lycReg_;
unsigned char statReg_;
bool cgb_;
void regChange(unsigned statReg, unsigned lycReg,
LyCounter const &lyCounter, unsigned long cc);
public:
template<bool isReader>void SyncState(NewState *ns);
};
}
#endif

View File

@ -1,72 +0,0 @@
#ifndef M0_IRQ_H
#define M0_IRQ_H
#include "lcddef.h"
#include "../savestate.h"
#include "../newstate.h"
namespace gambatte {
class MStatIrqEvent {
public:
MStatIrqEvent() : lycReg_(0), statReg_(0) {}
void lcdReset(unsigned lycReg) { lycReg_ = lycReg; }
void lycRegChange(unsigned lycReg, unsigned long nextM0IrqTime,
unsigned long nextM2IrqTime, unsigned long cc, bool ds, bool cgb) {
if (cc + 5 * cgb + 1 - ds < std::min(nextM0IrqTime, nextM2IrqTime))
lycReg_ = lycReg;
}
void statRegChange(unsigned statReg, unsigned long nextM0IrqTime, unsigned long nextM1IrqTime,
unsigned long nextM2IrqTime, unsigned long cc, bool cgb) {
if (cc + 2 * cgb < std::min(std::min(nextM0IrqTime, nextM1IrqTime), nextM2IrqTime))
statReg_ = statReg;
}
bool doM0Event(unsigned ly, unsigned statReg, unsigned lycReg) {
bool const flagIrq = ((statReg | statReg_) & lcdstat_m0irqen)
&& (!(statReg_ & lcdstat_lycirqen) || ly != lycReg_);
lycReg_ = lycReg;
statReg_ = statReg;
return flagIrq;
}
bool doM1Event(unsigned statReg) {
bool const flagIrq = (statReg & lcdstat_m1irqen)
&& !(statReg_ & (lcdstat_m2irqen | lcdstat_m0irqen));
statReg_ = statReg;
return flagIrq;
}
bool doM2Event(unsigned ly, unsigned statReg, unsigned lycReg) {
bool const blockedByM1Irq = ly == 0 && (statReg_ & lcdstat_m1irqen);
bool const blockedByLycIrq = (statReg_ & lcdstat_lycirqen)
&& (ly == 0 ? ly : ly - 1) == lycReg_;
bool const flagIrq = !blockedByM1Irq && !blockedByLycIrq;
lycReg_ = lycReg;
statReg_ = statReg;
return flagIrq;
}
void loadState(SaveState const& state) {
lycReg_ = state.ppu.m0lyc;
statReg_ = state.mem.ioamhram.get()[0x141];
}
private:
unsigned char statReg_;
unsigned char lycReg_;
public:
template<bool isReader>
void SyncState(NewState *ns)
{
NSS(statReg_);
NSS(lycReg_);
}
};
}
#endif

View File

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

View File

@ -1,24 +0,0 @@
#ifndef NEXT_M0_TIME_H_
#define NEXT_M0_TIME_H_
#include "newstate.h"
namespace gambatte {
class NextM0Time {
public:
NextM0Time() : predictedNextM0Time_(0) {}
void predictNextM0Time(class PPU const &v);
void invalidatePredictedNextM0Time() { predictedNextM0Time_ = 0; }
unsigned long predictedNextM0Time() const { return predictedNextM0Time_; }
private:
unsigned long predictedNextM0Time_;
public:
template<bool isReader>void SyncState(NewState *ns);
};
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,163 +0,0 @@
//
// Copyright (C) 2010 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef PPU_H
#define PPU_H
#include "lcddef.h"
#include "ly_counter.h"
#include "sprite_mapper.h"
#include "gbint.h"
#include <cstddef>
#include "newstate.h"
namespace gambatte {
enum {
layer_mask_bg = 1,
layer_mask_obj = 2,
layer_mask_window = 4 };
enum {
max_num_palettes = 8,
num_palette_entries = 4,
ppu_force_signed_enum = -1 };
class PPUFrameBuf {
public:
PPUFrameBuf() : buf_(0), fbline_(nullfbline()), pitch_(0) {}
uint_least32_t * fb() const { return buf_; }
uint_least32_t * fbline() const { return fbline_; }
std::ptrdiff_t pitch() const { return pitch_; }
void setBuf(uint_least32_t *buf, std::ptrdiff_t pitch) { buf_ = buf; pitch_ = pitch; fbline_ = nullfbline(); }
void setFbline(unsigned ly) { fbline_ = buf_ ? buf_ + std::ptrdiff_t(ly) * pitch_ : nullfbline(); }
private:
uint_least32_t *buf_;
uint_least32_t *fbline_;
std::ptrdiff_t pitch_;
static uint_least32_t * nullfbline() { static uint_least32_t nullfbline_[160]; return nullfbline_; }
};
struct PPUPriv;
struct PPUState {
void (*f)(PPUPriv &v);
unsigned (*predictCyclesUntilXpos_f)(PPUPriv const &v, int targetxpos, unsigned cycles);
unsigned char id;
};
struct PPUPriv {
unsigned long bgPalette[max_num_palettes * num_palette_entries];
unsigned long spPalette[max_num_palettes * num_palette_entries];
struct Sprite { unsigned char spx, oampos, line, attrib; } spriteList[lcd_max_num_sprites_per_line + 1];
unsigned short spwordList[lcd_max_num_sprites_per_line + 1];
unsigned char nextSprite;
unsigned char currentSprite;
unsigned layersMask;
unsigned char const *vram;
PPUState const *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 cgbDmg;
bool weMaster;
PPUPriv(NextM0Time &nextM0Time, unsigned char const *oamram, unsigned char const *vram);
};
class PPU {
public:
PPU(NextM0Time &nextM0Time, unsigned char const *oamram, unsigned char const *vram)
: p_(nextM0Time, oamram, vram)
{
}
unsigned long * bgPalette() { return p_.bgPalette; }
bool cgb() const { return p_.cgb; }
bool cgbDmg() const { return p_.cgbDmg; }
void doLyCountEvent() { p_.lyCounter.doEvent(); }
unsigned long doSpriteMapEvent(unsigned long time) { return p_.spriteMapper.doEvent(time); }
PPUFrameBuf const & 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(SaveState const &state, unsigned char const *oamram);
LyCounter const & lyCounter() const { return p_.lyCounter; }
unsigned long now() const { return p_.now; }
void oamChange(unsigned long cc) { p_.spriteMapper.oamChange(cc); }
void oamChange(unsigned char const *oamram, unsigned long cc) { p_.spriteMapper.oamChange(oamram, cc); }
unsigned long predictedNextXposTime(unsigned xpos) const;
void reset(unsigned char const *oamram, unsigned char const *vram, bool cgb);
void setCgbDmg(bool enabled) { p_.cgbDmg = enabled; }
void resetCc(unsigned long oldCc, unsigned long newCc);
void setFrameBuf(uint_least32_t *buf, std::ptrdiff_t pitch) { p_.framebuf.setBuf(buf, pitch); }
void setLcdc(unsigned lcdc, unsigned long cc);
void setScx(unsigned scx) { p_.scx = scx; }
void setScy(unsigned scy) { p_.scy = scy; }
void setStatePtrs(SaveState &ss) { p_.spriteMapper.setStatePtrs(ss); }
void setWx(unsigned wx) { p_.wx = wx; }
void setWy(unsigned wy) { p_.wy = wy; }
void updateWy2() { p_.wy2 = p_.wy; }
void speedChange();
unsigned long * spPalette() { return p_.spPalette; }
void update(unsigned long cc);
void setLayers(unsigned mask) { p_.layersMask = mask; }
private:
PPUPriv p_;
public:
template<bool isReader>void SyncState(NewState *ns);
};
}
#endif

View File

@ -1,209 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "sprite_mapper.h"
#include "counterdef.h"
#include "next_m0_time.h"
#include "../insertion_sort.h"
#include <algorithm>
using namespace gambatte;
namespace {
class SpxLess {
public:
explicit SpxLess(unsigned char const *spxlut) : spxlut_(spxlut) {}
bool operator()(unsigned char lhs, unsigned char rhs) const {
return spxlut_[lhs] < spxlut_[rhs];
}
private:
unsigned char const *const spxlut_;
};
unsigned toPosCycles(unsigned long const cc, LyCounter const& lyCounter) {
unsigned lc = lyCounter.lineCycles(cc) + 1;
if (lc >= lcd_cycles_per_line)
lc -= lcd_cycles_per_line;
return lc;
}
}
SpriteMapper::OamReader::OamReader(LyCounter const &lyCounter, unsigned char const *oamram)
: lyCounter_(lyCounter)
, oamram_(oamram)
, cgb_(false)
{
reset(oamram, false);
}
void SpriteMapper::OamReader::reset(unsigned char const* const oamram, bool const cgb) {
oamram_ = oamram;
cgb_ = cgb;
setLargeSpritesSrc(false);
lu_ = 0;
lastChange_ = 0xFF;
std::fill_n(lsbuf_, sizeof lsbuf_ / sizeof * lsbuf_, largeSpritesSrc_);
for (int i = 0; i < lcd_num_oam_entries; ++i) {
buf_[2 * i] = oamram[4 * i];
buf_[2 * i + 1] = oamram[4 * i + 1];
}
}
void SpriteMapper::OamReader::update(unsigned long const cc) {
if (cc > lu_) {
if (changed()) {
unsigned const lulc = toPosCycles(lu_, lyCounter_);
unsigned pos = std::min(lulc, 2u * lcd_num_oam_entries);
unsigned distance = 2 * lcd_num_oam_entries;
if ((cc - lu_) >> lyCounter_.isDoubleSpeed() < lcd_cycles_per_line) {
unsigned cclc = toPosCycles(cc, lyCounter_);
distance = std::min(cclc, 2u * lcd_num_oam_entries)
- pos + (cclc < lulc ? 2 * lcd_num_oam_entries : 0);
}
{
unsigned targetDistance =
lastChange_ - pos + (lastChange_ <= pos ? 2 * lcd_num_oam_entries : 0);
if (targetDistance <= distance) {
distance = targetDistance;
lastChange_ = 0xFF;
}
}
while (distance--) {
if (!(pos & 1)) {
if (pos == 2 * lcd_num_oam_entries)
pos = 0;
if (cgb_)
lsbuf_[pos / 2] = largeSpritesSrc_;
buf_[pos] = oamram_[2 * pos];
buf_[pos + 1] = oamram_[2 * pos + 1];
}
else
lsbuf_[pos / 2] = (lsbuf_[pos / 2] & cgb_) | largeSpritesSrc_;
++pos;
}
}
lu_ = cc;
}
}
void SpriteMapper::OamReader::change(unsigned long cc) {
update(cc);
lastChange_ = std::min(toPosCycles(lu_, lyCounter_), 2u * lcd_num_oam_entries);
}
void SpriteMapper::OamReader::setStatePtrs(SaveState& state) {
state.ppu.oamReaderBuf.set(buf_, sizeof buf_ / sizeof * buf_);
state.ppu.oamReaderSzbuf.set(lsbuf_, sizeof lsbuf_ / sizeof * lsbuf_);
}
void SpriteMapper::OamReader::loadState(SaveState const& ss, unsigned char const* const oamram) {
oamram_ = oamram;
largeSpritesSrc_ = ss.mem.ioamhram.get()[0x140] >> 2 & 1;
lu_ = ss.ppu.enableDisplayM0Time;
change(lu_);
}
SYNCFUNC(SpriteMapper::OamReader)
{
NSS(buf_);
NSS(lsbuf_);
NSS(lu_);
NSS(lastChange_);
NSS(largeSpritesSrc_);
NSS(cgb_);
}
void SpriteMapper::OamReader::enableDisplay(unsigned long cc) {
std::fill_n(buf_, sizeof buf_ / sizeof * buf_, 0);
std::fill_n(lsbuf_, sizeof lsbuf_ / sizeof * lsbuf_, false);
lu_ = cc + (2 * lcd_num_oam_entries << lyCounter_.isDoubleSpeed()) + 1;
lastChange_ = 2 * lcd_num_oam_entries;
}
SpriteMapper::SpriteMapper(NextM0Time &nextM0Time,
LyCounter const &lyCounter,
unsigned char const *oamram)
: nextM0Time_(nextM0Time)
, oamReader_(lyCounter, oamram)
{
clearMap();
}
void SpriteMapper::reset(unsigned char const *oamram, bool cgb) {
oamReader_.reset(oamram, cgb);
clearMap();
}
void SpriteMapper::clearMap() {
std::fill_n(num_, sizeof num_ / sizeof * num_, 1 * need_sorting_flag);
}
void SpriteMapper::mapSprites() {
clearMap();
for (int i = 0; i < lcd_num_oam_entries; ++i) {
int const spriteHeight = 8 + 8 * largeSprites(i);
unsigned const bottomPos = posbuf()[2 * i] - 17 + spriteHeight;
if (bottomPos < lcd_vres - 1u + spriteHeight) {
int ly = std::max(static_cast<int>(bottomPos) + 1 - spriteHeight, 0);
int const end = std::min(bottomPos, lcd_vres - 1u) + 1;
do {
if (num_[ly] < need_sorting_flag + lcd_max_num_sprites_per_line)
spritemap_[ly][num_[ly]++ - need_sorting_flag] = 2 * i;
} while (++ly != end);
}
}
nextM0Time_.invalidatePredictedNextM0Time();
}
void SpriteMapper::sortLine(unsigned const ly) const {
num_[ly] &= ~(1u * need_sorting_flag);
insertionSort(spritemap_[ly], spritemap_[ly] + num_[ly],
SpxLess(posbuf() + 1));
}
unsigned long SpriteMapper::doEvent(unsigned long const time) {
oamReader_.update(time);
mapSprites();
return oamReader_.changed()
? time + oamReader_.lineTime()
: static_cast<unsigned long>(disabled_time);
}
SYNCFUNC(SpriteMapper)
{
NSS(spritemap_);
NSS(num_);
SSS(oamReader_);
}

View File

@ -1,125 +0,0 @@
//
// Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
//
// 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.,
// 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
//
#ifndef SPRITE_MAPPER_H
#define SPRITE_MAPPER_H
#include "ly_counter.h"
#include "../savestate.h"
#include "newstate.h"
namespace gambatte {
class NextM0Time;
class SpriteMapper {
public:
SpriteMapper(NextM0Time &nextM0Time,
LyCounter const &lyCounter,
unsigned char const *oamram);
void reset(unsigned char const *oamram, bool cgb);
unsigned long doEvent(unsigned long time);
bool largeSprites(int spno) const { return oamReader_.largeSprites(spno); }
int numSprites(unsigned ly) const { return num_[ly] & ~(1u * need_sorting_flag); }
void oamChange(unsigned long cc) { oamReader_.change(cc); }
void oamChange(unsigned char const *oamram, unsigned long cc) { oamReader_.change(oamram, cc); }
unsigned char const * oamram() const { return oamReader_.oam(); }
unsigned char const * posbuf() const { return oamReader_.spritePosBuf(); }
void resetCycleCounter(unsigned long oldCc, unsigned long newCc) {
oamReader_.update(oldCc);
oamReader_.resetCycleCounter(oldCc, newCc);
}
void setLargeSpritesSource(bool src) { oamReader_.setLargeSpritesSrc(src); }
unsigned char const* sprites(unsigned ly) const {
if (num_[ly] & need_sorting_flag)
sortLine(ly);
return spritemap_[ly];
}
void setStatePtrs(SaveState &state) { oamReader_.setStatePtrs(state); }
void enableDisplay(unsigned long cc) { oamReader_.enableDisplay(cc); }
void loadState(SaveState const &state, unsigned char const *oamram) {
oamReader_.loadState(state, oamram);
mapSprites();
}
bool inactivePeriodAfterDisplayEnable(unsigned long cc) const {
return oamReader_.inactivePeriodAfterDisplayEnable(cc);
}
static unsigned long schedule(LyCounter const& lyCounter, unsigned long cc) {
return lyCounter.nextLineCycle(2 * lcd_num_oam_entries, cc);
}
private:
class OamReader {
public:
OamReader(LyCounter const &lyCounter, unsigned char const *oamram);
void reset(unsigned char const *oamram, bool cgb);
void change(unsigned long cc);
void change(unsigned char const *oamram, unsigned long cc) { change(cc); oamram_ = oamram; }
bool changed() const { return lastChange_ != 0xFF; }
bool largeSprites(int spNo) const { return lsbuf_[spNo]; }
unsigned char const * oam() const { return oamram_; }
void resetCycleCounter(unsigned long oldCc, unsigned long newCc) { lu_ -= oldCc - newCc; }
void setLargeSpritesSrc(bool src) { largeSpritesSrc_ = src; }
void update(unsigned long cc);
unsigned char const * spritePosBuf() const { return buf_; }
void setStatePtrs(SaveState &state);
void enableDisplay(unsigned long cc);
void loadState(SaveState const &ss, unsigned char const *oamram);
bool inactivePeriodAfterDisplayEnable(unsigned long cc) const { return cc < lu_; }
unsigned lineTime() const { return lyCounter_.lineTime(); }
private:
unsigned char buf_[2 * lcd_num_oam_entries];
bool lsbuf_[lcd_num_oam_entries];
LyCounter const &lyCounter_;
unsigned char const *oamram_;
unsigned long lu_;
unsigned char lastChange_;
bool largeSpritesSrc_;
bool cgb_;
public:
template<bool isReader>void SyncState(NewState *ns);
};
enum { need_sorting_flag = 0x80 };
mutable unsigned char spritemap_[lcd_vres][lcd_max_num_sprites_per_line];
mutable unsigned char num_[lcd_vres];
NextM0Time &nextM0Time_;
OamReader oamReader_;
void clearMap();
void mapSprites();
void sortLine(unsigned ly) const;
public:
template<bool isReader>void SyncState(NewState *ns);
};
}
#endif