mirror of
https://github.com/0ldsk00l/nestopia.git
synced 2025-04-02 10:31:51 -04:00
393 lines
8.6 KiB
C++
393 lines
8.6 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 "NstBoard.hpp"
|
|
#include "NstBoardMmc3.hpp"
|
|
#include "NstBoardBmcFk23c.hpp"
|
|
#include "../NstCrc32.hpp"
|
|
#include "../NstDipSwitches.hpp"
|
|
|
|
namespace Nes
|
|
{
|
|
namespace Core
|
|
{
|
|
namespace Boards
|
|
{
|
|
namespace Bmc
|
|
{
|
|
#ifdef NST_MSVC_OPTIMIZE
|
|
#pragma optimize("s", on)
|
|
#endif
|
|
|
|
class Fk23c::CartSwitches : public DipSwitches
|
|
{
|
|
enum Type
|
|
{
|
|
CRC_4_IN_1 = 0x38BA830E,
|
|
CRC_6_IN_1 = 0xC16708E8,
|
|
CRC_8_IN_1 = 0x63A87C95,
|
|
CRC_15_IN_1 = 0x30FF6159,
|
|
CRC_18_IN_1 = 0x83A38A2F,
|
|
CRC_20_IN_1 = 0xFD9D1925
|
|
};
|
|
|
|
uint mode;
|
|
const Type type;
|
|
|
|
explicit CartSwitches(Type t)
|
|
: mode(0), type(t) {}
|
|
|
|
uint NumValues(uint) const
|
|
{
|
|
return type == CRC_4_IN_1 || type == CRC_6_IN_1 || type == CRC_8_IN_1 ? 2 : 8;
|
|
}
|
|
|
|
public:
|
|
|
|
static CartSwitches* Create(const Context& c)
|
|
{
|
|
switch (const dword crc = Crc32::Compute(c.prg.Mem(),c.prg.Size()))
|
|
{
|
|
case CRC_4_IN_1:
|
|
case CRC_6_IN_1:
|
|
case CRC_8_IN_1:
|
|
case CRC_15_IN_1:
|
|
case CRC_18_IN_1:
|
|
case CRC_20_IN_1:
|
|
|
|
return new CartSwitches( static_cast<Type>(crc) );
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void SetMode(uint value)
|
|
{
|
|
const uint num = CartSwitches::NumValues(0) - 1;
|
|
mode = NST_MIN(value,num);
|
|
}
|
|
|
|
uint GetMode() const
|
|
{
|
|
return mode;
|
|
}
|
|
|
|
private:
|
|
|
|
uint GetValue(uint) const
|
|
{
|
|
return mode;
|
|
}
|
|
|
|
void SetValue(uint,uint value)
|
|
{
|
|
mode = value;
|
|
}
|
|
|
|
uint NumDips() const
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
cstring GetDipName(uint) const
|
|
{
|
|
return "Mode";
|
|
}
|
|
|
|
cstring GetValueName(uint,uint i) const
|
|
{
|
|
switch (type)
|
|
{
|
|
case CRC_4_IN_1:
|
|
{
|
|
static const char names[2][8] =
|
|
{
|
|
"4-in-1",
|
|
"7-in-1"
|
|
};
|
|
|
|
return names[i];
|
|
}
|
|
|
|
case CRC_6_IN_1:
|
|
{
|
|
static const char names[2][8] =
|
|
{
|
|
"6-in-1",
|
|
"4-in-1"
|
|
};
|
|
|
|
return names[i];
|
|
}
|
|
|
|
case CRC_8_IN_1:
|
|
{
|
|
static const char names[2][8] =
|
|
{
|
|
"8-in-1",
|
|
"4-in-1"
|
|
};
|
|
|
|
return names[i];
|
|
}
|
|
|
|
case CRC_15_IN_1:
|
|
case CRC_20_IN_1:
|
|
{
|
|
static const char names[8][9] =
|
|
{
|
|
"15-in-1",
|
|
"80-in-1",
|
|
"160-in-1",
|
|
"20-in-1",
|
|
"99-in-1",
|
|
"210-in-1",
|
|
"25-in-1",
|
|
"260-in-1"
|
|
};
|
|
|
|
return names[i];
|
|
}
|
|
|
|
case CRC_18_IN_1:
|
|
{
|
|
static const char names[8][9] =
|
|
{
|
|
"18-in-1",
|
|
"58-in-1",
|
|
"160-in-1",
|
|
"15-in-1",
|
|
"52-in-1",
|
|
"180-in-1",
|
|
"30-in-1",
|
|
"288-in-1"
|
|
};
|
|
|
|
return names[i];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
};
|
|
|
|
Fk23c::Fk23c(const Context& c)
|
|
: Mmc3(c), cartSwitches(CartSwitches::Create(c)) {}
|
|
|
|
Fk23c::~Fk23c()
|
|
{
|
|
delete cartSwitches;
|
|
}
|
|
|
|
void Fk23c::SubReset(const bool hard)
|
|
{
|
|
for (uint i=0; i < 8; ++i)
|
|
exRegs[i] = 0xFF;
|
|
|
|
if (prg.Source().Size() <= SIZE_512K)
|
|
{
|
|
for (uint i=0; i < 4; ++i)
|
|
exRegs[i] = 0x00;
|
|
}
|
|
|
|
unromChr = 0x0;
|
|
|
|
Mmc3::SubReset( hard );
|
|
|
|
Map( 0x5000U, 0x5FFFU, &Fk23c::Poke_5000 );
|
|
Map( 0x8000U, 0xFFFFU, &Fk23c::Poke_8000 );
|
|
|
|
UpdatePrg();
|
|
UpdateChr();
|
|
}
|
|
|
|
Fk23c::Device Fk23c::QueryDevice(DeviceType type)
|
|
{
|
|
if (type == DEVICE_DIP_SWITCHES)
|
|
return cartSwitches;
|
|
else
|
|
return Board::QueryDevice( type );
|
|
}
|
|
|
|
void Fk23c::SubLoad(State::Loader& state,const dword baseChunk)
|
|
{
|
|
if (baseChunk == AsciiId<'B','F','K'>::V)
|
|
{
|
|
while (const dword chunk = state.Begin())
|
|
{
|
|
if (chunk == AsciiId<'R','E','G'>::V)
|
|
{
|
|
State::Loader::Data<9> data( state );
|
|
|
|
for (uint i=0; i < 8; ++i)
|
|
exRegs[i] = data[i];
|
|
|
|
unromChr = data[8] & 0x3;
|
|
|
|
if (cartSwitches)
|
|
cartSwitches->SetMode( data[8] >> 2 & 0x7 );
|
|
}
|
|
|
|
state.End();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Mmc3::SubLoad( state, baseChunk );
|
|
}
|
|
}
|
|
|
|
void Fk23c::SubSave(State::Saver& state) const
|
|
{
|
|
Mmc3::SubSave( state );
|
|
|
|
const byte data[] =
|
|
{
|
|
exRegs[0],
|
|
exRegs[1],
|
|
exRegs[2],
|
|
exRegs[3],
|
|
exRegs[4],
|
|
exRegs[5],
|
|
exRegs[6],
|
|
exRegs[7],
|
|
unromChr | (cartSwitches ? cartSwitches->GetMode() << 2 : 0)
|
|
};
|
|
|
|
state.Begin( AsciiId<'B','F','K'>::V ).Begin( AsciiId<'R','E','G'>::V ).Write( data ).End().End();
|
|
}
|
|
|
|
#ifdef NST_MSVC_OPTIMIZE
|
|
#pragma optimize("", on)
|
|
#endif
|
|
|
|
void NST_FASTCALL Fk23c::UpdatePrg(uint address,uint bank)
|
|
{
|
|
if ((exRegs[0] & 0x7U) - 3 > 1 && (!(exRegs[3] & 0x2U) || address < 0x4000))
|
|
{
|
|
if (exRegs[0] & 0x3U)
|
|
bank = (bank & (0x3FU >> (exRegs[0] & 0x3U))) | (exRegs[1] << 1);
|
|
|
|
prg.SwapBank<SIZE_8K>( address, bank );
|
|
}
|
|
}
|
|
|
|
void NST_FASTCALL Fk23c::UpdateChr(uint address,uint bank) const
|
|
{
|
|
if (!(exRegs[0] & 0x40U) && (!(exRegs[3] & 0x2U) || (address != 0x400 && address != 0xC00)))
|
|
chr.SwapBank<SIZE_1K>( address, (exRegs[2] & 0x7FU) << 3 | bank );
|
|
}
|
|
|
|
void Fk23c::UpdatePrg()
|
|
{
|
|
if ((exRegs[0] & 0x7U) == 4)
|
|
{
|
|
prg.SwapBank<SIZE_32K,0x0000>( exRegs[1] >> 1 );
|
|
}
|
|
else if ((exRegs[0] & 0x7U) == 3)
|
|
{
|
|
prg.SwapBanks<SIZE_16K,0x0000>( exRegs[1], exRegs[1] );
|
|
}
|
|
else
|
|
{
|
|
if (exRegs[3] & 0x2U)
|
|
prg.SwapBanks<SIZE_8K,0x4000>( exRegs[4], exRegs[5] );
|
|
|
|
Mmc3::UpdatePrg();
|
|
}
|
|
}
|
|
|
|
void Fk23c::UpdateChr() const
|
|
{
|
|
ppu.Update();
|
|
|
|
if (exRegs[0] & 0x40U)
|
|
{
|
|
chr.SwapBank<SIZE_8K,0x0000>( exRegs[2] | unromChr );
|
|
}
|
|
else
|
|
{
|
|
if (exRegs[3] & 0x2U)
|
|
{
|
|
const uint base = (exRegs[2] & 0x7FU) << 3;
|
|
|
|
chr.SwapBank<SIZE_1K,0x0400>( base | exRegs[6] );
|
|
chr.SwapBank<SIZE_1K,0x0C00>( base | exRegs[7] );
|
|
}
|
|
|
|
Mmc3::UpdateChr();
|
|
}
|
|
}
|
|
|
|
NES_POKE_AD(Fk23c,5000)
|
|
{
|
|
if (address & (1U << ((cartSwitches ? cartSwitches->GetMode() : 0) + 4)))
|
|
{
|
|
exRegs[address & 0x3] = data;
|
|
|
|
Fk23c::UpdatePrg();
|
|
Fk23c::UpdateChr();
|
|
}
|
|
}
|
|
|
|
NES_POKE_AD(Fk23c,8000)
|
|
{
|
|
if (exRegs[0] & 0x40U)
|
|
{
|
|
unromChr = (exRegs[0] & 0x30U) ? 0x0 : data & 0x3;
|
|
|
|
Fk23c::UpdateChr();
|
|
}
|
|
else switch (address & 0xE001)
|
|
{
|
|
case 0x8000: Mmc3::NES_DO_POKE(8000,address,data); break;
|
|
case 0x8001:
|
|
|
|
if (exRegs[3] << 2 & (regs.ctrl0 & 0x8))
|
|
{
|
|
exRegs[4 | (regs.ctrl0 & 0x3)] = data;
|
|
|
|
Fk23c::UpdatePrg();
|
|
Fk23c::UpdateChr();
|
|
}
|
|
else
|
|
{
|
|
Mmc3::NES_DO_POKE(8001,address,data);
|
|
}
|
|
break;
|
|
|
|
case 0xA000: SetMirroringHV(data); break;
|
|
case 0xA001: Mmc3::NES_DO_POKE(A001,address,data); break;
|
|
case 0xC000: Mmc3::NES_DO_POKE(C000,address,data); break;
|
|
case 0xC001: Mmc3::NES_DO_POKE(C001,address,data); break;
|
|
case 0xE000: Mmc3::NES_DO_POKE(E000,address,data); break;
|
|
case 0xE001: Mmc3::NES_DO_POKE(E001,address,data); break;
|
|
|
|
default: NST_UNREACHABLE();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|