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 "NstBoard.hpp" 26 #include "../NstTimer.hpp" 27 #include "../NstSoundPlayer.hpp" 28 #include "NstBoardJalecoSs88006.hpp" 29 30 namespace Nes 31 { 32 namespace Core 33 { 34 namespace Boards 35 { 36 namespace Jaleco 37 { 38 #ifdef NST_MSVC_OPTIMIZE 39 #pragma optimize("s", on) 40 #endif 41 Reset(const bool hard)42 void Ss88006::Irq::Reset(const bool hard) 43 { 44 if (hard) 45 { 46 mask = 0xFFFF; 47 count = 0; 48 latch = 0; 49 } 50 } 51 Ss88006(const Context & c)52 Ss88006::Ss88006(const Context& c) 53 : 54 Board (c), 55 irq (*c.cpu), 56 sound 57 ( 58 Sound::Player::Create 59 ( 60 *c.apu, 61 c.chips, 62 L"D7756C", 63 board == Type::JALECO_JF24 ? Sound::Player::GAME_TERAO_NO_DOSUKOI_OOZUMOU : 64 board == Type::JALECO_JF23 ? Sound::Player::GAME_MOERO_PRO_YAKYUU_88 : 65 board == Type::JALECO_JF29 ? Sound::Player::GAME_MOERO_PRO_YAKYUU_88 : 66 board == Type::JALECO_JF33 ? Sound::Player::GAME_MOERO_PRO_YAKYUU_88 : 67 Sound::Player::GAME_UNKNOWN, 68 32 69 ) 70 ) 71 {} 72 ~Ss88006()73 Ss88006::~Ss88006() 74 { 75 Sound::Player::Destroy( sound ); 76 } 77 SubReset(const bool hard)78 void Ss88006::SubReset(const bool hard) 79 { 80 if (hard) 81 wrk.Source().SetSecurity( false, false ); 82 83 reg = 0; 84 irq.Reset( hard, hard ? false : irq.Connected() ); 85 86 for (uint i=0x0000; i < 0x1000; i += 0x4) 87 { 88 Map( 0x8000 + i, &Ss88006::Poke_8000 ); 89 Map( 0x8001 + i, &Ss88006::Poke_8001 ); 90 Map( 0x8002 + i, &Ss88006::Poke_8002 ); 91 Map( 0x8003 + i, &Ss88006::Poke_8003 ); 92 Map( 0x9000 + i, &Ss88006::Poke_9000 ); 93 Map( 0x9001 + i, &Ss88006::Poke_9001 ); 94 Map( 0x9002 + i, &Ss88006::Poke_9002 ); 95 Map( 0xA000 + i, &Ss88006::Poke_A000 ); 96 Map( 0xA001 + i, &Ss88006::Poke_A001 ); 97 Map( 0xA002 + i, &Ss88006::Poke_A002 ); 98 Map( 0xA003 + i, &Ss88006::Poke_A003 ); 99 Map( 0xB000 + i, &Ss88006::Poke_B000 ); 100 Map( 0xB001 + i, &Ss88006::Poke_B001 ); 101 Map( 0xB002 + i, &Ss88006::Poke_B002 ); 102 Map( 0xB003 + i, &Ss88006::Poke_B003 ); 103 Map( 0xC000 + i, &Ss88006::Poke_C000 ); 104 Map( 0xC001 + i, &Ss88006::Poke_C001 ); 105 Map( 0xC002 + i, &Ss88006::Poke_C002 ); 106 Map( 0xC003 + i, &Ss88006::Poke_C003 ); 107 Map( 0xD000 + i, &Ss88006::Poke_D000 ); 108 Map( 0xD001 + i, &Ss88006::Poke_D001 ); 109 Map( 0xD002 + i, &Ss88006::Poke_D002 ); 110 Map( 0xD003 + i, &Ss88006::Poke_D003 ); 111 Map( 0xE000 + i, &Ss88006::Poke_E000 ); 112 Map( 0xE001 + i, &Ss88006::Poke_E001 ); 113 Map( 0xE002 + i, &Ss88006::Poke_E002 ); 114 Map( 0xE003 + i, &Ss88006::Poke_E003 ); 115 Map( 0xF000 + i, &Ss88006::Poke_F000 ); 116 Map( 0xF001 + i, &Ss88006::Poke_F001 ); 117 Map( 0xF002 + i, NMT_SWAP_HV01 ); 118 119 if (sound) 120 Map( 0xF003 + i, &Ss88006::Poke_F003 ); 121 } 122 } 123 SubLoad(State::Loader & state,const dword baseChunk)124 void Ss88006::SubLoad(State::Loader& state,const dword baseChunk) 125 { 126 NST_VERIFY( baseChunk == (AsciiId<'J','S','8'>::V) ); 127 128 if (sound) 129 sound->Stop(); 130 131 if (baseChunk == AsciiId<'J','S','8'>::V) 132 { 133 while (const dword chunk = state.Begin()) 134 { 135 switch (chunk) 136 { 137 case AsciiId<'I','R','Q'>::V: 138 { 139 State::Loader::Data<5> data( state ); 140 141 irq.Connect( data[0] & 0x1 ); 142 143 if (data[0] & 0x8) irq.unit.mask = 0x000F; 144 else if (data[0] & 0x4) irq.unit.mask = 0x00FF; 145 else if (data[0] & 0x2) irq.unit.mask = 0x0FFF; 146 else irq.unit.mask = 0xFFFF; 147 148 irq.unit.latch = data[1] | data[2] << 8; 149 irq.unit.count = data[3] | data[4] << 8; 150 break; 151 } 152 153 case AsciiId<'R','E','G'>::V: 154 155 NST_VERIFY( sound ); 156 reg = state.Read8(); 157 break; 158 } 159 160 state.End(); 161 } 162 } 163 } 164 SubSave(State::Saver & state) const165 void Ss88006::SubSave(State::Saver& state) const 166 { 167 state.Begin( AsciiId<'J','S','8'>::V ); 168 169 const byte data[5] = 170 { 171 (irq.Connected() ? 0x1U : 0x0U) | 172 ( 173 irq.unit.mask == 0x000F ? 0x8U : 174 irq.unit.mask == 0x00FF ? 0x4U : 175 irq.unit.mask == 0x0FFF ? 0x2U : 176 0x0U 177 ), 178 irq.unit.latch & 0xFF, 179 irq.unit.latch >> 8, 180 irq.unit.count & 0xFF, 181 irq.unit.count >> 8 182 }; 183 184 state.Begin( AsciiId<'I','R','Q'>::V ).Write( data ).End(); 185 186 if (sound) 187 state.Begin( AsciiId<'R','E','G'>::V ).Write8( reg ).End(); 188 189 state.End(); 190 } 191 192 #ifdef NST_MSVC_OPTIMIZE 193 #pragma optimize("", on) 194 #endif 195 196 template<uint MASK,uint SHIFT> SwapPrg(const uint address,const uint data)197 void Ss88006::SwapPrg(const uint address,const uint data) 198 { 199 prg.SwapBank<SIZE_8K>( address, (prg.GetBank<SIZE_8K>(address) & MASK) | (data & 0xF) << SHIFT ); 200 } 201 202 NES_POKE_D(Ss88006,8000) { SwapPrg<0xF0,0>( 0x0000, data ); } 203 NES_POKE_D(Ss88006,8001) { SwapPrg<0x0F,4>( 0x0000, data ); } 204 NES_POKE_D(Ss88006,8002) { SwapPrg<0xF0,0>( 0x2000, data ); } 205 NES_POKE_D(Ss88006,8003) { SwapPrg<0x0F,4>( 0x2000, data ); } 206 NES_POKE_D(Ss88006,9000) { SwapPrg<0xF0,0>( 0x4000, data ); } 207 NES_POKE_D(Ss88006,9001) { SwapPrg<0x0F,4>( 0x4000, data ); } 208 209 NES_POKE_D(Ss88006,9002) 210 { 211 NST_VERIFY( data == 0x3 || data == 0x0 ); 212 wrk.Source().SetSecurity( data & 0x1, data & 0x2 ); 213 } 214 215 template<uint MASK,uint SHIFT> SwapChr(const uint address,const uint data) const216 void Ss88006::SwapChr(const uint address,const uint data) const 217 { 218 ppu.Update(); 219 chr.SwapBank<SIZE_1K>( address, (chr.GetBank<SIZE_1K>(address) & MASK) | (data & 0xF) << SHIFT ); 220 } 221 NES_POKE_D(Ss88006,A000)222 NES_POKE_D(Ss88006,A000) { SwapChr<0xF0,0>( 0x0000, data ); } NES_POKE_D(Ss88006,A001)223 NES_POKE_D(Ss88006,A001) { SwapChr<0x0F,4>( 0x0000, data ); } NES_POKE_D(Ss88006,A002)224 NES_POKE_D(Ss88006,A002) { SwapChr<0xF0,0>( 0x0400, data ); } NES_POKE_D(Ss88006,A003)225 NES_POKE_D(Ss88006,A003) { SwapChr<0x0F,4>( 0x0400, data ); } NES_POKE_D(Ss88006,B000)226 NES_POKE_D(Ss88006,B000) { SwapChr<0xF0,0>( 0x0800, data ); } NES_POKE_D(Ss88006,B001)227 NES_POKE_D(Ss88006,B001) { SwapChr<0x0F,4>( 0x0800, data ); } NES_POKE_D(Ss88006,B002)228 NES_POKE_D(Ss88006,B002) { SwapChr<0xF0,0>( 0x0C00, data ); } NES_POKE_D(Ss88006,B003)229 NES_POKE_D(Ss88006,B003) { SwapChr<0x0F,4>( 0x0C00, data ); } NES_POKE_D(Ss88006,C000)230 NES_POKE_D(Ss88006,C000) { SwapChr<0xF0,0>( 0x1000, data ); } NES_POKE_D(Ss88006,C001)231 NES_POKE_D(Ss88006,C001) { SwapChr<0x0F,4>( 0x1000, data ); } NES_POKE_D(Ss88006,C002)232 NES_POKE_D(Ss88006,C002) { SwapChr<0xF0,0>( 0x1400, data ); } NES_POKE_D(Ss88006,C003)233 NES_POKE_D(Ss88006,C003) { SwapChr<0x0F,4>( 0x1400, data ); } NES_POKE_D(Ss88006,D000)234 NES_POKE_D(Ss88006,D000) { SwapChr<0xF0,0>( 0x1800, data ); } NES_POKE_D(Ss88006,D001)235 NES_POKE_D(Ss88006,D001) { SwapChr<0x0F,4>( 0x1800, data ); } NES_POKE_D(Ss88006,D002)236 NES_POKE_D(Ss88006,D002) { SwapChr<0xF0,0>( 0x1C00, data ); } NES_POKE_D(Ss88006,D003)237 NES_POKE_D(Ss88006,D003) { SwapChr<0x0F,4>( 0x1C00, data ); } 238 NES_POKE_D(Ss88006,E000)239 NES_POKE_D(Ss88006,E000) 240 { 241 irq.Update(); 242 irq.unit.latch = (irq.unit.latch & 0xFFF0) | (data & 0xF) << 0; 243 } 244 NES_POKE_D(Ss88006,E001)245 NES_POKE_D(Ss88006,E001) 246 { 247 irq.Update(); 248 irq.unit.latch = (irq.unit.latch & 0xFF0F) | (data & 0xF) << 4; 249 } 250 NES_POKE_D(Ss88006,E002)251 NES_POKE_D(Ss88006,E002) 252 { 253 irq.Update(); 254 irq.unit.latch = (irq.unit.latch & 0xF0FF) | (data & 0xF) << 8; 255 } 256 NES_POKE_D(Ss88006,E003)257 NES_POKE_D(Ss88006,E003) 258 { 259 irq.Update(); 260 irq.unit.latch = (irq.unit.latch & 0x0FFF) | (data & 0xF) << 12; 261 } 262 NES_POKE(Ss88006,F000)263 NES_POKE(Ss88006,F000) 264 { 265 irq.Update(); 266 irq.unit.count = irq.unit.latch; 267 irq.ClearIRQ(); 268 } 269 NES_POKE_D(Ss88006,F001)270 NES_POKE_D(Ss88006,F001) 271 { 272 irq.Update(); 273 274 if (data & 0x8) irq.unit.mask = 0x000F; 275 else if (data & 0x4) irq.unit.mask = 0x00FF; 276 else if (data & 0x2) irq.unit.mask = 0x0FFF; 277 else irq.unit.mask = 0xFFFF; 278 279 irq.Connect( data & 0x1 ); 280 irq.ClearIRQ(); 281 } 282 NES_POKE_D(Ss88006,F003)283 NES_POKE_D(Ss88006,F003) 284 { 285 NST_ASSERT( sound ); 286 287 uint tmp = reg; 288 reg = data; 289 290 if ((data & 0x2) < (tmp & 0x2) && (data & 0x1D) == (tmp & 0x1D)) 291 sound->Play( data >> 2 & 0x1F ); 292 } 293 Clock()294 bool Ss88006::Irq::Clock() 295 { 296 return (count & mask) && !(--count & mask); 297 } 298 Sync(Event event,Input::Controllers * controllers)299 void Ss88006::Sync(Event event,Input::Controllers* controllers) 300 { 301 if (event == EVENT_END_FRAME) 302 irq.VSync(); 303 304 Board::Sync( event, controllers ); 305 } 306 } 307 } 308 } 309 } 310