let's get rid of libgambatte sources.......
This commit is contained in:
parent
62c6513ee3
commit
7a584b48bd
|
@ -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))
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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>
|
|
@ -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>
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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
|
@ -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
|
|
@ -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
|
@ -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
|
|
@ -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*/
|
|
@ -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
|
|
@ -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_);
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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_);
|
||||
}
|
|
@ -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
|
|
@ -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_);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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_);
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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_);
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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_);
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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_);
|
||||
}
|
|
@ -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
|
|
@ -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_);
|
||||
}
|
|
@ -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
|
|
@ -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_);
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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_);
|
||||
}
|
|
@ -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
|
|
@ -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_);
|
||||
}
|
|
@ -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
|
|
@ -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_);
|
||||
}
|
|
@ -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
|
|
@ -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_);
|
||||
}
|
|
@ -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
|
|
@ -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_);
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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_);
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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_);
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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_);
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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_);
|
||||
}
|
|
@ -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
|
@ -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
|
|
@ -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_);
|
||||
}
|
|
@ -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
|
Loading…
Reference in New Issue