nestopia/source/core/board/NstBoardCony.cpp
2012-09-02 13:13:55 -04:00

417 lines
8.8 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
//
////////////////////////////////////////////////////////////////////////////////////////
#include "../NstTimer.hpp"
#include "NstBoard.hpp"
#include "NstBoardCony.hpp"
#include "../NstDipSwitches.hpp"
#include "../NstCrc32.hpp"
namespace Nes
{
namespace Core
{
namespace Boards
{
namespace Cony
{
#ifdef NST_MSVC_OPTIMIZE
#pragma optimize("s", on)
#endif
class Standard::CartSwitches : public DipSwitches
{
enum
{
FF2 = 0xD0350E25,
SFXT40 = 0x1030C4EB,
WH2 = 0x1461D1F8
};
uint region;
CartSwitches()
: region(0) {}
public:
static CartSwitches* Create(const Context& c)
{
switch (Crc32::Compute(c.prg.Mem(),c.prg.Size()))
{
case FF2:
case SFXT40:
case WH2:
return new CartSwitches;
}
return NULL;
}
void SetRegion(uint value)
{
region = value ? 1 : 0;
}
uint GetRegion() const
{
return region ? 0xFF : 0x00;
}
private:
uint GetValue(uint) const
{
return region;
}
void SetValue(uint,uint value)
{
region = value;
}
uint NumDips() const
{
return 1;
}
uint NumValues(uint) const
{
return 2;
}
cstring GetDipName(uint) const
{
return "Region";
}
cstring GetValueName(uint,uint i) const
{
return i ? "Asia" : "US";
}
};
void Standard::Irq::Reset(const bool hard)
{
if (hard)
{
enabled = false;
count = 0;
step = 1;
}
}
Standard::Standard(const Context& c)
:
Board (c),
irq (*c.cpu),
cartSwitches (CartSwitches::Create(c))
{}
Standard::~Standard()
{
delete cartSwitches;
}
Standard::Device Standard::QueryDevice(DeviceType type)
{
if (type == DEVICE_DIP_SWITCHES)
return cartSwitches;
else
return Board::QueryDevice( type );
}
void Standard::SubReset(const bool hard)
{
irq.Reset( hard, true );
if (hard)
{
regs.ctrl = 0;
for (uint i=0; i < 5; ++i)
regs.prg[i] = 0;
regs.pr8 = 0;
}
UpdatePrg();
Map( 0x5000U, &Standard::Peek_5000 );
Map( 0x5100U, 0x51FF, &Standard::Peek_5100, &Standard::Poke_5100 );
if (!board.GetWram())
Map( 0x6000U, 0x7FFFU, &Standard::Peek_6000 );
for (uint i=0x8000; i < 0x9000; i += 0x400)
{
Map( i+0x000, i+0x0FF, &Standard::Poke_8000 );
Map( i+0x100, i+0x1FF, &Standard::Poke_8100 );
for (uint j=i+0x00, n=i+0x100; j < n; j += 0x02)
{
Map( j+0x200, &Standard::Poke_8200 );
Map( j+0x201, &Standard::Poke_8201 );
}
for (uint j=i+0x00, n=i+0x100; j < n; j += 0x20)
{
Map( j+0x300, j+0x30F, &Standard::Poke_8300 );
if (chr.Source().Size() == SIZE_512K)
{
Map( j+0x310, j+0x311, &Standard::Poke_8310_1 );
Map( j+0x316, j+0x317, &Standard::Poke_8310_1 );
}
else
{
Map( j+0x310, j+0x317, &Standard::Poke_8310_0 );
}
}
}
Map( 0xB000U, &Standard::Poke_8000 );
Map( 0xB0FFU, &Standard::Poke_8000 );
Map( 0xB100U, &Standard::Poke_8000 );
}
void Standard::SubLoad(State::Loader& state,const dword baseChunk)
{
NST_VERIFY( baseChunk == (AsciiId<'C','N','Y'>::V) );
if (baseChunk == AsciiId<'C','N','Y'>::V)
{
while (const dword chunk = state.Begin())
{
switch (chunk)
{
case AsciiId<'R','E','G'>::V:
regs.ctrl = state.Read8();
state.Read( regs.prg );
break;
case AsciiId<'P','R','8'>::V:
regs.pr8 = state.Read8();
break;
case AsciiId<'I','R','Q'>::V:
{
State::Loader::Data<3> data( state );
irq.unit.enabled = data[0] & 0x1;
irq.unit.step = (data[0] & 0x2) ? ~0U : 1U;
irq.unit.count = data[1] | data[2] << 8;
break;
}
case AsciiId<'L','A','N'>::V:
NST_VERIFY( cartSwitches );
if (cartSwitches)
cartSwitches->SetRegion( state.Read8() & 0x1 );
break;
}
state.End();
}
}
}
void Standard::SubSave(State::Saver& state) const
{
state.Begin( AsciiId<'C','N','Y'>::V );
{
const byte data[1+5] =
{
regs.ctrl,
regs.prg[0],
regs.prg[1],
regs.prg[2],
regs.prg[3],
regs.prg[4]
};
state.Begin( AsciiId<'R','E','G'>::V ).Write( data ).End();
}
state.Begin( AsciiId<'P','R','8'>::V ).Write8( regs.pr8 ).End();
{
const byte data[3] =
{
(irq.unit.enabled ? 0x1U : 0x0U) |
(irq.unit.step == 1 ? 0x0U : 0x2U),
irq.unit.count & 0xFF,
irq.unit.count >> 8
};
state.Begin( AsciiId<'I','R','Q'>::V ).Write( data ).End();
}
if (cartSwitches)
state.Begin( AsciiId<'L','A','N'>::V ).Write8( cartSwitches->GetRegion() ? 0x1 : 0x0 ).End();
state.End();
}
#ifdef NST_MSVC_OPTIMIZE
#pragma optimize("", on)
#endif
void Standard::UpdatePrg()
{
if (regs.ctrl & 0x10U)
{
wrk.SwapBank<SIZE_8K,0x0000>( 0x1F );
prg.SwapBanks<SIZE_8K,0x0000>( regs.prg[0], regs.prg[1] );
prg.SwapBank<SIZE_8K,0x4000>( regs.prg[2] );
}
else
{
wrk.SwapBank<SIZE_8K,0x0000>( regs.prg[3] );
prg.SwapBank<SIZE_16K,0x0000>( regs.prg[4] & 0x3FU );
prg.SwapBank<SIZE_16K,0x4000>( (regs.prg[4] & 0x30U) | 0x0F );
}
}
NES_PEEK(Standard,5000)
{
return cartSwitches ? cartSwitches->GetRegion() : 0xFF;
}
NES_PEEK(Standard,5100)
{
return regs.pr8;
}
NES_POKE_D(Standard,5100)
{
regs.pr8 = data;
}
NES_PEEK_A(Standard,6000)
{
NST_VERIFY( regs.ctrl & 0x20U );
return (regs.ctrl & 0x20U) ? wrk[0][address - 0x6000] : address >> 8;
}
NES_POKE_D(Standard,8000)
{
if (regs.prg[4] != data)
{
regs.prg[4] = data;
UpdatePrg();
}
}
NES_POKE_D(Standard,8100)
{
const uint diff = data ^ regs.ctrl;
regs.ctrl = data;
if (diff & 0x10)
UpdatePrg();
if (diff & 0xC0)
{
irq.Update();
irq.unit.step = (data & 0x40) ? ~0U : 1U;
}
if (diff & 0x03)
SetMirroringVH01( data );
}
NES_POKE_D(Standard,8200)
{
irq.Update();
irq.unit.count = (irq.unit.count & 0xFF00) | data;
irq.ClearIRQ();
}
NES_POKE_D(Standard,8201)
{
irq.Update();
irq.unit.count = (irq.unit.count & 0x00FF) | (data << 8);
irq.unit.enabled = regs.ctrl & 0x80U;
irq.ClearIRQ();
}
NES_POKE_AD(Standard,8310_0)
{
ppu.Update();
chr.SwapBank<SIZE_1K>( (address & 0x7) << 10, (regs.prg[4] << 4 & 0x300U) | data );
}
NES_POKE_AD(Standard,8310_1)
{
ppu.Update();
chr.SwapBank<SIZE_2K>( (address & 0x3) << 11, data );
}
NES_POKE_AD(Standard,8300)
{
data &= 0x1F;
if (regs.prg[address & 0x3] != data)
{
regs.prg[address & 0x3] = data;
UpdatePrg();
}
}
bool Standard::Irq::Clock()
{
if (enabled && count)
{
count = (count + step) & 0xFFFF;
if (!count)
{
enabled = false;
return true;
}
}
return false;
}
void Standard::Sync(Event event,Input::Controllers* controllers)
{
if (event == EVENT_END_FRAME)
irq.VSync();
Board::Sync( event, controllers );
}
}
}
}
}