//////////////////////////////////////////////////////////////////////////////////////// // // 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( 0x1F ); prg.SwapBanks( regs.prg[0], regs.prg[1] ); prg.SwapBank( regs.prg[2] ); } else { wrk.SwapBank( regs.prg[3] ); prg.SwapBank( regs.prg[4] & 0x3FU ); prg.SwapBank( (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( (address & 0x7) << 10, (regs.prg[4] << 4 & 0x300U) | data ); } NES_POKE_AD(Standard,8310_1) { ppu.Update(); chr.SwapBank( (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 ); } } } } }