nestopia/source/core/board/NstBoardMmc5.hpp
2012-09-02 13:13:55 -04:00

476 lines
10 KiB
C++

////////////////////////////////////////////////////////////////////////////////////////
//
// Nestopia - NES/Famicom emulator written in C++
//
// Copyright (C) 2003-2008 Martin Freij
//
// This file is part of Nestopia.
//
// Nestopia is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// Nestopia 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 for more details.
//
// You should have received a copy of the GNU General Public License
// along with Nestopia; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
////////////////////////////////////////////////////////////////////////////////////////
#ifndef NST_BOARD_MMC5_H
#define NST_BOARD_MMC5_H
#ifdef NST_PRAGMA_ONCE
#pragma once
#endif
namespace Nes
{
namespace Core
{
namespace Boards
{
class NST_NO_VTABLE Mmc5 : public Board
{
protected:
explicit Mmc5(const Context&);
public:
class Sound : public Apu::Channel
{
public:
explicit Sound(Apu&,bool=true);
void WriteCtrl(uint);
uint ReadCtrl() const;
void WriteSquareReg0(uint,uint);
void WriteSquareReg1(uint,uint);
void WriteSquareReg2(uint,uint);
void WritePcmReg0(uint);
void WritePcmReg1(uint);
void SaveState(State::Saver&,dword) const;
void LoadState(State::Loader&);
protected:
void Reset();
bool UpdateSettings();
Cycle Clock(Cycle,Cycle,Cycle);
Sample GetSample();
private:
enum
{
NUM_SQUARES = 2
};
class Square
{
public:
void Reset();
NST_SINGLE_CALL dword GetSample(Cycle);
NST_SINGLE_CALL void WriteReg0(uint);
NST_SINGLE_CALL void WriteReg1(uint,uint);
NST_SINGLE_CALL void WriteReg2(uint,uint);
NST_SINGLE_CALL void Disable(bool);
NST_SINGLE_CALL void ClockQuarter();
NST_SINGLE_CALL void ClockHalf();
inline uint GetLengthCounter() const;
void UpdateSettings(uint);
void SaveState(State::Saver&,dword) const;
void LoadState(State::Loader&,dword);
private:
inline bool CanOutput() const;
enum
{
MIN_FRQ = 0x4,
REG1_WAVELENGTH_LOW = 0xFF,
REG2_WAVELENGTH_HIGH = 0x07,
DUTY_SHIFT = 6
};
uint waveLength;
ibool active;
Cycle frequency;
idword timer;
uint step;
uint duty;
LengthCounter lengthCounter;
Envelope envelope;
};
class Pcm
{
public:
void Reset();
inline Sample GetSample() const;
NST_SINGLE_CALL void WriteReg0(uint);
NST_SINGLE_CALL void WriteReg1(uint);
void SaveState(State::Saver&,dword) const;
void LoadState(State::Loader&);
private:
enum
{
VOLUME = OUTPUT_MUL / 4,
PCM_DISABLE = 0x1
};
ibool enabled;
Sample sample;
Sample amp;
};
uint output;
Cycle rate;
uint fixed;
uint atHalfClock;
dword quarterClock;
Square square[NUM_SQUARES];
Pcm pcm;
DcBlocker dcBlocker;
};
// Needs to be public because of bug in GCC 3.x.x
enum FetchType
{
NT_CIRAM_0,
NT_CIRAM_1,
NT_EXRAM,
NT_FILL,
NT_ZERO
};
private:
void SubReset(bool);
void VBlank();
void HDummy();
void HActive0();
void HActiveX();
void Sync(Event,Input::Controllers*);
void SubSave(State::Saver&) const;
void SubLoad(State::Loader&,dword);
template<uint ADDRESS>
void SwapPrg8Ex(uint);
void UpdatePrg();
void UpdateChrA() const;
void UpdateChrB() const;
void UpdateRenderMethod();
bool ClockSpliter();
inline void Update();
inline ibool IsPpuSprite8x16() const;
uint GetExtPattern(uint) const;
uint GetSpliterAttribute() const;
uint GetSpliterPattern(uint) const;
template<FetchType>
NST_FORCE_INLINE uint FetchByte(uint) const;
template<FetchType NT>
NST_FORCE_INLINE uint FetchNt(uint) const;
template<FetchType NT>
NST_FORCE_INLINE uint FetchNtExt(uint);
template<FetchType NT>
NST_FORCE_INLINE uint FetchNtSplit(uint);
template<FetchType NT>
NST_FORCE_INLINE uint FetchNtExtSplit(uint);
NES_DECL_HOOK( Cpu );
NES_DECL_HOOK( HActive );
NES_DECL_HOOK( HBlank );
NES_DECL_ACCESSOR( Nt_CiRam_0 );
NES_DECL_ACCESSOR( Nt_CiRam_1 );
NES_DECL_ACCESSOR( Nt_ExRam );
NES_DECL_ACCESSOR( Nt_Fill );
NES_DECL_ACCESSOR( Nt_Zero );
NES_DECL_ACCESSOR( NtExt_CiRam_0 );
NES_DECL_ACCESSOR( NtExt_CiRam_1 );
NES_DECL_ACCESSOR( NtExt_ExRam );
NES_DECL_ACCESSOR( NtExt_Fill );
NES_DECL_ACCESSOR( NtSplit_CiRam_0 );
NES_DECL_ACCESSOR( NtSplit_CiRam_1 );
NES_DECL_ACCESSOR( NtSplit_ExRam );
NES_DECL_ACCESSOR( NtSplit_Fill );
NES_DECL_ACCESSOR( NtExtSplit_CiRam_0 );
NES_DECL_ACCESSOR( NtExtSplit_CiRam_1 );
NES_DECL_ACCESSOR( NtExtSplit_ExRam );
NES_DECL_ACCESSOR( NtExtSplit_Fill );
NES_DECL_ACCESSOR( CRom );
NES_DECL_ACCESSOR( CRomExt );
NES_DECL_ACCESSOR( CRomSplit );
NES_DECL_ACCESSOR( CRomExtSplit );
NES_DECL_POKE( 2001 );
NES_DECL_PEEK( 2001 );
NES_DECL_POKE( 5000 );
NES_DECL_POKE( 5002 );
NES_DECL_POKE( 5003 );
NES_DECL_POKE( 5004 );
NES_DECL_POKE( 5006 );
NES_DECL_POKE( 5007 );
NES_DECL_POKE( 5010 );
NES_DECL_POKE( 5011 );
NES_DECL_PEEK( 5015 );
NES_DECL_POKE( 5015 );
NES_DECL_POKE( 5100 );
NES_DECL_POKE( 5101 );
NES_DECL_POKE( 5102 );
NES_DECL_POKE( 5103 );
NES_DECL_POKE( 5104 );
NES_DECL_POKE( 5105 );
NES_DECL_POKE( 5106 );
NES_DECL_POKE( 5107 );
NES_DECL_POKE( 5113 );
NES_DECL_POKE( 5114 );
NES_DECL_POKE( 5120 );
NES_DECL_POKE( 5128 );
NES_DECL_POKE( 5130 );
NES_DECL_POKE( 5200 );
NES_DECL_POKE( 5201 );
NES_DECL_POKE( 5202 );
NES_DECL_POKE( 5203 );
NES_DECL_PEEK( 5204 );
NES_DECL_POKE( 5204 );
NES_DECL_PEEK( 5205 );
NES_DECL_POKE( 5205 );
NES_DECL_PEEK( 5206 );
NES_DECL_POKE( 5206 );
NES_DECL_PEEK( 5C00 );
NES_DECL_POKE( 5C00 );
NES_DECL_PEEK( 6000 );
NES_DECL_POKE( 6000 );
NES_DECL_PEEK( 8000 );
NES_DECL_POKE( 8000 );
NES_DECL_PEEK( A000 );
NES_DECL_POKE( A000 );
NES_DECL_PEEK( C000 );
NES_DECL_POKE( C000 );
struct Flow
{
void Reset();
typedef void (Mmc5::*Phase)();
Cycle cycles;
Phase phase;
uint scanline;
};
struct Irq
{
void Reset();
enum
{
ENABLED = 0x01,
FRAME = 0x40,
HIT = 0x80,
SIGNAL_HIT = ENABLED|HIT
};
uint state;
uint count;
uint target;
};
struct Regs
{
void Reset();
enum
{
PRG_MODE = 0x03,
PRG_MODE_32K = 0x00,
PRG_MODE_16K = 0x01,
PRG_MODE_16K_8K = 0x02,
PRG_MODE_8K = 0x03,
PRG_ROM_SELECT = 0x80,
PRG_ROM_BANK = 0x7F,
PRG_RAM_BANK = 0x07,
CHR_MODE = 0x03,
CHR_MODE_8K = 0x00,
CHR_MODE_4K = 0x01,
CHR_MODE_2K = 0x02,
CHR_MODE_1K = 0x03,
CHR_HIGH = 0x03,
WRK_WRITABLE_A = 0x02,
WRK_WRITABLE_B = 0x01,
NMT_MODE = 0x03,
NMT_CIRAM_0 = 0x00,
NMT_CIRAM_1 = 0x01,
NMT_EXRAM = 0x02,
NMT_FILL = 0x03,
EXRAM_MODE = 0x03,
EXRAM_MODE_PPU_NT = 0x00,
EXRAM_MODE_PPU_EXT = 0x01,
EXRAM_MODE_CPU_RAM = 0x02,
EXRAM_MODE_CPU_ROM = 0x03,
EXRAM_EXT_CHR_BANK = 0x3F,
PPU_CTRL0_SP8X16 = 0x20,
PPU_CTRL1_ENABLED = 0x18
};
uint prgMode;
uint chrMode;
uint exRamMode;
uint mul[2];
};
struct Banks
{
explicit Banks(uint);
void Reset();
enum
{
READABLE_6 = 0x004,
READABLE_8 = 0x008,
READABLE_A = 0x010,
READABLE_C = 0x020,
WRITABLE_6 = 0x040,
WRITABLE_8 = 0x080,
WRITABLE_A = 0x100,
WRITABLE_C = 0x200,
CAN_WRITE_6 = READABLE_6|WRITABLE_6|Regs::WRK_WRITABLE_A|Regs::WRK_WRITABLE_B,
CAN_WRITE_8 = READABLE_8|WRITABLE_8|Regs::WRK_WRITABLE_A|Regs::WRK_WRITABLE_B,
CAN_WRITE_A = READABLE_A|WRITABLE_A|Regs::WRK_WRITABLE_A|Regs::WRK_WRITABLE_B,
CAN_WRITE_C = READABLE_C|WRITABLE_C|Regs::WRK_WRITABLE_A|Regs::WRK_WRITABLE_B
};
enum FetchMode
{
FETCH_MODE_NONE,
FETCH_MODE_BG,
FETCH_MODE_SP
};
enum LastChr
{
LAST_CHR_A = 0,
LAST_CHR_B = 1
};
class Wrk
{
byte banks[8];
public:
enum
{
INVALID = 8
};
explicit Wrk(dword);
inline uint operator [] (uint) const;
};
uint nmt;
word chrA[8];
word chrB[4];
dword chrHigh;
LastChr lastChr;
FetchMode fetchMode;
uint security;
byte prg[4];
const Wrk wrk;
};
struct Filler
{
void Reset();
uint tile;
uint attribute;
static const byte squared[4];
};
struct Spliter
{
void Reset();
enum
{
CTRL_START = 0x1F,
CTRL_RIGHT_SIDE = 0x40,
CTRL_ENABLED = 0x80
};
uint ctrl;
uint tile;
ibool inside;
uint yStart;
uint chrBank;
uint x;
uint y;
};
struct ExRam
{
void Reset(bool);
uint tile;
byte mem[SIZE_1K];
};
Flow flow;
Irq irq;
Regs regs;
Banks banks;
const byte* ciRam[2];
Filler filler;
Spliter spliter;
Io::Port p2001;
ExRam exRam;
Sound sound;
};
}
}
}
#endif