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