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