1 //////////////////////////////////////////////////////////////////////////////////////// 2 // 3 // Nestopia - NES/Famicom emulator written in C++ 4 // 5 // Copyright (C) 2003-2008 Martin Freij 6 // 7 // This file is part of Nestopia. 8 // 9 // Nestopia is free software; you can redistribute it and/or modify 10 // it under the terms of the GNU General Public License as published by 11 // the Free Software Foundation; either version 2 of the License, or 12 // (at your option) any later version. 13 // 14 // Nestopia is distributed in the hope that it will be useful, 15 // but WITHOUT ANY WARRANTY; without even the implied warranty of 16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 // GNU General Public License for more details. 18 // 19 // You should have received a copy of the GNU General Public License 20 // along with Nestopia; if not, write to the Free Software 21 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 22 // 23 //////////////////////////////////////////////////////////////////////////////////////// 24 25 #include <cstring> 26 #include "NstBoard.hpp" 27 #include "../NstTimer.hpp" 28 #include "NstBoardFfe.hpp" 29 30 namespace Nes 31 { 32 namespace Core 33 { 34 namespace Boards 35 { 36 #ifdef NST_MSVC_OPTIMIZE 37 #pragma optimize("s", on) 38 #endif 39 Trainer(const Ram & ram)40 Ffe::Trainer::Trainer(const Ram& ram) 41 : available(ram.Size() >= SIZE) 42 { 43 if (available) 44 std::memcpy( data, ram.Mem(), SIZE ); 45 else 46 std::memset( data, 0x00, SIZE ); 47 } 48 Ffe(const Context & c)49 Ffe::Ffe(const Context& c) 50 : 51 Board (c), 52 irq (board == Type::CUSTOM_FFE3 ? NULL : new Timer::M2<Irq>(*c.cpu,0xFFFF)), 53 trainer (c.trainer) 54 { 55 } 56 ~Ffe()57 Ffe::~Ffe() 58 { 59 delete irq; 60 } 61 Reset(const bool hard)62 void Ffe::Irq::Reset(const bool hard) 63 { 64 if (hard) 65 { 66 count = 0; 67 enabled = false; 68 } 69 } 70 SubReset(const bool hard)71 void Ffe::SubReset(const bool hard) 72 { 73 if (hard) 74 mode = 0; 75 76 if (trainer.available && board.GetWram() >= 0x1000 + Trainer::SIZE) 77 std::memcpy( wrk.Source().Mem(0x1000), trainer.data, Trainer::SIZE ); 78 79 Map( 0x42FEU, &Ffe::Poke_42FE ); 80 Map( 0x42FFU, &Ffe::Poke_42FF ); 81 82 if (irq) 83 { 84 irq->Reset( hard, hard || irq->Connected() ); 85 86 Map( 0x4501U, &Ffe::Poke_4501 ); 87 Map( 0x4502U, &Ffe::Poke_4502 ); 88 Map( 0x4503U, &Ffe::Poke_4503 ); 89 } 90 91 if (board.GetId() == Type::CUSTOM_FFE3) 92 { 93 Map( 0x8000U, 0xFFFFU, &Ffe::Poke_Prg_F3 ); 94 95 if (hard) 96 prg.SwapBank<SIZE_32K,0x0000>(0); 97 } 98 else if (board.GetId() == Type::CUSTOM_FFE4) 99 { 100 Map( 0x8000U, 0xFFFFU, &Ffe::Poke_Prg_F4 ); 101 102 if (hard) 103 prg.SwapBank<SIZE_16K,0x4000>(7); 104 } 105 else if (board.GetId() == Type::CUSTOM_FFE8) 106 { 107 Map( 0x4504U, PRG_SWAP_8K_0 ); 108 Map( 0x4505U, PRG_SWAP_8K_1 ); 109 Map( 0x4506U, PRG_SWAP_8K_2 ); 110 Map( 0x4507U, PRG_SWAP_8K_3 ); 111 Map( 0x4510U, CHR_SWAP_1K_0 ); 112 Map( 0x4511U, CHR_SWAP_1K_1 ); 113 Map( 0x4512U, CHR_SWAP_1K_2 ); 114 Map( 0x4513U, CHR_SWAP_1K_3 ); 115 Map( 0x4514U, CHR_SWAP_1K_4 ); 116 Map( 0x4515U, CHR_SWAP_1K_5 ); 117 Map( 0x4516U, CHR_SWAP_1K_6 ); 118 Map( 0x4517U, CHR_SWAP_1K_7 ); 119 } 120 } 121 SubLoad(State::Loader & state,const dword baseChunk)122 void Ffe::SubLoad(State::Loader& state,const dword baseChunk) 123 { 124 NST_VERIFY( baseChunk == (AsciiId<'F','F','E'>::V) ); 125 126 if (baseChunk == AsciiId<'F','F','E'>::V) 127 { 128 while (const dword chunk = state.Begin()) 129 { 130 switch (chunk) 131 { 132 case AsciiId<'R','E','G'>::V: 133 134 NST_VERIFY( board == Type::CUSTOM_FFE4 ); 135 136 if (board == Type::CUSTOM_FFE4) 137 mode = state.Read8() & 0x1; 138 139 break; 140 141 case AsciiId<'I','R','Q'>::V: 142 143 NST_VERIFY( irq ); 144 145 if (irq) 146 { 147 State::Loader::Data<3> data( state ); 148 149 irq->unit.enabled = data[0] & 0x1; 150 irq->unit.count = data[1] | data[2] << 8; 151 } 152 break; 153 } 154 155 state.End(); 156 } 157 } 158 } 159 SubSave(State::Saver & state) const160 void Ffe::SubSave(State::Saver& state) const 161 { 162 state.Begin( AsciiId<'F','F','E'>::V ); 163 164 if (board == Type::CUSTOM_FFE4) 165 state.Begin( AsciiId<'R','E','G'>::V ).Write8( mode ).End(); 166 167 if (irq) 168 { 169 const byte data[3] = 170 { 171 irq->unit.enabled != false, 172 irq->unit.count & 0xFF, 173 irq->unit.count >> 8 174 }; 175 176 state.Begin( AsciiId<'I','R','Q'>::V ).Write( data ).End(); 177 } 178 179 state.End(); 180 } 181 182 #ifdef NST_MSVC_OPTIMIZE 183 #pragma optimize("", on) 184 #endif 185 Clock()186 bool Ffe::Irq::Clock() 187 { 188 if (enabled && count++ == clock) 189 { 190 count = 0; 191 enabled = false; 192 return true; 193 } 194 195 return false; 196 } 197 198 NES_POKE_D(Ffe,42FE) 199 { 200 mode = data >> 7 ^ 0x1; 201 ppu.SetMirroring( (data & 0x10) ? Ppu::NMT_1 : Ppu::NMT_0 ); 202 } 203 204 NES_POKE_D(Ffe,42FF) 205 { 206 ppu.SetMirroring( (data & 0x10) ? Ppu::NMT_H : Ppu::NMT_V ); 207 } 208 209 NES_POKE_D(Ffe,4501) 210 { 211 irq->Update(); 212 irq->unit.enabled = data & 0x1; 213 irq->ClearIRQ(); 214 } 215 216 NES_POKE_D(Ffe,4502) 217 { 218 irq->Update(); 219 irq->unit.count = (irq->unit.count & 0xFF00) | data; 220 } 221 222 NES_POKE_D(Ffe,4503) 223 { 224 irq->Update(); 225 irq->unit.count = (irq->unit.count & 0x00FF) | (data << 8); 226 irq->unit.enabled = true; 227 irq->ClearIRQ(); 228 } 229 NES_POKE_D(Ffe,Prg_F3)230 NES_POKE_D(Ffe,Prg_F3) 231 { 232 ppu.Update(); 233 prg.SwapBank<SIZE_16K,0x0000>( data >> 3 ); 234 chr.SwapBank<SIZE_8K,0x0000>( data & 0x7 ); 235 } 236 NES_POKE_D(Ffe,Prg_F4)237 NES_POKE_D(Ffe,Prg_F4) 238 { 239 ppu.Update(); 240 241 if (mode || chr.Source(0).Writable()) 242 { 243 prg.SwapBank<SIZE_16K,0x0000>( data >> 2 ); 244 data &= 0x3; 245 } 246 247 chr.Source( mode ).SwapBank<SIZE_8K,0x0000>( data ); 248 } 249 Sync(Event event,Input::Controllers * controllers)250 void Ffe::Sync(Event event,Input::Controllers* controllers) 251 { 252 if (event == EVENT_END_FRAME) 253 { 254 if (irq) 255 irq->VSync(); 256 } 257 258 Board::Sync( event, controllers ); 259 } 260 } 261 } 262 } 263