//////////////////////////////////////////////////////////////////////////////////////// // // 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 "../NstLog.hpp" #include "../NstCpu.hpp" #include "../NstPpu.hpp" #include "../NstState.hpp" #include "NstVsSystem.hpp" #include "NstVsRbiBaseball.hpp" #include "NstVsTkoBoxing.hpp" #include "NstVsSuperXevious.hpp" namespace Nes { namespace Core { #ifdef NST_MSVC_OPTIMIZE #pragma optimize("s", on) #endif class Cartridge::VsSystem::Dip { class Proxy; struct Setting { uint data; cstring name; }; Setting* settings; uint size; uint selection; cstring name; public: struct Value { cstring const name; const uint data; const uint selection; Value(cstring n,uint d,uint s=0) : name(n), data(d), selection(s) {} }; void operator = (const Value&); private: class Proxy { Dip& dip; const uint index; public: Proxy(Dip& d,uint i) : dip(d), index(i) {} void operator = (const Value& value) { dip.settings[index].data = value.data; dip.settings[index].name = value.name; } operator uint() const { return dip.settings[index].data; } cstring Name() const { return dip.settings[index].name; } }; public: Dip() : settings(NULL) {} ~Dip() { delete [] settings; } uint Size() const { return size; } void Select(uint i) { NST_ASSERT( i < size ); selection = i; } uint Selection() const { return selection; } Proxy operator [] (uint i) { NST_ASSERT( i < size ); return Proxy(*this,i); } cstring Name() const { return name; } }; void Cartridge::VsSystem::Dip::operator = (const Value& value) { NST_ASSERT( settings == NULL && value.data && value.selection < value.data ); name = value.name; size = value.data; selection = value.selection; settings = new Setting [size]; } Cartridge::VsSystem::VsDipSwitches::VsDipSwitches(Dip*& old,uint n) : table(old), size(n) { old = NULL; regs[0] = 0; regs[1] = 0; for (uint i=0; i < n; ++i) { regs[0] |= (table[i][table[i].Selection()] & DIPSWITCH_4016_MASK) << DIPSWITCH_4016_SHIFT; regs[1] |= (table[i][table[i].Selection()] & DIPSWITCH_4017_MASK) << DIPSWITCH_4017_SHIFT; } } Cartridge::VsSystem::VsDipSwitches::~VsDipSwitches() { delete [] table; } inline void Cartridge::VsSystem::VsDipSwitches::Reset() { coinTimer = 0; regs[0] &= ~uint(COIN); } inline uint Cartridge::VsSystem::VsDipSwitches::Reg(uint i) const { return regs[i]; } uint Cartridge::VsSystem::VsDipSwitches::NumDips() const { return size; } uint Cartridge::VsSystem::VsDipSwitches::NumValues(uint dip) const { NST_ASSERT( dip < size ); return table[dip].Size(); } cstring Cartridge::VsSystem::VsDipSwitches::GetDipName(uint dip) const { NST_ASSERT( dip < size ); return table[dip].Name(); } cstring Cartridge::VsSystem::VsDipSwitches::GetValueName(uint dip,uint value) const { NST_ASSERT( dip < size && value < table[dip].Size() ); return table[dip][value].Name(); } uint Cartridge::VsSystem::VsDipSwitches::GetValue(uint dip) const { NST_ASSERT( dip < size ); return table[dip].Selection(); } void Cartridge::VsSystem::VsDipSwitches::SetValue(uint dip,uint value) { NST_ASSERT( dip < size && value < table[dip].Size() ); const uint old = table[dip].Selection(); regs[0] &= ~((table[dip][old] & DIPSWITCH_4016_MASK) << DIPSWITCH_4016_SHIFT); regs[1] &= ~((table[dip][old] & DIPSWITCH_4017_MASK) << DIPSWITCH_4017_SHIFT); table[dip].Select( value ); regs[0] |= (table[dip][value] & DIPSWITCH_4016_MASK) << DIPSWITCH_4016_SHIFT; regs[1] |= (table[dip][value] & DIPSWITCH_4017_MASK) << DIPSWITCH_4017_SHIFT; } #ifdef NST_MSVC_OPTIMIZE #pragma optimize("", on) #endif void Cartridge::VsSystem::VsDipSwitches::BeginFrame(Input::Controllers* const input) { if (!coinTimer) { if (input) { Input::Controllers::VsSystem::callback( input->vsSystem ); if (input->vsSystem.insertCoin & COIN) { regs[0] |= input->vsSystem.insertCoin & COIN; coinTimer = 20; } } } else if (--coinTimer == 15) { regs[0] &= ~uint(COIN); } } #ifdef NST_MSVC_OPTIMIZE #pragma optimize("s", on) #endif struct Cartridge::VsSystem::Context { Dip* dips; uint numDips; Cpu& cpu; Ppu& ppu; PpuModel ppuModel; Mode mode; InputMapper::Type inputMapper; Context(Cpu& c,Ppu& p) : dips (NULL), numDips (0), cpu (c), ppu (p), ppuModel (PPU_RP2C03B), mode (MODE_STD), inputMapper (InputMapper::TYPE_NONE) { } void SetDips(uint n) { numDips = n; dips = new Dip [n]; } }; Cartridge::VsSystem* Cartridge::VsSystem::Create ( Cpu& cpu, Ppu& ppu, const PpuModel ppuModel, const dword prgCrc ) { switch (prgCrc) { // VS. Dual-System Games are unsupported case 0xB90497AA: // Tennis case 0x2A909613: // Tennis (alt) case 0xBC202DB6: // Tennis P2 case 0x008A9C16: // Wrecking Crew P1 case 0x30C42B1E: // Wrecking Crew P2 case 0xAD407F52: // Balloon Fight P1 case 0x6AD67502: // Balloon Fight P2 case 0x18A93B7B: // Mahjong (J) case 0xA2AD7D61: // Mahjong (J) (alt) case 0xA9A4A6C5: // Mahjong (J) P1 case 0x78D1D213: // Mahjong (J) P2 case 0x13A91937: // Baseball P1 case 0xC4DD2523: // Baseball P1 (alt 1) case 0xB5853830: // Baseball P1 (alt 2) case 0x968A6E9D: // Baseball P2 case 0xF64D7252: // Baseball P2 (alt 1) case 0xF5DEBF88: // Baseball P2 (alt 2) case 0xF42DAB14: // Ice Climber P1 case 0x7D6B764F: // Ice Climber P2 throw RESULT_ERR_UNSUPPORTED_VSSYSTEM; } Context context( cpu, ppu ); try { // Credit to the MAME devs for much of the DIP switch info. switch (prgCrc) { case 0xEB2DBA63: // TKO Boxing context.SetDips(7); context.dips[0] = Dip::Value( "Coinage", 4, 0 ); context.dips[0][0] = Dip::Value( "1 Coin / 1 Credit", 0x00 ); context.dips[0][1] = Dip::Value( "1 Coin / 2 Credits", 0x01 ); context.dips[0][2] = Dip::Value( "2 Coins / 1 Credit", 0x02 ); context.dips[0][3] = Dip::Value( "3 Coins / 1 Credit", 0x03 ); context.dips[1] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[1][0] = Dip::Value( "Off", 0x00 ); context.dips[1][1] = Dip::Value( "On", 0x04 ); context.dips[2] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[2][0] = Dip::Value( "Off", 0x00 ); context.dips[2][1] = Dip::Value( "On", 0x08 ); context.dips[3] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[3][0] = Dip::Value( "Off", 0x00 ); context.dips[3][1] = Dip::Value( "On", 0x10 ); context.dips[4] = Dip::Value( "Palette Color", 2, 1 ); context.dips[4][0] = Dip::Value( "Black", 0x00 ); context.dips[4][1] = Dip::Value( "White", 0x20 ); context.dips[5] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[5][0] = Dip::Value( "Off", 0x00 ); context.dips[5][1] = Dip::Value( "On", 0x40 ); context.dips[6] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[6][0] = Dip::Value( "Off", 0x00 ); context.dips[6][1] = Dip::Value( "On", 0x80 ); context.inputMapper = InputMapper::TYPE_1; context.mode = MODE_TKO; break; case 0x135ADF7C: // RBI Baseball context.SetDips(4); context.dips[0] = Dip::Value( "Coinage", 4, 0 ); context.dips[0][0] = Dip::Value( "1 Coin / 1 Credit", 0x00 ); context.dips[0][1] = Dip::Value( "1 Coin / 2 Credits", 0x01 ); context.dips[0][2] = Dip::Value( "2 Coins / 1 Credit", 0x02 ); context.dips[0][3] = Dip::Value( "3 Coins / 1 Credit", 0x03 ); context.dips[1] = Dip::Value( "Max. 1p/in, 2p/in, Min", 4, 1 ); context.dips[1][0] = Dip::Value( "2, 1, 3", 0x04 ); context.dips[1][1] = Dip::Value( "2, 2, 4", 0x0C ); context.dips[1][2] = Dip::Value( "3, 2, 6", 0x00 ); context.dips[1][3] = Dip::Value( "4, 3, 7", 0x08 ); context.dips[2] = Dip::Value( "Demo Sounds", 2, 1 ); context.dips[2][0] = Dip::Value( "Off", 0x00 ); context.dips[2][1] = Dip::Value( "On", 0x10 ); context.dips[3] = Dip::Value( "PPU", 5, 0 ); context.dips[3][0] = Dip::Value( "RP2C03", 0x20 ); context.dips[3][1] = Dip::Value( "RP2C04-0001", 0x00 ); context.dips[3][2] = Dip::Value( "RP2C04-0002", 0x40 ); context.dips[3][3] = Dip::Value( "RP2C04-0003", 0x80 ); context.dips[3][4] = Dip::Value( "RP2C04-0004", 0xC0 ); context.inputMapper = InputMapper::TYPE_2; context.mode = MODE_RBI; break; case 0xED588F00: // Duck Hunt context.SetDips(4); context.dips[0] = Dip::Value( "Coinage", 8, 0 ); context.dips[0][0] = Dip::Value( "1 Coin / 1 Credit", 0x00 ); context.dips[0][1] = Dip::Value( "1 Coin / 2 Credits", 0x04 ); context.dips[0][2] = Dip::Value( "1 Coin / 3 Credits", 0x02 ); context.dips[0][3] = Dip::Value( "2 Coins / 1 Credit", 0x06 ); context.dips[0][4] = Dip::Value( "3 Coins / 1 Credit", 0x01 ); context.dips[0][5] = Dip::Value( "4 Coins / 1 Credit", 0x05 ); context.dips[0][6] = Dip::Value( "5 Coins / 1 Credit", 0x03 ); context.dips[0][7] = Dip::Value( "Free Play", 0x07 ); context.dips[1] = Dip::Value( "Difficulty", 4, 1 ); context.dips[1][0] = Dip::Value( "Easy", 0x00 ); context.dips[1][1] = Dip::Value( "Normal", 0x08 ); context.dips[1][2] = Dip::Value( "Hard", 0x10 ); context.dips[1][3] = Dip::Value( "Very Hard", 0x18 ); context.dips[2] = Dip::Value( "Misses per Game", 2, 1 ); context.dips[2][0] = Dip::Value( "3", 0x00 ); context.dips[2][1] = Dip::Value( "5", 0x20 ); context.dips[3] = Dip::Value( "Bonus Life", 4, 0 ); context.dips[3][0] = Dip::Value( "30000", 0x00 ); context.dips[3][1] = Dip::Value( "50000", 0x40 ); context.dips[3][2] = Dip::Value( "80000", 0x80 ); context.dips[3][3] = Dip::Value( "100000", 0xC0 ); break; case 0x16D3F469: // Ninja Jajamaru Kun (J) context.SetDips(5); context.dips[0] = Dip::Value( "Coinage", 8, 0 ); context.dips[0][0] = Dip::Value( "1 Coin / 1 Credit", 0x00 ); context.dips[0][1] = Dip::Value( "1 Coin / 2 Credits", 0x04 ); context.dips[0][2] = Dip::Value( "1 Coin / 3 Credits", 0x02 ); context.dips[0][3] = Dip::Value( "1 Coin / 4 Credits", 0x06 ); context.dips[0][4] = Dip::Value( "2 Coins / 1 Credit", 0x01 ); context.dips[0][5] = Dip::Value( "3 Coins / 1 Credit", 0x05 ); context.dips[0][6] = Dip::Value( "4 Coins / 1 Credit", 0x03 ); context.dips[0][7] = Dip::Value( "Free Play", 0x07 ); context.dips[1] = Dip::Value( "Lives", 3, 0 ); context.dips[1][0] = Dip::Value( "3", 0x00 ); context.dips[1][1] = Dip::Value( "4", 0x10 ); context.dips[1][2] = Dip::Value( "5", 0x08 ); context.dips[2] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[2][0] = Dip::Value( "Off", 0x00 ); context.dips[2][1] = Dip::Value( "On", 0x20 ); context.dips[3] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[3][0] = Dip::Value( "Off", 0x00 ); context.dips[3][1] = Dip::Value( "On", 0x40 ); context.dips[4] = Dip::Value( "Demo Sounds", 2, 1 ); context.dips[4][0] = Dip::Value( "Off", 0x00 ); context.dips[4][1] = Dip::Value( "On", 0x80 ); context.ppuModel = PPU_RC2C05_01; context.inputMapper = InputMapper::TYPE_3; break; case 0x8850924B: // Tetris context.SetDips(6); context.dips[0] = Dip::Value( "Coinage", 4, 0 ); context.dips[0][0] = Dip::Value( "1 Coin / 1 Credit", 0x00 ); context.dips[0][1] = Dip::Value( "1 Coin / 2 Credits", 0x02 ); context.dips[0][2] = Dip::Value( "2 Coins / 1 Credit", 0x01 ); context.dips[0][3] = Dip::Value( "3 Coins / 1 Credit", 0x03 ); context.dips[1] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[1][0] = Dip::Value( "Off", 0x00 ); context.dips[1][1] = Dip::Value( "On", 0x04 ); context.dips[2] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[2][0] = Dip::Value( "Off", 0x00 ); context.dips[2][1] = Dip::Value( "On", 0x08 ); context.dips[3] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[3][0] = Dip::Value( "Off", 0x00 ); context.dips[3][1] = Dip::Value( "On", 0x10 ); context.dips[4] = Dip::Value( "Palette Color", 3, 2 ); context.dips[4][0] = Dip::Value( "Black", 0x40 ); context.dips[4][1] = Dip::Value( "Green", 0x20 ); context.dips[4][2] = Dip::Value( "Grey", 0x60 ); context.dips[5] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[5][0] = Dip::Value( "Off", 0x00 ); context.dips[5][1] = Dip::Value( "On", 0x80 ); context.inputMapper = InputMapper::TYPE_2; break; case 0x8C0C2DF5: // Top Gun context.SetDips(5); context.dips[0] = Dip::Value( "Coinage", 8, 0 ); context.dips[0][0] = Dip::Value( "1 Coin / 1 Credit", 0x00 ); context.dips[0][1] = Dip::Value( "1 Coin / 2 Credits", 0x04 ); context.dips[0][2] = Dip::Value( "1 Coin / 3 Credits", 0x02 ); context.dips[0][3] = Dip::Value( "2 Coins / 1 Credit", 0x06 ); context.dips[0][4] = Dip::Value( "3 Coins / 1 Credit", 0x01 ); context.dips[0][5] = Dip::Value( "4 Coins / 1 Credit", 0x05 ); context.dips[0][6] = Dip::Value( "5 Coins / 1 Credit", 0x03 ); context.dips[0][7] = Dip::Value( "Free Play", 0x07 ); context.dips[1] = Dip::Value( "Lives per Coin", 2, 0 ); context.dips[1][0] = Dip::Value( "3 - 12 Max", 0x00 ); context.dips[1][1] = Dip::Value( "2 - 9 Max", 0x08 ); context.dips[2] = Dip::Value( "Bonus", 4, 0 ); context.dips[2][0] = Dip::Value( "30k and every 50k", 0x00 ); context.dips[2][1] = Dip::Value( "50k and every 100k", 0x20 ); context.dips[2][2] = Dip::Value( "100k and every 150k", 0x10 ); context.dips[2][3] = Dip::Value( "200k and every 200k", 0x30 ); context.dips[3] = Dip::Value( "Difficulty", 2, 0 ); context.dips[3][0] = Dip::Value( "Normal", 0x00 ); context.dips[3][1] = Dip::Value( "Hard", 0x40 ); context.dips[4] = Dip::Value( "Demo Sounds", 2, 1 ); context.dips[4][0] = Dip::Value( "Off", 0x00 ); context.dips[4][1] = Dip::Value( "On", 0x80 ); context.ppuModel = PPU_RC2C05_04; context.inputMapper = InputMapper::TYPE_1; break; case 0x70901B25: // Slalom context.SetDips(5); context.dips[0] = Dip::Value( "Coinage", 8, 0 ); context.dips[0][0] = Dip::Value( "1 Coin / 1 Credit", 0x00 ); context.dips[0][1] = Dip::Value( "1 Coin / 2 Credits", 0x04 ); context.dips[0][2] = Dip::Value( "1 Coin / 3 Credits", 0x02 ); context.dips[0][3] = Dip::Value( "2 Coins / 1 Credit", 0x06 ); context.dips[0][4] = Dip::Value( "3 Coins / 1 Credit", 0x01 ); context.dips[0][5] = Dip::Value( "4 Coins / 1 Credit", 0x05 ); context.dips[0][6] = Dip::Value( "5 Coins / 1 Credit", 0x03 ); context.dips[0][7] = Dip::Value( "Free Play", 0x07 ); context.dips[1] = Dip::Value( "Freestyle Points", 2, 0 ); context.dips[1][0] = Dip::Value( "Left / Right", 0x00 ); context.dips[1][1] = Dip::Value( "Hold Time", 0x08 ); context.dips[2] = Dip::Value( "Difficulty", 4, 1 ); context.dips[2][0] = Dip::Value( "Easy", 0x00 ); context.dips[2][1] = Dip::Value( "Normal", 0x10 ); context.dips[2][2] = Dip::Value( "Hard", 0x20 ); context.dips[2][3] = Dip::Value( "Hardest", 0x30 ); context.dips[3] = Dip::Value( "Allow Continue", 2, 1 ); context.dips[3][0] = Dip::Value( "No", 0x40 ); context.dips[3][1] = Dip::Value( "Yes", 0x00 ); context.dips[4] = Dip::Value( "Inverted input", 2, 0 ); context.dips[4][0] = Dip::Value( "Off", 0x00 ); context.dips[4][1] = Dip::Value( "On", 0x80 ); context.ppuModel = PPU_RP2C04_0002; context.inputMapper = InputMapper::TYPE_1; break; case 0xCF36261E: // Super Sky Kid context.SetDips(5); context.dips[0] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[0][0] = Dip::Value( "Off", 0x00 ); context.dips[0][1] = Dip::Value( "On", 0x01 ); context.dips[1] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[1][0] = Dip::Value( "Off", 0x00 ); context.dips[1][1] = Dip::Value( "On", 0x02 ); context.dips[2] = Dip::Value( "Lives", 2, 0 ); context.dips[2][0] = Dip::Value( "2", 0x00 ); context.dips[2][1] = Dip::Value( "3", 0x04 ); context.dips[3] = Dip::Value( "Coinage", 4, 0 ); context.dips[3][0] = Dip::Value( "1 Coin / 1 Credit", 0x00 ); context.dips[3][1] = Dip::Value( "1 Coin / 2 Credits", 0x08 ); context.dips[3][2] = Dip::Value( "2 Coins / 1 Credit", 0x10 ); context.dips[3][3] = Dip::Value( "3 Coins / 1 Credit", 0x18 ); context.dips[4] = Dip::Value( "PPU", 5, 0 ); context.dips[4][0] = Dip::Value( "RP2C03", 0x20 ); context.dips[4][1] = Dip::Value( "RP2C04-0001", 0x00 ); context.dips[4][2] = Dip::Value( "RP2C04-0002", 0x40 ); context.dips[4][3] = Dip::Value( "RP2C04-0003", 0x80 ); context.dips[4][4] = Dip::Value( "RP2C04-0004", 0xC0 ); context.inputMapper = InputMapper::TYPE_3; break; case 0xE1AA8214: // Star Luster context.SetDips(6); context.dips[0] = Dip::Value( "Coinage", 4, 0 ); context.dips[0][0] = Dip::Value( "1 Coin / 1 Credit", 0x00 ); context.dips[0][1] = Dip::Value( "1 Coin / 2 Credits", 0x02 ); context.dips[0][2] = Dip::Value( "2 Coins / 1 Credit", 0x01 ); context.dips[0][3] = Dip::Value( "3 Coins / 1 Credit", 0x03 ); context.dips[1] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[1][0] = Dip::Value( "Off", 0x00 ); context.dips[1][1] = Dip::Value( "On", 0x04 ); context.dips[2] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[2][0] = Dip::Value( "Off", 0x00 ); context.dips[2][1] = Dip::Value( "On", 0x08 ); context.dips[3] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[3][0] = Dip::Value( "Off", 0x00 ); context.dips[3][1] = Dip::Value( "On", 0x10 ); context.dips[4] = Dip::Value( "Palette Color", 3, 0 ); context.dips[4][0] = Dip::Value( "Black", 0x40 ); context.dips[4][1] = Dip::Value( "Green", 0x20 ); context.dips[4][2] = Dip::Value( "Grey", 0x60 ); context.dips[5] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[5][0] = Dip::Value( "Off", 0x00 ); context.dips[5][1] = Dip::Value( "On", 0x80 ); context.inputMapper = InputMapper::TYPE_1; break; case 0xD5D7EAC4: // Dr. Mario context.SetDips(5); context.dips[0] = Dip::Value( "Drop Rate Increases After", 4, 0 ); context.dips[0][0] = Dip::Value( "7 Pills", 0x00 ); context.dips[0][1] = Dip::Value( "8 Pills", 0x01 ); context.dips[0][2] = Dip::Value( "9 Pills", 0x02 ); context.dips[0][3] = Dip::Value( "10 Pills", 0x03 ); context.dips[1] = Dip::Value( "Virus Level", 4, 0 ); context.dips[1][0] = Dip::Value( "1", 0x00 ); context.dips[1][1] = Dip::Value( "3", 0x04 ); context.dips[1][2] = Dip::Value( "5", 0x08 ); context.dips[1][3] = Dip::Value( "7", 0x0C ); context.dips[2] = Dip::Value( "Drop Speed Up", 4, 0 ); context.dips[2][0] = Dip::Value( "Slow", 0x00 ); context.dips[2][1] = Dip::Value( "Medium", 0x10 ); context.dips[2][2] = Dip::Value( "Fast", 0x20 ); context.dips[2][3] = Dip::Value( "Fastest", 0x30 ); context.dips[3] = Dip::Value( "Free Play", 2, 0 ); context.dips[3][0] = Dip::Value( "Off", 0x00 ); context.dips[3][1] = Dip::Value( "On", 0x40 ); context.dips[4] = Dip::Value( "Demo Sounds", 2, 1 ); context.dips[4][0] = Dip::Value( "Off", 0x00 ); context.dips[4][1] = Dip::Value( "On", 0x80 ); context.ppuModel = PPU_RP2C04_0003; context.inputMapper = InputMapper::TYPE_2; break; case 0xFFBEF374: // Castlevania context.SetDips(4); context.dips[0] = Dip::Value( "Coinage", 8, 0 ); context.dips[0][0] = Dip::Value( "1 Coin / 1 Credit", 0x00 ); context.dips[0][1] = Dip::Value( "1 Coin / 2 Credits", 0x04 ); context.dips[0][2] = Dip::Value( "1 Coin / 3 Credits", 0x02 ); context.dips[0][3] = Dip::Value( "2 Coins / 1 Credit", 0x06 ); context.dips[0][4] = Dip::Value( "3 Coins / 1 Credit", 0x01 ); context.dips[0][5] = Dip::Value( "4 Coins / 1 Credit", 0x05 ); context.dips[0][6] = Dip::Value( "5 Coins / 1 Credit", 0x03 ); context.dips[0][7] = Dip::Value( "Free Play", 0x07 ); context.dips[1] = Dip::Value( "Lives", 2, 1 ); context.dips[1][0] = Dip::Value( "2", 0x08 ); context.dips[1][1] = Dip::Value( "3", 0x00 ); context.dips[2] = Dip::Value( "Bonus", 4, 0 ); context.dips[2][0] = Dip::Value( "100k", 0x00 ); context.dips[2][1] = Dip::Value( "200k", 0x20 ); context.dips[2][2] = Dip::Value( "300k", 0x10 ); context.dips[2][3] = Dip::Value( "400k", 0x30 ); context.dips[3] = Dip::Value( "Difficulty", 2, 0 ); context.dips[3][0] = Dip::Value( "Normal", 0x00 ); context.dips[3][1] = Dip::Value( "Hard", 0x40 ); context.ppuModel = PPU_RP2C04_0002; context.inputMapper = InputMapper::TYPE_1; break; case 0xE2C0A2BE: // Platoon context.SetDips(6); context.dips[0] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[0][0] = Dip::Value( "Off", 0x00 ); context.dips[0][1] = Dip::Value( "On", 0x01 ); context.dips[1] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[1][0] = Dip::Value( "Off", 0x00 ); context.dips[1][1] = Dip::Value( "On", 0x02 ); context.dips[2] = Dip::Value( "Demo Sounds", 2, 1 ); context.dips[2][0] = Dip::Value( "Off", 0x00 ); context.dips[2][1] = Dip::Value( "On", 0x04 ); context.dips[3] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[3][0] = Dip::Value( "Off", 0x00 ); context.dips[3][1] = Dip::Value( "On", 0x08 ); context.dips[4] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[4][0] = Dip::Value( "Off", 0x00 ); context.dips[4][1] = Dip::Value( "On", 0x10 ); context.dips[5] = Dip::Value( "Coinage", 8, 0 ); context.dips[5][0] = Dip::Value( "1 Coin / 1 Credit", 0x00 ); context.dips[5][1] = Dip::Value( "1 Coin / 2 Credits", 0x20 ); context.dips[5][2] = Dip::Value( "1 Coin / 3 Credits", 0x40 ); context.dips[5][3] = Dip::Value( "2 Coins / 1 Credit", 0x60 ); context.dips[5][4] = Dip::Value( "3 Coins / 1 Credit", 0x80 ); context.dips[5][5] = Dip::Value( "4 Coins / 1 Credit", 0xA0 ); context.dips[5][6] = Dip::Value( "5 Coins / 1 Credit", 0xC0 ); context.dips[5][7] = Dip::Value( "Free Play", 0xE0 ); context.ppuModel = PPU_RP2C04_0001; context.inputMapper = InputMapper::TYPE_1; break; case 0xCBE85490: // Excitebike case 0x29155E0C: // Excitebike (alt) context.SetDips(4); context.dips[0] = Dip::Value( "Coinage", 8, 0 ); context.dips[0][0] = Dip::Value( "1 Coin / 1 Credit", 0x00 ); context.dips[0][1] = Dip::Value( "1 Coin / 2 Credits", 0x04 ); context.dips[0][2] = Dip::Value( "1 Coin / 3 Credits", 0x02 ); context.dips[0][3] = Dip::Value( "1 Coin / 4 Credits", 0x06 ); context.dips[0][4] = Dip::Value( "2 Coins / 1 Credit", 0x01 ); context.dips[0][5] = Dip::Value( "3 Coins / 1 Credit", 0x05 ); context.dips[0][6] = Dip::Value( "4 Coins / 1 Credit", 0x03 ); context.dips[0][7] = Dip::Value( "Free Play", 0x07 ); context.dips[1] = Dip::Value( "Bonus", 4, 0 ); context.dips[1][0] = Dip::Value( "100k and Every 50k", 0x00 ); context.dips[1][1] = Dip::Value( "Every 100k", 0x10 ); context.dips[1][2] = Dip::Value( "100k Only", 0x08 ); context.dips[1][3] = Dip::Value( "None", 0x18 ); context.dips[2] = Dip::Value( "1st Half Qualifying Time", 2, 0 ); context.dips[2][0] = Dip::Value( "Normal", 0x00 ); context.dips[2][1] = Dip::Value( "Hard", 0x20 ); context.dips[3] = Dip::Value( "2nd Half Qualifying Time", 2, 0 ); context.dips[3][0] = Dip::Value( "Normal", 0x00 ); context.dips[3][1] = Dip::Value( "Hard", 0x40 ); if (prgCrc == 0x29155E0C) context.ppuModel = PPU_RP2C04_0004; else context.ppuModel = PPU_RP2C04_0003; context.inputMapper = InputMapper::TYPE_1; break; case 0x07138C06: // Clu Clu Land context.SetDips(5); context.dips[0] = Dip::Value( "Coinage", 8, 0 ); context.dips[0][0] = Dip::Value( "1 Coin / 1 Credit", 0x00 ); context.dips[0][1] = Dip::Value( "1 Coin / 2 Credits", 0x04 ); context.dips[0][2] = Dip::Value( "1 Coin / 3 Credits", 0x02 ); context.dips[0][3] = Dip::Value( "1 Coin / 4 Credits", 0x06 ); context.dips[0][4] = Dip::Value( "2 Coins / 1 Credit", 0x01 ); context.dips[0][5] = Dip::Value( "3 Coins / 1 Credit", 0x05 ); context.dips[0][6] = Dip::Value( "4 Coins / 1 Credit", 0x03 ); context.dips[0][7] = Dip::Value( "Free Play", 0x07 ); context.dips[1] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[1][0] = Dip::Value( "Off", 0x00 ); context.dips[1][1] = Dip::Value( "On", 0x08 ); context.dips[2] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[2][0] = Dip::Value( "Off", 0x00 ); context.dips[2][1] = Dip::Value( "On", 0x10 ); context.dips[3] = Dip::Value( "Lives", 4, 1 ); context.dips[3][0] = Dip::Value( "2", 0x60 ); context.dips[3][1] = Dip::Value( "3", 0x00 ); context.dips[3][2] = Dip::Value( "4", 0x40 ); context.dips[3][3] = Dip::Value( "5", 0x20 ); context.dips[4] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[4][0] = Dip::Value( "Off", 0x00 ); context.dips[4][1] = Dip::Value( "On", 0x80 ); context.ppuModel = PPU_RP2C04_0004; context.inputMapper = InputMapper::TYPE_2; break; case 0x43A357EF: // Ice Climber case 0xD4EB5923: // -||- context.SetDips(4); context.dips[0] = Dip::Value( "Coinage", 8, 0 ); context.dips[0][0] = Dip::Value( "1 Coin / 1 Credit", 0x00 ); context.dips[0][1] = Dip::Value( "1 Coin / 2 Credits", 0x04 ); context.dips[0][2] = Dip::Value( "1 Coin / 3 Credits", 0x02 ); context.dips[0][3] = Dip::Value( "1 Coin / 4 Credits", 0x06 ); context.dips[0][4] = Dip::Value( "2 Coins / 1 Credit", 0x01 ); context.dips[0][5] = Dip::Value( "3 Coins / 1 Credit", 0x05 ); context.dips[0][6] = Dip::Value( "4 Coins / 1 Credit", 0x03 ); context.dips[0][7] = Dip::Value( "Free Play", 0x07 ); context.dips[1] = Dip::Value( "Lives", 4, 0 ); context.dips[1][0] = Dip::Value( "3", 0x00 ); context.dips[1][1] = Dip::Value( "4", 0x10 ); context.dips[1][2] = Dip::Value( "5", 0x08 ); context.dips[1][3] = Dip::Value( "7", 0x18 ); context.dips[2] = Dip::Value( "Difficulty", 2, 0 ); context.dips[2][0] = Dip::Value( "Normal", 0x00 ); context.dips[2][1] = Dip::Value( "Hard", 0x20 ); context.dips[3] = Dip::Value( "Time before the bear", 2, 0 ); context.dips[3][0] = Dip::Value( "Long", 0x00 ); context.dips[3][1] = Dip::Value( "Short", 0x40 ); context.ppuModel = PPU_RP2C04_0004; if (prgCrc == 0x43A357EF) context.inputMapper = InputMapper::TYPE_2; else context.inputMapper = InputMapper::TYPE_4; break; case 0x737DD1BF: // Super Mario Bros case 0x4BF3972D: // -||- case 0x8B60CC58: // -||- case 0x8192C804: // -||- context.SetDips(5); context.dips[0] = Dip::Value( "Coinage", 8, 0 ); context.dips[0][0] = Dip::Value( "1 Coin / 1 Credit", 0x00 ); context.dips[0][1] = Dip::Value( "1 Coin / 2 Credits", 0x06 ); context.dips[0][2] = Dip::Value( "1 Coin / 3 Credits", 0x01 ); context.dips[0][3] = Dip::Value( "1 Coin / 4 Credits", 0x05 ); context.dips[0][4] = Dip::Value( "1 Coin / 5 Credits", 0x03 ); context.dips[0][5] = Dip::Value( "2 Coins / 1 Credit", 0x04 ); context.dips[0][6] = Dip::Value( "3 Coins / 1 Credit", 0x02 ); context.dips[0][7] = Dip::Value( "Free Play", 0x07 ); context.dips[1] = Dip::Value( "Lives", 2, 1 ); context.dips[1][0] = Dip::Value( "2", 0x08 ); context.dips[1][1] = Dip::Value( "3", 0x00 ); context.dips[2] = Dip::Value( "Bonus Life", 4, 0 ); context.dips[2][0] = Dip::Value( "100", 0x00 ); context.dips[2][1] = Dip::Value( "150", 0x20 ); context.dips[2][2] = Dip::Value( "200", 0x10 ); context.dips[2][3] = Dip::Value( "250", 0x30 ); context.dips[3] = Dip::Value( "Timer", 2, 0 ); context.dips[3][0] = Dip::Value( "Normal", 0x00 ); context.dips[3][1] = Dip::Value( "Fast", 0x40 ); context.dips[4] = Dip::Value( "Continue Lives", 2, 0 ); context.dips[4][0] = Dip::Value( "3", 0x80 ); context.dips[4][1] = Dip::Value( "4", 0x00 ); context.ppuModel = PPU_RP2C04_0004; context.inputMapper = InputMapper::TYPE_1; break; case 0xEC461DB9: // Pinball case 0xE528F651: // -||- (alt) context.SetDips(5); context.dips[0] = Dip::Value( "Coinage", 8, 0 ); context.dips[0][0] = Dip::Value( "1 Coin / 1 Credit", 0x01 ); context.dips[0][1] = Dip::Value( "1 Coin / 2 Credits", 0x06 ); context.dips[0][2] = Dip::Value( "1 Coin / 3 Credits", 0x02 ); context.dips[0][3] = Dip::Value( "1 Coin / 4 Credits", 0x04 ); context.dips[0][4] = Dip::Value( "2 Coins / 1 Credit", 0x05 ); context.dips[0][5] = Dip::Value( "3 Coins / 1 Credit", 0x03 ); context.dips[0][6] = Dip::Value( "4 Coins / 1 Credit", 0x07 ); context.dips[0][7] = Dip::Value( "Free Play", 0x00 ); context.dips[1] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[1][0] = Dip::Value( "Off", 0x00 ); context.dips[1][1] = Dip::Value( "On", 0x08 ); context.dips[2] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[2][0] = Dip::Value( "Off", 0x00 ); context.dips[2][1] = Dip::Value( "On", 0x10 ); context.dips[3] = Dip::Value( "Balls", 4, 1 ); context.dips[3][0] = Dip::Value( "2", 0x60 ); context.dips[3][1] = Dip::Value( "3", 0x00 ); context.dips[3][2] = Dip::Value( "4", 0x40 ); context.dips[3][3] = Dip::Value( "5", 0x20 ); context.dips[4] = Dip::Value( "Ball Speed", 2, 0 ); context.dips[4][0] = Dip::Value( "Normal", 0x00 ); context.dips[4][1] = Dip::Value( "Fast", 0x80 ); if (prgCrc == 0xEC461DB9) { context.ppuModel = PPU_RP2C04_0001; context.inputMapper = InputMapper::TYPE_1; } else { context.inputMapper = InputMapper::TYPE_5; } break; case 0xAE8063EF: // Mach Rider - Fighting Course context.SetDips(5); context.dips[0] = Dip::Value( "Coinage", 8, 0 ); context.dips[0][0] = Dip::Value( "1 Coin / 1 Credit", 0x00 ); context.dips[0][1] = Dip::Value( "1 Coin / 2 Credits", 0x04 ); context.dips[0][2] = Dip::Value( "1 Coin / 3 Credits", 0x02 ); context.dips[0][3] = Dip::Value( "1 Coin / 4 Credits", 0x06 ); context.dips[0][4] = Dip::Value( "2 Coins / 1 Credit", 0x01 ); context.dips[0][5] = Dip::Value( "3 Coins / 1 Credit", 0x05 ); context.dips[0][6] = Dip::Value( "4 Coins / 1 Credit", 0x03 ); context.dips[0][7] = Dip::Value( "Free Play", 0x07 ); context.dips[1] = Dip::Value( "Km 1st Race", 2, 0 ); context.dips[1][0] = Dip::Value( "12", 0x00 ); context.dips[1][1] = Dip::Value( "15", 0x10 ); context.dips[2] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[2][0] = Dip::Value( "Off", 0x00 ); context.dips[2][1] = Dip::Value( "On", 0x20 ); context.dips[3] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[3][0] = Dip::Value( "Off", 0x00 ); context.dips[3][1] = Dip::Value( "On", 0x40 ); context.dips[4] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[4][0] = Dip::Value( "Off", 0x00 ); context.dips[4][1] = Dip::Value( "On", 0x80 ); context.ppuModel = PPU_RP2C04_0001; context.inputMapper = InputMapper::TYPE_1; break; case 0x0B65A917: // Mach Rider case 0x8A6A9848: // -||- context.SetDips(5); context.dips[0] = Dip::Value( "Coinage", 8, 0 ); context.dips[0][0] = Dip::Value( "1 Coin / 1 Credit", 0x00 ); context.dips[0][1] = Dip::Value( "1 Coin / 2 Credits", 0x04 ); context.dips[0][2] = Dip::Value( "1 Coin / 3 Credits", 0x02 ); context.dips[0][3] = Dip::Value( "1 Coin / 4 Credits", 0x06 ); context.dips[0][4] = Dip::Value( "2 Coins / 1 Credit", 0x01 ); context.dips[0][5] = Dip::Value( "3 Coins / 1 Credit", 0x05 ); context.dips[0][6] = Dip::Value( "4 Coins / 1 Credit", 0x03 ); context.dips[0][7] = Dip::Value( "Free Play", 0x07 ); context.dips[1] = Dip::Value( "Time", 4, 0 ); context.dips[1][0] = Dip::Value( "280", 0x00 ); context.dips[1][1] = Dip::Value( "250", 0x10 ); context.dips[1][2] = Dip::Value( "220", 0x08 ); context.dips[1][3] = Dip::Value( "200", 0x18 ); context.dips[2] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[2][0] = Dip::Value( "Off", 0x00 ); context.dips[2][1] = Dip::Value( "On", 0x20 ); context.dips[3] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[3][0] = Dip::Value( "Off", 0x00 ); context.dips[3][1] = Dip::Value( "On", 0x40 ); context.dips[4] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[4][0] = Dip::Value( "Off", 0x00 ); context.dips[4][1] = Dip::Value( "On", 0x80 ); context.ppuModel = PPU_RP2C04_0002; context.inputMapper = InputMapper::TYPE_1; break; case 0x46914E3E: // Soccer context.SetDips(3); context.dips[0] = Dip::Value( "Coinage", 8, 0 ); context.dips[0][0] = Dip::Value( "1 Coin / 1 Credit", 0x00 ); context.dips[0][1] = Dip::Value( "1 Coin / 2 Credits", 0x04 ); context.dips[0][2] = Dip::Value( "1 Coin / 3 Credits", 0x02 ); context.dips[0][3] = Dip::Value( "1 Coin / 4 Credits", 0x06 ); context.dips[0][4] = Dip::Value( "2 Coins / 1 Credit", 0x01 ); context.dips[0][5] = Dip::Value( "3 Coins / 1 Credit", 0x05 ); context.dips[0][6] = Dip::Value( "4 Coins / 1 Credit", 0x03 ); context.dips[0][7] = Dip::Value( "Free Play", 0x07 ); context.dips[1] = Dip::Value( "Points Timer", 4, 2 ); context.dips[1][0] = Dip::Value( "600 Pts", 0x00 ); context.dips[1][1] = Dip::Value( "800 Pts", 0x10 ); context.dips[1][2] = Dip::Value( "1000 Pts", 0x08 ); context.dips[1][3] = Dip::Value( "1200 Pts", 0x18 ); context.dips[2] = Dip::Value( "Difficulty", 4, 1 ); context.dips[2][0] = Dip::Value( "Easy", 0x00 ); context.dips[2][1] = Dip::Value( "Normal", 0x40 ); context.dips[2][2] = Dip::Value( "Hard", 0x20 ); context.dips[2][3] = Dip::Value( "Very Hard", 0x60 ); context.ppuModel = PPU_RP2C04_0003; context.inputMapper = InputMapper::TYPE_2; break; case 0x70433F2C: // Battle City context.SetDips(7); context.dips[0] = Dip::Value( "Credits for 2 Players", 2, 1 ); context.dips[0][0] = Dip::Value( "1", 0x00 ); context.dips[0][1] = Dip::Value( "2", 0x01 ); context.dips[1] = Dip::Value( "Lives", 2, 0 ); context.dips[1][0] = Dip::Value( "3", 0x00 ); context.dips[1][1] = Dip::Value( "5", 0x02 ); context.dips[2] = Dip::Value( "Demo Sounds", 2, 1 ); context.dips[2][0] = Dip::Value( "Off", 0x00 ); context.dips[2][1] = Dip::Value( "On", 0x04 ); context.dips[3] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[3][0] = Dip::Value( "Off", 0x00 ); context.dips[3][1] = Dip::Value( "On", 0x08 ); context.dips[4] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[4][0] = Dip::Value( "Off", 0x00 ); context.dips[4][1] = Dip::Value( "On", 0x10 ); context.dips[5] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[5][0] = Dip::Value( "Off", 0x00 ); context.dips[5][1] = Dip::Value( "On", 0x20 ); context.dips[6] = Dip::Value( "PPU", 4, 0 ); context.dips[6][0] = Dip::Value( "RP2C04-0001", 0x00 ); context.dips[6][1] = Dip::Value( "RP2C04-0002", 0x40 ); context.dips[6][2] = Dip::Value( "RP2C04-0003", 0x80 ); context.dips[6][3] = Dip::Value( "RP2C04-0004", 0xC0 ); context.ppuModel = PPU_RP2C04_0001; context.inputMapper = InputMapper::TYPE_2; break; case 0xD99A2087: // Gradius context.SetDips(5); context.dips[0] = Dip::Value( "Coinage", 8, 0 ); context.dips[0][0] = Dip::Value( "1 Coin / 1 Credit", 0x00 ); context.dips[0][1] = Dip::Value( "1 Coin / 2 Credits", 0x04 ); context.dips[0][2] = Dip::Value( "1 Coin / 3 Credits", 0x02 ); context.dips[0][3] = Dip::Value( "2 Coins / 1 Credit", 0x06 ); context.dips[0][4] = Dip::Value( "3 Coins / 1 Credit", 0x01 ); context.dips[0][5] = Dip::Value( "4 Coins / 1 Credit", 0x05 ); context.dips[0][6] = Dip::Value( "5 Coins / 1 Credit", 0x03 ); context.dips[0][7] = Dip::Value( "Free Play", 0x07 ); context.dips[1] = Dip::Value( "Lives", 2, 0 ); context.dips[1][0] = Dip::Value( "3", 0x08 ); context.dips[1][1] = Dip::Value( "4", 0x00 ); context.dips[2] = Dip::Value( "Bonus", 4, 0 ); context.dips[2][0] = Dip::Value( "100k", 0x00 ); context.dips[2][1] = Dip::Value( "200k", 0x20 ); context.dips[2][2] = Dip::Value( "300k", 0x10 ); context.dips[2][3] = Dip::Value( "400k", 0x30 ); context.dips[3] = Dip::Value( "Difficulty", 2, 0 ); context.dips[3][0] = Dip::Value( "Normal", 0x00 ); context.dips[3][1] = Dip::Value( "Hard", 0x40 ); context.dips[4] = Dip::Value( "Demo Sounds", 2, 1 ); context.dips[4][0] = Dip::Value( "Off", 0x00 ); context.dips[4][1] = Dip::Value( "On", 0x80 ); context.ppuModel = PPU_RP2C04_0001; context.inputMapper = InputMapper::TYPE_2; break; case 0x1E438D52: // Goonies context.SetDips(6); context.dips[0] = Dip::Value( "Coinage", 8, 0 ); context.dips[0][0] = Dip::Value( "1 Coin / 1 Credit", 0x00 ); context.dips[0][1] = Dip::Value( "1 Coin / 2 Credits", 0x04 ); context.dips[0][2] = Dip::Value( "1 Coin / 3 Credits", 0x02 ); context.dips[0][3] = Dip::Value( "2 Coins / 1 Credit", 0x06 ); context.dips[0][4] = Dip::Value( "3 Coins / 1 Credit", 0x01 ); context.dips[0][5] = Dip::Value( "4 Coins / 1 Credit", 0x05 ); context.dips[0][6] = Dip::Value( "5 Coins / 1 Credit", 0x03 ); context.dips[0][7] = Dip::Value( "Free Play", 0x07 ); context.dips[1] = Dip::Value( "Lives", 2, 0 ); context.dips[1][0] = Dip::Value( "3", 0x00 ); context.dips[1][1] = Dip::Value( "2", 0x08 ); context.dips[2] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[2][0] = Dip::Value( "Off", 0x00 ); context.dips[2][1] = Dip::Value( "On", 0x10 ); context.dips[3] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[3][0] = Dip::Value( "Off", 0x00 ); context.dips[3][1] = Dip::Value( "On", 0x20 ); context.dips[4] = Dip::Value( "Timer", 2, 0 ); context.dips[4][0] = Dip::Value( "Normal", 0x00 ); context.dips[4][1] = Dip::Value( "Fast", 0x40 ); context.dips[5] = Dip::Value( "Demo Sounds", 2, 1 ); context.dips[5][0] = Dip::Value( "Off", 0x00 ); context.dips[5][1] = Dip::Value( "On", 0x80 ); context.ppuModel = PPU_RP2C04_0003; context.inputMapper = InputMapper::TYPE_1; break; case 0xFF5135A3: // Hogan's Alley context.SetDips(4); context.dips[0] = Dip::Value( "Coinage", 8, 0 ); context.dips[0][0] = Dip::Value( "1 Coin / 1 Credit", 0x00 ); context.dips[0][1] = Dip::Value( "1 Coin / 2 Credits", 0x04 ); context.dips[0][2] = Dip::Value( "1 Coin / 3 Credits", 0x02 ); context.dips[0][3] = Dip::Value( "2 Coins / 1 Credit", 0x06 ); context.dips[0][4] = Dip::Value( "3 Coins / 1 Credit", 0x01 ); context.dips[0][5] = Dip::Value( "4 Coins / 1 Credit", 0x05 ); context.dips[0][6] = Dip::Value( "5 Coins / 1 Credit", 0x03 ); context.dips[0][7] = Dip::Value( "Free Play", 0x07 ); context.dips[1] = Dip::Value( "Difficulty", 4, 1 ); context.dips[1][0] = Dip::Value( "Easy", 0x00 ); context.dips[1][1] = Dip::Value( "Normal", 0x08 ); context.dips[1][2] = Dip::Value( "Hard", 0x10 ); context.dips[1][3] = Dip::Value( "Very Hard", 0x18 ); context.dips[2] = Dip::Value( "Misses per Game", 2, 1 ); context.dips[2][0] = Dip::Value( "3", 0x00 ); context.dips[2][1] = Dip::Value( "5", 0x20 ); context.dips[3] = Dip::Value( "Bonus Life", 4, 0 ); context.dips[3][0] = Dip::Value( "30000", 0x00 ); context.dips[3][1] = Dip::Value( "50000", 0x40 ); context.dips[3][2] = Dip::Value( "80000", 0x80 ); context.dips[3][3] = Dip::Value( "100000", 0xC0 ); context.ppuModel = PPU_RP2C04_0001; break; case 0x17AE56BE: // Freedom Force context.SetDips(6); context.dips[0] = Dip::Value( "Coinage", 8, 0 ); context.dips[0][0] = Dip::Value( "1 Coin / 1 Credit", 0x00 ); context.dips[0][1] = Dip::Value( "1 Coin / 2 Credits", 0x04 ); context.dips[0][2] = Dip::Value( "1 Coin / 3 Credits", 0x02 ); context.dips[0][3] = Dip::Value( "2 Coins / 1 Credit", 0x06 ); context.dips[0][4] = Dip::Value( "3 Coins / 1 Credit", 0x01 ); context.dips[0][5] = Dip::Value( "4 Coins / 1 Credit", 0x05 ); context.dips[0][6] = Dip::Value( "5 Coins / 1 Credit", 0x03 ); context.dips[0][7] = Dip::Value( "Free Play", 0x07 ); context.dips[1] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[1][0] = Dip::Value( "Off", 0x00 ); context.dips[1][1] = Dip::Value( "On", 0x08 ); context.dips[2] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[2][0] = Dip::Value( "Off", 0x00 ); context.dips[2][1] = Dip::Value( "On", 0x10 ); context.dips[3] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[3][0] = Dip::Value( "Off", 0x00 ); context.dips[3][1] = Dip::Value( "On", 0x20 ); context.dips[4] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[4][0] = Dip::Value( "Off", 0x00 ); context.dips[4][1] = Dip::Value( "On", 0x40 ); context.dips[5] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[5][0] = Dip::Value( "Off", 0x00 ); context.dips[5][1] = Dip::Value( "On", 0x80 ); context.ppuModel = PPU_RP2C04_0001; break; case 0xC99EC059: // Raid on Bungeling Bay context.SetDips(6); context.dips[0] = Dip::Value( "Coinage", 8, 0 ); context.dips[0][0] = Dip::Value( "1 Coin / 1 Credit", 0x00 ); context.dips[0][1] = Dip::Value( "1 Coin / 2 Credits", 0x04 ); context.dips[0][2] = Dip::Value( "1 Coin / 3 Credits", 0x02 ); context.dips[0][3] = Dip::Value( "1 Coin / 4 Credits", 0x06 ); context.dips[0][4] = Dip::Value( "2 Coins / 1 Credit", 0x01 ); context.dips[0][5] = Dip::Value( "3 Coins / 1 Credit", 0x05 ); context.dips[0][6] = Dip::Value( "4 Coins / 1 Credit", 0x03 ); context.dips[0][7] = Dip::Value( "Free Play", 0x07 ); context.dips[1] = Dip::Value( "Lives", 2, 0 ); context.dips[1][0] = Dip::Value( "2", 0x00 ); context.dips[1][1] = Dip::Value( "3", 0x08 ); context.dips[2] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[2][0] = Dip::Value( "Off", 0x00 ); context.dips[2][1] = Dip::Value( "On", 0x10 ); context.dips[3] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[3][0] = Dip::Value( "Off", 0x00 ); context.dips[3][1] = Dip::Value( "On", 0x20 ); context.dips[4] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[4][0] = Dip::Value( "Off", 0x00 ); context.dips[4][1] = Dip::Value( "On", 0x40 ); context.dips[5] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[5][0] = Dip::Value( "Off", 0x00 ); context.dips[5][1] = Dip::Value( "On", 0x80 ); context.ppuModel = PPU_RP2C04_0002; context.inputMapper = InputMapper::TYPE_4; break; case 0xF9D3B0A3: // Super Xevious case 0x66BB838F: // -||- case 0x9924980A: // -||- context.SetDips(6); context.dips[0] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[0][0] = Dip::Value( "Off", 0x00 ); context.dips[0][1] = Dip::Value( "On", 0x01 ); context.dips[1] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[1][0] = Dip::Value( "Off", 0x00 ); context.dips[1][1] = Dip::Value( "On", 0x02 ); context.dips[2] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[2][0] = Dip::Value( "Off", 0x00 ); context.dips[2][1] = Dip::Value( "On", 0x04 ); context.dips[3] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[3][0] = Dip::Value( "Off", 0x00 ); context.dips[3][1] = Dip::Value( "On", 0x08 ); context.dips[4] = Dip::Value( "Coinage", 4, 0 ); context.dips[4][0] = Dip::Value( "1 Coin / 1 Credit", 0x00 ); context.dips[4][1] = Dip::Value( "1 Coin / 2 Credits", 0x10 ); context.dips[4][2] = Dip::Value( "2 Coins / 1 Credit", 0x20 ); context.dips[4][3] = Dip::Value( "3 Coins / 1 Credit", 0x30 ); context.dips[5] = Dip::Value( "PPU", 4, 0 ); context.dips[5][0] = Dip::Value( "RP2C04-0001", 0x00 ); context.dips[5][1] = Dip::Value( "RP2C04-0002", 0x40 ); context.dips[5][2] = Dip::Value( "RP2C04-0003", 0x80 ); context.dips[5][3] = Dip::Value( "RP2C04-0004", 0xC0 ); context.inputMapper = InputMapper::TYPE_1; context.ppuModel = PPU_RP2C04_0001; context.mode = MODE_XEV; break; case 0xCC2C4B5D: // Golf (J) case 0x86167220: // Lady Golf context.ppuModel = PPU_RP2C04_0002; // fallthrough case 0xA93A5AEE: // Golf context.SetDips(5); context.dips[0] = Dip::Value( "Coinage", 8, 0 ); context.dips[0][0] = Dip::Value( "1 Coin / 1 Credit", 0x01 ); context.dips[0][1] = Dip::Value( "1 Coin / 2 Credits", 0x06 ); context.dips[0][2] = Dip::Value( "1 Coin / 3 Credits", 0x02 ); context.dips[0][3] = Dip::Value( "1 Coin / 4 Credits", 0x04 ); context.dips[0][4] = Dip::Value( "2 Coins / 1 Credit", 0x05 ); context.dips[0][5] = Dip::Value( "3 Coins / 1 Credit", 0x03 ); context.dips[0][6] = Dip::Value( "4 Coins / 1 Credit", 0x07 ); context.dips[0][7] = Dip::Value( "Free Play", 0x00 ); context.dips[1] = Dip::Value( "Hole Size", 2, 0 ); context.dips[1][0] = Dip::Value( "Large", 0x00 ); context.dips[1][1] = Dip::Value( "Small", 0x08 ); context.dips[2] = Dip::Value( "Points per Stroke", 2, 0 ); context.dips[2][0] = Dip::Value( "Easier", 0x00 ); context.dips[2][1] = Dip::Value( "Harder", 0x10 ); context.dips[3] = Dip::Value( "Starting Points", 4, 0 ); context.dips[3][0] = Dip::Value( "10", 0x00 ); context.dips[3][1] = Dip::Value( "13", 0x40 ); context.dips[3][2] = Dip::Value( "16", 0x20 ); context.dips[3][3] = Dip::Value( "20", 0x60 ); context.dips[4] = Dip::Value( "Difficulty Vs. Computer", 2, 0 ); context.dips[4][0] = Dip::Value( "Easy", 0x00 ); context.dips[4][1] = Dip::Value( "Hard", 0x80 ); context.inputMapper = InputMapper::TYPE_2; break; case 0xCA85E56D: // Mighty Bomb Jack context.SetDips(5); context.dips[0] = Dip::Value( "Coinage", 8, 0 ); context.dips[0][0] = Dip::Value( "1 Coin / 1 Credit", 0x00 ); context.dips[0][1] = Dip::Value( "1 Coin / 2 Credits", 0x04 ); context.dips[0][2] = Dip::Value( "1 Coin / 3 Credits", 0x02 ); context.dips[0][3] = Dip::Value( "1 Coin / 4 Credits", 0x06 ); context.dips[0][4] = Dip::Value( "2 Coins / 1 Credit", 0x01 ); context.dips[0][5] = Dip::Value( "3 Coins / 1 Credit", 0x05 ); context.dips[0][6] = Dip::Value( "4 Coins / 1 Credit", 0x03 ); context.dips[0][7] = Dip::Value( "5 Coins / 1 Credit", 0x07 ); context.dips[1] = Dip::Value( "Lives", 4, 0 ); context.dips[1][0] = Dip::Value( "2", 0x10 ); context.dips[1][1] = Dip::Value( "3", 0x00 ); context.dips[1][2] = Dip::Value( "4", 0x08 ); context.dips[1][3] = Dip::Value( "5", 0x18 ); context.dips[2] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[2][0] = Dip::Value( "Off", 0x00 ); context.dips[2][1] = Dip::Value( "On", 0x20 ); context.dips[3] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[3][0] = Dip::Value( "Off", 0x00 ); context.dips[3][1] = Dip::Value( "On", 0x40 ); context.dips[4] = Dip::Value( "Unknown/Unused", 2, 0 ); context.dips[4][0] = Dip::Value( "Off", 0x00 ); context.dips[4][1] = Dip::Value( "On", 0x80 ); context.ppuModel = PPU_RC2C05_02; context.inputMapper = InputMapper::TYPE_1; break; case 0xFE446787: // Gumshoe context.SetDips(5); context.dips[0] = Dip::Value( "Coinage", 8, 0 ); context.dips[0][0] = Dip::Value( "1 Coin / 1 Credit", 0x00 ); context.dips[0][1] = Dip::Value( "1 Coin / 2 Credits", 0x04 ); context.dips[0][2] = Dip::Value( "1 Coin / 3 Credits", 0x02 ); context.dips[0][3] = Dip::Value( "2 Coins / 1 Credit", 0x06 ); context.dips[0][4] = Dip::Value( "3 Coins / 1 Credit", 0x01 ); context.dips[0][5] = Dip::Value( "4 Coins / 1 Credit", 0x05 ); context.dips[0][6] = Dip::Value( "5 Coins / 1 Credit", 0x03 ); context.dips[0][7] = Dip::Value( "Free Play", 0x07 ); context.dips[1] = Dip::Value( "Difficulty", 4, 1 ); context.dips[1][0] = Dip::Value( "Easy", 0x00 ); context.dips[1][1] = Dip::Value( "Normal", 0x08 ); context.dips[1][2] = Dip::Value( "Hard", 0x10 ); context.dips[1][3] = Dip::Value( "Very Hard", 0x18 ); context.dips[2] = Dip::Value( "Lives", 2, 1 ); context.dips[2][0] = Dip::Value( "3", 0x10 ); context.dips[2][1] = Dip::Value( "5", 0x00 ); context.dips[3] = Dip::Value( "Bullets per Balloon", 2, 1 ); context.dips[3][0] = Dip::Value( "2", 0x40 ); context.dips[3][1] = Dip::Value( "3", 0x00 ); context.dips[4] = Dip::Value( "Bonus Life", 2, 0 ); context.dips[4][0] = Dip::Value( "80000", 0x00 ); context.dips[4][1] = Dip::Value( "100000", 0x80 ); context.ppuModel = PPU_RC2C05_03; break; default: context.SetDips(8); for (uint i=0; i < 8; ++i) { context.dips[i] = Dip::Value( "Unknown", 2, 0 ); context.dips[i][0] = Dip::Value( "Off", 0x00 ); context.dips[i][1] = Dip::Value( "On", 1U << i ); } if (ppuModel != PPU_RP2C02) context.ppuModel = ppuModel; break; } switch (context.mode) { case MODE_RBI: return new RbiBaseball ( context ); case MODE_TKO: return new TkoBoxing ( context ); case MODE_XEV: return new SuperXevious ( context ); default: return new VsSystem ( context ); } } catch (...) { delete [] context.dips; throw; } } void Cartridge::VsSystem::Destroy(Cartridge::VsSystem* vsSystem) { delete vsSystem; } #ifdef NST_MSVC_OPTIMIZE #pragma optimize("", on) #endif struct Cartridge::VsSystem::InputMapper::Type1 : InputMapper { void Fix(Pad (&pads)[4],const uint (&ports)[2]) const { const uint p[2] = { ports[0] < 4 ? pads[ports[0]].buttons : 0, ports[1] < 4 ? pads[ports[1]].buttons : 0 }; for (uint i=2; i--; ) { if (ports[i] < 4) pads[ports[i]].buttons = (p[i] & ~uint(Pad::SELECT|Pad::START)) | ((p[i] & Pad::SELECT) << 1) | ((p[i] & Pad::START) >> 1); } } }; struct Cartridge::VsSystem::InputMapper::Type2 : InputMapper { void Fix(Pad (&pads)[4],const uint (&ports)[2]) const { const uint p[2] = { ports[0] < 4 ? pads[ports[0]].buttons : 0, ports[1] < 4 ? pads[ports[1]].buttons : 0 }; for (uint i=2; i--; ) { if (ports[i] < 4) pads[ports[i]].buttons = (p[i^1] & ~uint(Pad::SELECT|Pad::START)) | ((p[i^0] & Pad::SELECT) << 1) | ((p[i^0] & Pad::START) >> 1); } } }; struct Cartridge::VsSystem::InputMapper::Type3 : InputMapper { void Fix(Pad (&pads)[4],const uint (&ports)[2]) const { const uint p[2] = { ports[0] < 4 ? pads[ports[0]].buttons : 0, ports[1] < 4 ? pads[ports[1]].buttons : 0 }; if (ports[1] < 4) pads[ports[1]].buttons = p[0] & ~uint(Pad::SELECT|Pad::START); if (ports[0] < 4) pads[ports[0]].buttons = (p[1] & ~uint(Pad::SELECT|Pad::START)) | ((p[0] & Pad::START) >> 1) | (p[1] & Pad::START); } }; struct Cartridge::VsSystem::InputMapper::Type4 : InputMapper { void Fix(Pad (&pads)[4],const uint (&ports)[2]) const { const uint p[2] = { ports[0] < 4 ? pads[ports[0]].buttons : 0, ports[1] < 4 ? pads[ports[1]].buttons : 0 }; for (uint i=2; i--; ) { if (ports[i] < 4) pads[ports[i]].buttons = (p[i^1] & ~uint(Pad::SELECT|Pad::START)) | (((p[i^0] & Pad::SELECT) ^ Pad::SELECT) << 1) | ((p[i^0] & Pad::START) >> 1); } } }; struct Cartridge::VsSystem::InputMapper::Type5 : InputMapper { void Fix(Pad (&pads)[4],const uint (&ports)[2]) const { const uint p[2] = { ports[0] < 4 ? pads[ports[0]].buttons : 0, ports[1] < 4 ? pads[ports[1]].buttons : 0 }; if (ports[1] < 4) pads[ports[1]].buttons = (p[1] & ~uint(Pad::A|Pad::SELECT|Pad::START)) | ((p[0] & Pad::B) >> 1) | ((p[1] & Pad::SELECT) << 1) | ((p[1] & Pad::START) >> 1); if (ports[0] < 4) pads[ports[0]].buttons = (p[0] & ~uint(Pad::B|Pad::SELECT|Pad::START)) | ((p[1] & Pad::A) << 1) | ((p[0] & Pad::SELECT) << 1) | ((p[0] & Pad::START) >> 1); } }; void Cartridge::VsSystem::InputMapper::Begin(const Api::Input input,Input::Controllers* const controllers) { Input::Controllers::Pad::callback.Get( userCallback, userData ); if (controllers) { uint ports[2]; for (uint i=0; i < 2; ++i) { ports[i] = input.GetConnectedController(i) - Api::Input::PAD1; if (ports[i] < 4) Input::Controllers::Pad::callback( controllers->pad[ports[i]], ports[i] ); } Input::Controllers::Pad::callback.Set( NULL, NULL ); Fix( controllers->pad, ports ); } } void Cartridge::VsSystem::InputMapper::End() const { Input::Controllers::Pad::callback.Set( userCallback, userData ); } #ifdef NST_MSVC_OPTIMIZE #pragma optimize("s", on) #endif Cartridge::VsSystem::InputMapper* Cartridge::VsSystem::InputMapper::Create(Type type) { switch (type) { case TYPE_1: return new Type1; case TYPE_2: return new Type2; case TYPE_3: return new Type3; case TYPE_4: return new Type4; case TYPE_5: return new Type5; case TYPE_NONE: default: break; } return NULL; } Cartridge::VsSystem::VsSystem(Context& context) : cpu (context.cpu), ppu (context.ppu), inputMapper (InputMapper::Create( context.inputMapper )), dips (context.dips,context.numDips), ppuModel (context.ppuModel) { } Cartridge::VsSystem::~VsSystem() { delete inputMapper; } void Cartridge::VsSystem::Reset(bool) { dips.Reset(); coin = 0; p4016 = cpu.Map( 0x4016 ); p4017 = cpu.Map( 0x4017 ); cpu.Map( 0x4016 ).Set( this, &VsSystem::Peek_4016, &VsSystem::Poke_4016 ); cpu.Map( 0x4017 ).Set( this, &VsSystem::Peek_4017, &VsSystem::Poke_4017 ); cpu.Map( 0x4020 ).Set( this, &VsSystem::Peek_4020, &VsSystem::Poke_4020 ); cpu.Map( 0x5000, 0x5FFF ).Set( this, &VsSystem::Peek_Nop, &VsSystem::Poke_Nop ); Reset(); } void Cartridge::VsSystem::SaveState(State::Saver& state,const dword baseChunk) const { state.Begin( baseChunk ); state.Write8( coin ); SubSave( state ); state.End(); } void Cartridge::VsSystem::LoadState(State::Loader& state) { coin = state.Read8(); while (const dword chunk = state.Begin()) { SubLoad( state, chunk ); state.End(); } } #ifdef NST_MSVC_OPTIMIZE #pragma optimize("", on) #endif NES_PEEK_A(Cartridge::VsSystem,Nop) { return address >> 8; } NES_POKE(Cartridge::VsSystem,Nop) { } NES_PEEK_A(Cartridge::VsSystem,4016) { return dips.Reg(0) | (p4016.Peek( address ) & (STATUS_4016_MASK^0xFFU)); } NES_POKE_AD(Cartridge::VsSystem,4016) { p4016.Poke( address, data ); } NES_PEEK_A(Cartridge::VsSystem,4017) { return dips.Reg(1) | (p4017.Peek( address ) & (STATUS_4017_MASK^0xFFU)); } NES_POKE_AD(Cartridge::VsSystem,4017) { p4017.Poke( address, data ); } NES_PEEK(Cartridge::VsSystem,4020) { return coin; } NES_POKE_D(Cartridge::VsSystem,4020) { coin = data; } } }