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 "NstLog.hpp"
27 #include "NstFds.hpp"
28 #include "board/NstBoard.hpp"
29 #include "board/NstBoardMmc5.hpp"
30 #include "board/NstBoardKonami.hpp"
31 #include "board/NstBoardNamcot.hpp"
32 #include "board/NstBoardSunsoft.hpp"
33 #include "api/NstApiNsf.hpp"
34 #include "NstNsf.hpp"
35 
36 namespace Nes
37 {
38 	namespace Core
39 	{
40 		#ifdef NST_MSVC_OPTIMIZE
41 		#pragma optimize("s", on)
42 		#endif
43 
44 		class Nsf::Chips : Apu::Channel
45 		{
46 			struct Mmc5 : Boards::Mmc5::Sound
47 			{
48 				uint mul[2];
49 				byte exRam[SIZE_1K];
50 
Mmc5Nes::Core::Nsf::Chips::Mmc551 				explicit Mmc5(Apu& a)
52 				: Sound(a,false) {}
53 
54 				void Reset();
55 				void ClearExRam();
56 
57 				using Sound::UpdateSettings;
58 				using Sound::GetSample;
59 				using Sound::Clock;
60 			};
61 
62 			struct Fds : Core::Fds::Sound
63 			{
64 				byte ram[SIZE_8K+SIZE_32K];
65 
FdsNes::Core::Nsf::Chips::Fds66 				explicit Fds(Apu& a)
67 				: Sound(a,false) {}
68 
69 				void Reset();
70 				void SwapBank(const Prg&,uint,uint);
71 
72 				using Sound::UpdateSettings;
73 				using Sound::GetSample;
74 				using Sound::Clock;
75 			};
76 
77 			struct N163 : Boards::Namcot::N163::Sound
78 			{
N163Nes::Core::Nsf::Chips::N16379 				explicit N163(Apu& a)
80 				: Sound(a,false) {}
81 
82 				using Sound::Reset;
83 				using Sound::UpdateSettings;
84 				using Sound::GetSample;
85 			};
86 
87 			struct Vrc6 : Boards::Konami::Vrc6::Sound
88 			{
Vrc6Nes::Core::Nsf::Chips::Vrc689 				explicit Vrc6(Apu& a)
90 				: Sound(a,false) {}
91 
92 				using Sound::Reset;
93 				using Sound::UpdateSettings;
94 				using Sound::GetSample;
95 			};
96 
97 			struct Vrc7 : Boards::Konami::Vrc7::Sound
98 			{
Vrc7Nes::Core::Nsf::Chips::Vrc799 				explicit Vrc7(Apu& a)
100 				: Sound(a,false) {}
101 
102 				using Sound::Reset;
103 				using Sound::UpdateSettings;
104 				using Sound::GetSample;
105 			};
106 
107 			struct S5b : Boards::Sunsoft::S5b::Sound
108 			{
S5bNes::Core::Nsf::Chips::S5b109 				explicit S5b(Apu& a)
110 				: Sound(a,false) {}
111 
112 				using Sound::Reset;
113 				using Sound::UpdateSettings;
114 				using Sound::GetSample;
115 			};
116 
117 			template<typename T>
118 			struct Chip : Pointer<T>
119 			{
ChipNes::Core::Nsf::Chips::Chip120 				Chip(Apu& a,uint t)
121 				: Pointer<T>(t ? new T(a) : NULL) {}
122 			};
123 
124 			struct Clocks
125 			{
126 				void Reset(bool,bool);
127 
128 				Cycle next;
129 				Cycle mmc5;
130 				Cycle fds;
131 			};
132 
133 			void Reset();
134 			bool UpdateSettings();
135 			Sample GetSample();
136 			Cycle Clock(Cycle,Cycle,Cycle);
137 
138 			Clocks clocks;
139 
140 		public:
141 
142 			Chips(uint,Apu&);
143 
144 			Chip<Mmc5> mmc5;
145 			Chip<Vrc6> vrc6;
146 			Chip<Vrc7> vrc7;
147 			Chip<Fds>  fds;
148 			Chip<S5b>  s5b;
149 			Chip<N163> n163;
150 		};
151 
ClearExRam()152 		void Nsf::Chips::Mmc5::ClearExRam()
153 		{
154 			std::memset( exRam, 0, sizeof(exRam) );
155 		}
156 
Reset()157 		void Nsf::Chips::Mmc5::Reset()
158 		{
159 			mul[0] = 0;
160 			mul[1] = 0;
161 
162 			ClearExRam();
163 
164 			Sound::Reset();
165 		}
166 
Reset()167 		void Nsf::Chips::Fds::Reset()
168 		{
169 			std::memset( ram, 0, sizeof(ram) );
170 
171 			Sound::Reset();
172 		}
173 
SwapBank(const Prg & prg,uint page,uint bank)174 		void Nsf::Chips::Fds::SwapBank(const Prg& prg,uint page,uint bank)
175 		{
176 			std::memcpy( ram + SIZE_4K * page, prg.Source().Mem(bank * SIZE_4K), SIZE_4K );
177 		}
178 
Chips(const uint types,Apu & apu)179 		Nsf::Chips::Chips(const uint types,Apu& apu)
180 		:
181 		Channel  ( apu ),
182 		mmc5     ( apu, types & Api::Nsf::CHIP_MMC5 ),
183 		vrc6     ( apu, types & Api::Nsf::CHIP_VRC6 ),
184 		vrc7     ( apu, types & Api::Nsf::CHIP_VRC7 ),
185 		fds      ( apu, types & Api::Nsf::CHIP_FDS  ),
186 		s5b      ( apu, types & Api::Nsf::CHIP_S5B  ),
187 		n163     ( apu, types & Api::Nsf::CHIP_N163 )
188 		{
189 			Connect( UpdateSettings() );
190 		}
191 
Reset(bool mmc5Chip,bool fdsChip)192 		void Nsf::Chips::Clocks::Reset(bool mmc5Chip,bool fdsChip)
193 		{
194 			next = (mmc5Chip || fdsChip ? 0UL : Cpu::CYCLE_MAX);
195 			mmc5 = (mmc5Chip            ? 0UL : Cpu::CYCLE_MAX);
196 			fds  = (fdsChip             ? 0UL : Cpu::CYCLE_MAX);
197 		}
198 
Reset()199 		void Nsf::Chips::Reset()
200 		{
201 			clocks.Reset( mmc5, fds );
202 
203 			if ( mmc5 ) mmc5->Reset();
204 			if ( vrc6 ) vrc6->Reset();
205 			if ( vrc7 ) vrc7->Reset();
206 			if ( fds  ) fds->Reset();
207 			if ( s5b  ) s5b->Reset();
208 			if ( n163 ) n163->Reset();
209 		}
210 
UpdateSettings()211 		bool Nsf::Chips::UpdateSettings()
212 		{
213 			clocks.Reset( mmc5, fds );
214 
215 			return
216 			(
217 				( mmc5 ? mmc5->UpdateSettings() : 0U ) |
218 				( vrc6 ? vrc6->UpdateSettings() : 0U ) |
219 				( vrc7 ? vrc7->UpdateSettings() : 0U ) |
220 				( fds  ? fds->UpdateSettings()  : 0U ) |
221 				( s5b  ? s5b->UpdateSettings()  : 0U ) |
222 				( n163 ? n163->UpdateSettings() : 0U )
223 			);
224 		}
225 
Nsf(Context & context)226 		Nsf::Nsf(Context& context)
227 		:
228 		Image    (SOUND),
229 		cpu      (context.cpu),
230 		apu      (context.apu),
231 		chips    (NULL),
232 		favoredSystem (context.favoredSystem),
233 		tuneMode (Api::Nsf::TUNE_MODE_NTSC)
234 		{
235 			if (context.patch && context.patchResult)
236 				*context.patchResult = RESULT_ERR_UNSUPPORTED;
237 
238 			Stream::In stream( &context.stream );
239 
240 			uint version;
241 
242 			{
243 				byte data[5+1+2+6];
244 				stream.Read( data );
245 
246 				if
247 				(
248 					data[0] != Ascii<'N'>::V ||
249 					data[1] != Ascii<'E'>::V ||
250 					data[2] != Ascii<'S'>::V ||
251 					data[3] != Ascii<'M'>::V ||
252 					data[4] != 0x1A
253 				)
254 					throw RESULT_ERR_INVALID_FILE;
255 
256 				if (!data[6] || data[9] < 0x60 || data[11] < 0x60 || data[13] < 0x60)
257 					throw RESULT_ERR_CORRUPT_FILE;
258 
259 				songs.count = data[6];
260 				songs.start = data[7] >= 1 && data[7] <= data[6] ? data[7] - 1 : 0;
261 
262 				addressing.load = data[8]  | uint( data[9]  ) << 8;
263 				addressing.init = data[10] | uint( data[11] ) << 8;
264 				addressing.play = data[12] | uint( data[13] ) << 8;
265 
266 				version = data[5];
267 			}
268 
269 			stream.Read( songs.info.name, 32 );
270 			stream.Read( songs.info.artist, 32 );
271 			stream.Read( songs.info.copyright, 32 );
272 
273 			songs.info.name[31] = '\0';
274 			songs.info.artist[31] = '\0';
275 			songs.info.copyright[31] = '\0';
276 
277 			speed.ntsc = stream.Read16();
278 			stream.Read( banks );
279 
280 			addressing.bankSwitched = 0 !=
281 			(
282 				uint( banks[0] ) |
283 				uint( banks[1] ) |
284 				uint( banks[2] ) |
285 				uint( banks[3] ) |
286 				uint( banks[4] ) |
287 				uint( banks[5] ) |
288 				uint( banks[6] ) |
289 				uint( banks[7] )
290 			);
291 
292 			speed.pal = stream.Read16();
293 			songs.current = songs.start;
294 
295 			switch (stream.Read8() & 0x3)
296 			{
297 				case 0x0: tuneMode = Api::Nsf::TUNE_MODE_NTSC; break;
298 				case 0x1: tuneMode = Api::Nsf::TUNE_MODE_PAL;  break;
299 				default:  tuneMode = Api::Nsf::TUNE_MODE_BOTH; break;
300 			}
301 
302 			uint types = stream.Read8();
303 
304 			if (!(types & Api::Nsf::CHIP_FDS) && addressing.load < 0x8000)
305 				throw RESULT_ERR_CORRUPT_FILE;
306 
307 			dword length = 0;
308 
309 			while (length < SIZE_4096K && stream.SafeRead8() <= 0xFF)
310 				++length;
311 
312 			if (length <= HEADER_RESERVED_LENGTH)
313 				throw RESULT_ERR_CORRUPT_FILE;
314 
315 			length -= HEADER_RESERVED_LENGTH;
316 			stream.Seek( -idword(length) );
317 
318 			{
319 				const uint offset = addressing.load & 0xFFFU;
320 
321 				prg.Source().Set( Ram::ROM, true, false, offset + length );
322 				prg.Source().Fill( JAM );
323 				stream.Read( prg.Source().Mem() + offset, length );
324 			}
325 
326 			if (types & Api::Nsf::CHIP_ALL)
327 				chips = new Chips (types,apu);
328 
329 			if (Log::Available())
330 			{
331 				Log log;
332 
333 				log << "Nsf: version " << version;
334 
335 				if (*songs.info.name)
336 					log << NST_LINEBREAK "Nsf: name: " << songs.info.name;
337 
338 				if (*songs.info.artist)
339 					log << NST_LINEBREAK "Nsf: artist: " << songs.info.artist;
340 
341 				if (*songs.info.copyright)
342 					log << NST_LINEBREAK "Nsf: copyright: " << songs.info.copyright;
343 
344 				log << NST_LINEBREAK "Nsf: starting song "
345 					<< (songs.start+1U)
346 					<< " of "
347 					<< songs.count
348 					<<
349 					(
350 						tuneMode == Api::Nsf::TUNE_MODE_NTSC ? NST_LINEBREAK "Nsf: NTSC mode"     :
351 						tuneMode == Api::Nsf::TUNE_MODE_PAL  ? NST_LINEBREAK "Nsf: PAL mode"      :
352                                                                NST_LINEBREAK "Nsf: PAL/NTSC mode"
353 					)
354 					<< NST_LINEBREAK "Nsf: "
355 					<< (length / SIZE_1K)
356 					<< (addressing.bankSwitched ? "k bank-switched " : "k flat ")
357 					<< ((types & Api::Nsf::CHIP_FDS) ? "PRG-RAM" : "PRG-ROM")
358 					<< NST_LINEBREAK "Nsf: load address - " << Log::Hex( 16, addressing.load )
359 					<< NST_LINEBREAK "Nsf: init address - " << Log::Hex( 16, addressing.init )
360 					<< NST_LINEBREAK "Nsf: play address - " << Log::Hex( 16, addressing.play )
361 					<< NST_LINEBREAK;
362 
363 				if (types & Api::Nsf::CHIP_ALL)
364 				{
365 					if ( chips->mmc5 ) log << "Nsf: MMC5 sound chip present" NST_LINEBREAK;
366 					if ( chips->vrc6 ) log << "Nsf: VRC6 sound chip present" NST_LINEBREAK;
367 					if ( chips->vrc7 ) log << "Nsf: VRC7 sound chip present" NST_LINEBREAK;
368 					if ( chips->fds  ) log << "Nsf: FDS sound chip present" NST_LINEBREAK;
369 					if ( chips->s5b  ) log << "Nsf: Sunsoft5B sound chip present" NST_LINEBREAK;
370 					if ( chips->n163 ) log << "Nsf: N163 sound chip present" NST_LINEBREAK;
371 				}
372 			}
373 		}
374 
~Nsf()375 		Nsf::~Nsf()
376 		{
377 			delete chips;
378 		}
379 
GetDesiredRegion() const380 		Region Nsf::GetDesiredRegion() const
381 		{
382 			return tuneMode == Api::Nsf::TUNE_MODE_PAL ? REGION_PAL : REGION_NTSC;
383 		}
384 
GetDesiredSystem(Region region,CpuModel * cpu,PpuModel * ppu) const385 		System Nsf::GetDesiredSystem(Region region,CpuModel* cpu,PpuModel* ppu) const
386 		{
387 			if ((region == REGION_PAL) && (favoredSystem == FAVORED_DENDY))
388 			{
389 				if (cpu)
390 					*cpu = CPU_DENDY;
391 
392 				if (ppu)
393 					*ppu = PPU_DENDY;
394 
395 				return SYSTEM_DENDY;
396 			}
397 			else
398 			{
399 				return Image::GetDesiredSystem( region, cpu, ppu );
400 			}
401 		}
402 
GetChips() const403 		uint Nsf::GetChips() const
404 		{
405 			uint types = 0;
406 
407 			if (chips)
408 			{
409 				if ( chips->vrc6 ) types |= Api::Nsf::CHIP_VRC6;
410 				if ( chips->vrc7 ) types |= Api::Nsf::CHIP_VRC7;
411 				if ( chips->fds  ) types |= Api::Nsf::CHIP_FDS;
412 				if ( chips->mmc5 ) types |= Api::Nsf::CHIP_MMC5;
413 				if ( chips->n163 ) types |= Api::Nsf::CHIP_N163;
414 				if ( chips->s5b  ) types |= Api::Nsf::CHIP_S5B;
415 			}
416 
417 			return types;
418 		}
419 
Reset(bool)420 		void Nsf::Reset(bool)
421 		{
422 			cpu.Map( 0x38EC ).Set( this, &Nsf::Peek_38EC, &Nsf::Poke_Nop );
423 			cpu.Map( 0x38ED ).Set( this, &Nsf::Peek_38ED, &Nsf::Poke_Nop );
424 			cpu.Map( 0x38EE ).Set( this, &Nsf::Peek_38EE, &Nsf::Poke_Nop );
425 			cpu.Map( 0x38EF ).Set( this, &Nsf::Peek_38EF, &Nsf::Poke_Nop );
426 			cpu.Map( 0x38F0 ).Set( this, &Nsf::Peek_38F0, &Nsf::Poke_Nop );
427 			cpu.Map( 0x38F1 ).Set( this, &Nsf::Peek_38F1, &Nsf::Poke_Nop );
428 			cpu.Map( 0x38F2 ).Set( this, &Nsf::Peek_38F2, &Nsf::Poke_Nop );
429 			cpu.Map( 0x38F3 ).Set( this, &Nsf::Peek_38F3, &Nsf::Poke_Nop );
430 			cpu.Map( 0x38F4 ).Set( this, &Nsf::Peek_38F4, &Nsf::Poke_Nop );
431 			cpu.Map( 0x38F5 ).Set( this, &Nsf::Peek_38F5, &Nsf::Poke_Nop );
432 			cpu.Map( 0x38F6 ).Set( this, &Nsf::Peek_38F6, &Nsf::Poke_Nop );
433 			cpu.Map( 0x38F7 ).Set( this, &Nsf::Peek_38F7, &Nsf::Poke_Nop );
434 			cpu.Map( 0x38F8 ).Set( this, &Nsf::Peek_38F8, &Nsf::Poke_Nop );
435 			cpu.Map( 0x38F9 ).Set( this, &Nsf::Peek_38F9, &Nsf::Poke_Nop );
436 			cpu.Map( 0x38FA ).Set( this, &Nsf::Peek_38FA, &Nsf::Poke_Nop );
437 			cpu.Map( 0x38FB ).Set( this, &Nsf::Peek_38FB, &Nsf::Poke_Nop );
438 			cpu.Map( 0x38FC ).Set( this, &Nsf::Peek_38FC, &Nsf::Poke_Nop );
439 			cpu.Map( 0x38FD ).Set( this, &Nsf::Peek_38FD, &Nsf::Poke_Nop );
440 			cpu.Map( 0x38FE ).Set( this, &Nsf::Peek_38FE, &Nsf::Poke_Nop );
441 			cpu.Map( 0x38FF ).Set( this, &Nsf::Peek_38FF, &Nsf::Poke_Nop );
442 
443 			cpu.Map( 0x4017 ).Set( this, &Nsf::Peek_Nop, &Nsf::Poke_4017 );
444 
445 			const bool fds = chips && chips->fds;
446 
447 			if (addressing.bankSwitched)
448 			{
449 				if (fds)
450 				{
451 					cpu.Map( 0x5FF6 ).Set( this, &Nsf::Peek_Nop, &Nsf::Poke_Fds_5FF6 );
452 					cpu.Map( 0x5FF7 ).Set( this, &Nsf::Peek_Nop, &Nsf::Poke_Fds_5FF7 );
453 				}
454 
455 				cpu.Map( 0x5FF8 ).Set( this, &Nsf::Peek_Nop, fds ? &Nsf::Poke_Fds_5FF8 : &Nsf::Poke_5FF8 );
456 				cpu.Map( 0x5FF9 ).Set( this, &Nsf::Peek_Nop, fds ? &Nsf::Poke_Fds_5FF9 : &Nsf::Poke_5FF9 );
457 				cpu.Map( 0x5FFA ).Set( this, &Nsf::Peek_Nop, fds ? &Nsf::Poke_Fds_5FFA : &Nsf::Poke_5FFA );
458 				cpu.Map( 0x5FFB ).Set( this, &Nsf::Peek_Nop, fds ? &Nsf::Poke_Fds_5FFB : &Nsf::Poke_5FFB );
459 				cpu.Map( 0x5FFC ).Set( this, &Nsf::Peek_Nop, fds ? &Nsf::Poke_Fds_5FFC : &Nsf::Poke_5FFC );
460 				cpu.Map( 0x5FFD ).Set( this, &Nsf::Peek_Nop, fds ? &Nsf::Poke_Fds_5FFD : &Nsf::Poke_5FFD );
461 				cpu.Map( 0x5FFE ).Set( this, &Nsf::Peek_Nop, fds ? &Nsf::Poke_Fds_5FFE : &Nsf::Poke_5FFE );
462 				cpu.Map( 0x5FFF ).Set( this, &Nsf::Peek_Nop, fds ? &Nsf::Poke_Fds_5FFF : &Nsf::Poke_5FFF );
463 			}
464 			else if (!fds)
465 			{
466 				for (dword i=0x8000, j=0; i < 0x10000; j += (i >= (addressing.load & 0xF000U)), i += 0x1000)
467 					prg.SwapBank<SIZE_4K>( i-0x8000, j );
468 			}
469 
470 			if (fds)
471 			{
472 				cpu.Map( 0x4040, 0x407F ).Set( this, &Nsf::Peek_Fds_4040, &Nsf::Poke_Fds_4040 );
473 				cpu.Map( 0x4080         ).Set( this, &Nsf::Peek_Nop,      &Nsf::Poke_Fds_4080 );
474 				cpu.Map( 0x4082         ).Set( this, &Nsf::Peek_Nop,      &Nsf::Poke_Fds_4082 );
475 				cpu.Map( 0x4083         ).Set( this, &Nsf::Peek_Nop,      &Nsf::Poke_Fds_4083 );
476 				cpu.Map( 0x4084         ).Set( this, &Nsf::Peek_Nop,      &Nsf::Poke_Fds_4084 );
477 				cpu.Map( 0x4085         ).Set( this, &Nsf::Peek_Nop,      &Nsf::Poke_Fds_4085 );
478 				cpu.Map( 0x4086         ).Set( this, &Nsf::Peek_Nop,      &Nsf::Poke_Fds_4086 );
479 				cpu.Map( 0x4087         ).Set( this, &Nsf::Peek_Nop,      &Nsf::Poke_Fds_4087 );
480 				cpu.Map( 0x4088         ).Set( this, &Nsf::Peek_Nop,      &Nsf::Poke_Fds_4088 );
481 				cpu.Map( 0x4089         ).Set( this, &Nsf::Peek_Nop,      &Nsf::Poke_Fds_4089 );
482 				cpu.Map( 0x408A         ).Set( this, &Nsf::Peek_Nop,      &Nsf::Poke_Fds_408A );
483 				cpu.Map( 0x4090         ).Set( this, &Nsf::Peek_Fds_4090, &Nsf::Poke_Nop      );
484 				cpu.Map( 0x4092         ).Set( this, &Nsf::Peek_Fds_4092, &Nsf::Poke_Nop      );
485 				cpu.Map( 0x6000, 0xFFFF ).Set( this, &Nsf::Peek_Fds_Ram,  &Nsf::Poke_Fds_Ram  );
486 			}
487 			else
488 			{
489 				cpu.Map( 0x6000, 0x7FFF ).Set( this, &Nsf::Peek_Wrk,   &Nsf::Poke_Wrk );
490 				cpu.Map( 0x8000, 0x8FFF ).Set( this, &Nsf::Peek_Prg_8, &Nsf::Poke_Nop );
491 				cpu.Map( 0x9000, 0x9FFF ).Set( this, &Nsf::Peek_Prg_9, &Nsf::Poke_Nop );
492 				cpu.Map( 0xA000, 0xAFFF ).Set( this, &Nsf::Peek_Prg_A, &Nsf::Poke_Nop );
493 				cpu.Map( 0xB000, 0xBFFF ).Set( this, &Nsf::Peek_Prg_B, &Nsf::Poke_Nop );
494 				cpu.Map( 0xC000, 0xCFFF ).Set( this, &Nsf::Peek_Prg_C, &Nsf::Poke_Nop );
495 				cpu.Map( 0xD000, 0xDFFF ).Set( this, &Nsf::Peek_Prg_D, &Nsf::Poke_Nop );
496 				cpu.Map( 0xE000, 0xEFFF ).Set( this, &Nsf::Peek_Prg_E, &Nsf::Poke_Nop );
497 				cpu.Map( 0xF000, 0xFFFF ).Set( this, &Nsf::Peek_Prg_F, &Nsf::Poke_Nop );
498 			}
499 
500 			if (chips)
501 			{
502 				if (chips->mmc5)
503 				{
504 					cpu.Map( 0x5000         ).Set( this, &Nsf::Peek_Nop,       &Nsf::Poke_Mmc5_5000 );
505 					cpu.Map( 0x5002         ).Set( this, &Nsf::Peek_Nop,       &Nsf::Poke_Mmc5_5002 );
506 					cpu.Map( 0x5003         ).Set( this, &Nsf::Peek_Nop,       &Nsf::Poke_Mmc5_5003 );
507 					cpu.Map( 0x5004         ).Set( this, &Nsf::Peek_Nop,       &Nsf::Poke_Mmc5_5004 );
508 					cpu.Map( 0x5006         ).Set( this, &Nsf::Peek_Nop,       &Nsf::Poke_Mmc5_5006 );
509 					cpu.Map( 0x5007         ).Set( this, &Nsf::Peek_Nop,       &Nsf::Poke_Mmc5_5007 );
510 					cpu.Map( 0x5010         ).Set( this, &Nsf::Peek_Nop,       &Nsf::Poke_Mmc5_5010 );
511 					cpu.Map( 0x5011         ).Set( this, &Nsf::Peek_Nop,       &Nsf::Poke_Mmc5_5011 );
512 					cpu.Map( 0x5015         ).Set( this, &Nsf::Peek_Mmc5_5015, &Nsf::Poke_Mmc5_5015 );
513 					cpu.Map( 0x5205         ).Set( this, &Nsf::Peek_Mmc5_5205, &Nsf::Poke_Mmc5_5205 );
514 					cpu.Map( 0x5206         ).Set( this, &Nsf::Peek_Mmc5_5206, &Nsf::Poke_Mmc5_5206 );
515 					cpu.Map( 0x5C00, 0x5FF5 ).Set( this, &Nsf::Peek_Mmc5_5C00, &Nsf::Poke_Mmc5_5C00 );
516 				}
517 
518 				if (chips->vrc6)
519 				{
520 					cpu.Map( 0x9000 ).Set( &Nsf::Poke_Vrc6_9000 );
521 					cpu.Map( 0x9001 ).Set( &Nsf::Poke_Vrc6_9001 );
522 					cpu.Map( 0x9002 ).Set( &Nsf::Poke_Vrc6_9002 );
523 					cpu.Map( 0xA000 ).Set( &Nsf::Poke_Vrc6_A000 );
524 					cpu.Map( 0xA001 ).Set( &Nsf::Poke_Vrc6_A001 );
525 					cpu.Map( 0xA002 ).Set( &Nsf::Poke_Vrc6_A002 );
526 					cpu.Map( 0xB000 ).Set( &Nsf::Poke_Vrc6_B000 );
527 					cpu.Map( 0xB001 ).Set( &Nsf::Poke_Vrc6_B001 );
528 					cpu.Map( 0xB002 ).Set( &Nsf::Poke_Vrc6_B002 );
529 				}
530 
531 				if (chips->vrc7)
532 				{
533 					cpu.Map( 0x9010 ).Set( &Nsf::Poke_Vrc7_9010 );
534 					cpu.Map( 0x9030 ).Set( &Nsf::Poke_Vrc7_9030 );
535 				}
536 
537 				if (chips->n163)
538 				{
539 					cpu.Map( 0x4800 ).Set( this, &Nsf::Peek_N163_48, &Nsf::Poke_N163_48 );
540 					cpu.Map( 0xF800 ).Set( &Nsf::Poke_N163_F8 );
541 				}
542 
543 				if (chips->s5b)
544 				{
545 					cpu.Map( 0xC000 ).Set( &Nsf::Poke_S5b_C );
546 					cpu.Map( 0xE000 ).Set( &Nsf::Poke_S5b_E );
547 				}
548 			}
549 
550 			cpu.Map( 0xFFFA ).Set( &Nsf::Peek_FFFA );
551 			cpu.Map( 0xFFFB ).Set( &Nsf::Peek_FFFB );
552 			cpu.Map( 0xFFFC ).Set( &Nsf::Peek_FFFC );
553 			cpu.Map( 0xFFFD ).Set( &Nsf::Peek_FFFD );
554 
555 			routine.reset = Routine::RESET;
556 			routine.nmi = Routine::NMI;
557 
558 			cpu.SetFrameCycles( cpu.GetModel() == CPU_RP2A03 ? PPU_RP2C02_HVSYNC : cpu.GetModel() == CPU_RP2A07 ? PPU_RP2C07_HVSYNC : PPU_DENDY_HVSYNC );
559 		}
560 
PowerOff()561 		bool Nsf::PowerOff()
562 		{
563 			StopSong();
564 			return true;
565 		}
566 
SelectSong(const uint song)567 		Result Nsf::SelectSong(const uint song)
568 		{
569 			if (song < songs.count)
570 			{
571 				if (songs.current != song)
572 				{
573 					songs.current = song;
574 
575 					if (routine.playing)
576 					{
577 						routine.nmi = Routine::NMI;
578 						apu.ClearBuffers();
579 					}
580 
581 					Api::Nsf::eventCallback( Api::Nsf::EVENT_SELECT_SONG );
582 
583 					return RESULT_OK;
584 				}
585 
586 				return RESULT_NOP;
587 			}
588 
589 			return RESULT_ERR_INVALID_PARAM;
590 		}
591 
PlaySong()592 		Result Nsf::PlaySong()
593 		{
594 			if (!routine.playing)
595 			{
596 				routine.nmi = Routine::NMI;
597 				routine.playing = true;
598 
599 				Api::Nsf::eventCallback( Api::Nsf::EVENT_PLAY_SONG );
600 
601 				return RESULT_OK;
602 			}
603 
604 			return RESULT_NOP;
605 		}
606 
StopSong()607 		Result Nsf::StopSong()
608 		{
609 			if (routine.playing)
610 			{
611 				routine.playing = false;
612 				routine.nmi = Routine::NMI;
613 				apu.ClearBuffers();
614 
615 				Api::Nsf::eventCallback( Api::Nsf::EVENT_STOP_SONG );
616 
617 				return RESULT_OK;
618 			}
619 
620 			return RESULT_NOP;
621 		}
622 
InitSong()623 		void Nsf::InitSong()
624 		{
625 			std::memset( wrk, 0x00, SIZE_8K );
626 
627 			if (chips && chips->mmc5)
628 				chips->mmc5->ClearExRam();
629 
630 			const bool fds = chips && chips->fds;
631 
632 			if (addressing.bankSwitched)
633 			{
634 				if (fds)
635 				{
636 					for (uint i=0; i < 2; ++i)
637 						cpu.Poke( 0x5FF6+i, banks[6+i] );
638 				}
639 
640 				for (uint i=0; i < 8; ++i)
641 					cpu.Poke( 0x5FF8+i, banks[i] );
642 			}
643 			else if (fds)
644 			{
645 				for (dword i=0x6000, j=0; i < 0x10000; j += (i >= (addressing.load & 0xF000U)), i += 0x1000)
646 					std::memcpy( chips->fds->ram + (i-0x6000), prg.Source().Mem(j * 0x1000), 0x1000 );
647 			}
648 
649 			if (fds)
650 			{
651 				cpu.Poke( 0x4089, 0x80 );
652 				cpu.Poke( 0x408A, 0xE8 );
653 			}
654 
655 			apu.ClearBuffers();
656 			std::memset( cpu.GetRam(), 0x00, Cpu::RAM_SIZE );
657 
658 			for (uint i=0x4000; i <= 0x4013; ++i)
659 				cpu.Poke( i, 0x00 );
660 
661 			cpu.Poke( 0x4015, 0x0F );
662 			cpu.Poke( 0x4017, 0xC0 );
663 		}
664 
665 		#ifdef NST_MSVC_OPTIMIZE
666 		#pragma optimize("", on)
667 		#endif
668 
BeginFrame()669 		void Nsf::BeginFrame()
670 		{
671 			routine.jmp = (routine.playing ? 0xFA : 0xFD);
672 
673 			if (routine.nmi)
674 				cpu.DoNMI(0);
675 		}
676 
Clock(Cycle rateCycles,Cycle rateClock,const Cycle targetCycles)677 		Cycle Nsf::Chips::Clock(Cycle rateCycles,Cycle rateClock,const Cycle targetCycles)
678 		{
679 			if (clocks.next != Cpu::CYCLE_MAX)
680 			{
681 				NST_ASSERT( (mmc5 || fds) && (clocks.mmc5 != Cpu::CYCLE_MAX || clocks.fds != Cpu::CYCLE_MAX) );
682 
683 				if (clocks.mmc5 == clocks.next)
684 					clocks.mmc5 = mmc5->Clock( rateCycles, rateClock, targetCycles ) - rateCycles;
685 
686 				if (clocks.fds == clocks.next)
687 					clocks.fds = fds->Clock( rateCycles, rateClock, targetCycles ) - rateCycles;
688 
689 				clocks.next = NST_MIN(clocks.mmc5,clocks.fds);
690 
691 				rateCycles += clocks.next;
692 
693 				return rateCycles;
694 			}
695 			else
696 			{
697 				NST_ASSERT( !mmc5 && !fds );
698 
699 				return Channel::Clock( rateCycles, rateClock, targetCycles );
700 			}
701 		}
702 
GetSample()703 		Nsf::Chips::Sample Nsf::Chips::GetSample()
704 		{
705 			return
706 			(
707 				(mmc5 ? mmc5->GetSample() : 0) +
708 				(vrc6 ? vrc6->GetSample() : 0) +
709 				(vrc7 ? vrc7->GetSample() : 0) +
710 				(fds  ? fds->GetSample()  : 0) +
711 				(s5b  ? s5b->GetSample()  : 0) +
712 				(n163 ? n163->GetSample() : 0)
713 			);
714 		}
715 
FetchLast(uint offset) const716 		inline uint Nsf::FetchLast(uint offset) const
717 		{
718 			NST_ASSERT( offset <= 0xFFF );
719 			return offset[chips && chips->fds ? chips->fds->ram + (sizeof(array(chips->fds->ram))-SIZE_4K) : prg[7]];
720 		}
721 
NES_PEEK(Nsf,FFFA)722 		NES_PEEK(Nsf,FFFA)
723 		{
724 			return routine.nmi ? routine.nmi &= Routine::NMI_B, routine.playing ? 0xEC : 0xFD : FetchLast(0xFFA);
725 		}
726 
NES_PEEK(Nsf,FFFB)727 		NES_PEEK(Nsf,FFFB)
728 		{
729 			return routine.nmi ? routine.nmi &= Routine::NMI_A, 0x38 : FetchLast(0xFFB);
730 		}
731 
NES_PEEK(Nsf,FFFC)732 		NES_PEEK(Nsf,FFFC)
733 		{
734 			return routine.reset ? routine.reset &= Routine::RESET_B, 0xFD : FetchLast(0xFFC);
735 		}
736 
NES_PEEK(Nsf,FFFD)737 		NES_PEEK(Nsf,FFFD)
738 		{
739 			return routine.reset ? routine.reset &= Routine::RESET_A, 0x38 : FetchLast(0xFFD);
740 		}
741 
742 		NES_PEEK(Nsf,38EC)
743 		{
744 			NST_VERIFY( routine.playing );
745 
746 			InitSong();
747 			return LDA;
748 		}
749 
750 		NES_PEEK(Nsf,38ED)
751 		{
752 			NST_VERIFY( routine.playing );
753 			return songs.current;
754 		}
755 
756 		NES_PEEK(Nsf,38EE)
757 		{
758 			NST_VERIFY( routine.playing );
759 			return LDX;
760 		}
761 
762 		NES_PEEK(Nsf,38EF)
763 		{
764 			NST_VERIFY( routine.playing );
765 			return 0xFC;
766 		}
767 
768 		NES_PEEK(Nsf,38F0)
769 		{
770 			NST_VERIFY( routine.playing );
771 			return TXS;
772 		}
773 
774 		NES_PEEK(Nsf,38F1)
775 		{
776 			NST_VERIFY( routine.playing );
777 			return LDX;
778 		}
779 
780 		NES_PEEK(Nsf,38F2)
781 		{
782 			NST_VERIFY( routine.playing );
783 			return cpu.GetModel() == CPU_RP2A07;
784 		}
785 
786 		NES_PEEK(Nsf,38F3)
787 		{
788 			NST_VERIFY( routine.playing );
789 			return JSR;
790 		}
791 
792 		NES_PEEK(Nsf,38F4)
793 		{
794 			NST_VERIFY( routine.playing );
795 			return addressing.init & 0xFFU;
796 		}
797 
798 		NES_PEEK(Nsf,38F5)
799 		{
800 			NST_VERIFY( routine.playing );
801 			return addressing.init >> 8;
802 		}
803 
804 		NES_PEEK(Nsf,38F6)
805 		{
806 			NST_VERIFY( routine.playing );
807 			return SEI;
808 		}
809 
810 		NES_PEEK(Nsf,38F7)
811 		{
812 			NST_VERIFY( routine.playing );
813 			routine.jmp = 0xFD;
814 			return JMP;
815 		}
816 
817 		NES_PEEK(Nsf,38F8)
818 		{
819 			NST_VERIFY( routine.playing );
820 			return 0xFD;
821 		}
822 
823 		NES_PEEK(Nsf,38F9)
824 		{
825 			NST_VERIFY( routine.playing );
826 			return 0x38;
827 		}
828 
829 		NES_PEEK(Nsf,38FA)
830 		{
831 			NST_VERIFY( routine.playing );
832 			routine.jmp = 0xFD;
833 			return JSR;
834 		}
835 
836 		NES_PEEK(Nsf,38FB)
837 		{
838 			NST_VERIFY( routine.playing );
839 			return addressing.play & 0xFFU;
840 		}
841 
842 		NES_PEEK(Nsf,38FC)
843 		{
844 			NST_VERIFY( routine.playing );
845 			return addressing.play >> 8;
846 		}
847 
848 		NES_PEEK(Nsf,38FD)
849 		{
850 			return JMP;
851 		}
852 
853 		NES_PEEK(Nsf,38FE)
854 		{
855 			return routine.jmp;
856 		}
857 
858 		NES_PEEK(Nsf,38FF)
859 		{
860 			return 0x38;
861 		}
862 
863 		NES_POKE_D(Nsf,4017)
864 		{
865 			apu.WriteFrameCtrl( data );
866 		}
867 
NES_PEEK_A(Nsf,Nop)868 		NES_PEEK_A (Nsf,Nop) { return address >> 8; }
NES_POKE(Nsf,Nop)869 		NES_POKE   (Nsf,Nop) {}
870 
NES_PEEK_A(Nsf,Prg_8)871 		NES_PEEK_A (Nsf,Prg_8) { return prg[0][address - 0x8000]; }
NES_PEEK_A(Nsf,Prg_9)872 		NES_PEEK_A (Nsf,Prg_9) { return prg[1][address - 0x9000]; }
NES_PEEK_A(Nsf,Prg_A)873 		NES_PEEK_A (Nsf,Prg_A) { return prg[2][address - 0xA000]; }
NES_PEEK_A(Nsf,Prg_B)874 		NES_PEEK_A (Nsf,Prg_B) { return prg[3][address - 0xB000]; }
NES_PEEK_A(Nsf,Prg_C)875 		NES_PEEK_A (Nsf,Prg_C) { return prg[4][address - 0xC000]; }
NES_PEEK_A(Nsf,Prg_D)876 		NES_PEEK_A (Nsf,Prg_D) { return prg[5][address - 0xD000]; }
NES_PEEK_A(Nsf,Prg_E)877 		NES_PEEK_A (Nsf,Prg_E) { return prg[6][address - 0xE000]; }
NES_PEEK_A(Nsf,Prg_F)878 		NES_PEEK_A (Nsf,Prg_F) { return prg[7][address - 0xF000]; }
879 
NES_PEEK_A(Nsf,Wrk)880 		NES_PEEK_A  (Nsf,Wrk) { return wrk[address - 0x6000]; }
NES_POKE_AD(Nsf,Wrk)881 		NES_POKE_AD (Nsf,Wrk) { wrk[address - 0x6000] = data; }
882 
883 		NES_POKE_D (Nsf,5FF8) { prg.SwapBank<SIZE_4K,0x0000>( data ); }
884 		NES_POKE_D (Nsf,5FF9) { prg.SwapBank<SIZE_4K,0x1000>( data ); }
885 		NES_POKE_D (Nsf,5FFA) { prg.SwapBank<SIZE_4K,0x2000>( data ); }
886 		NES_POKE_D (Nsf,5FFB) { prg.SwapBank<SIZE_4K,0x3000>( data ); }
887 		NES_POKE_D (Nsf,5FFC) { prg.SwapBank<SIZE_4K,0x4000>( data ); }
888 		NES_POKE_D (Nsf,5FFD) { prg.SwapBank<SIZE_4K,0x5000>( data ); }
889 		NES_POKE_D (Nsf,5FFE) { prg.SwapBank<SIZE_4K,0x6000>( data ); }
890 		NES_POKE_D (Nsf,5FFF) { prg.SwapBank<SIZE_4K,0x7000>( data ); }
891 
NES_PEEK_A(Nsf,Fds_4040)892 		NES_PEEK_A  (Nsf,Fds_4040) { return chips->fds->ReadWaveData( address ); }
NES_POKE_AD(Nsf,Fds_4040)893 		NES_POKE_AD (Nsf,Fds_4040) { chips->fds->WriteWaveData( address, data ); }
NES_POKE_D(Nsf,Fds_4080)894 		NES_POKE_D  (Nsf,Fds_4080) { chips->fds->WriteReg0( data ); }
NES_POKE_D(Nsf,Fds_4082)895 		NES_POKE_D  (Nsf,Fds_4082) { chips->fds->WriteReg1( data ); }
NES_POKE_D(Nsf,Fds_4083)896 		NES_POKE_D  (Nsf,Fds_4083) { chips->fds->WriteReg2( data ); }
NES_POKE_D(Nsf,Fds_4084)897 		NES_POKE_D  (Nsf,Fds_4084) { chips->fds->WriteReg3( data ); }
NES_POKE_D(Nsf,Fds_4085)898 		NES_POKE_D  (Nsf,Fds_4085) { chips->fds->WriteReg4( data ); }
NES_POKE_D(Nsf,Fds_4086)899 		NES_POKE_D  (Nsf,Fds_4086) { chips->fds->WriteReg5( data ); }
NES_POKE_D(Nsf,Fds_4087)900 		NES_POKE_D  (Nsf,Fds_4087) { chips->fds->WriteReg6( data ); }
NES_POKE_D(Nsf,Fds_4088)901 		NES_POKE_D  (Nsf,Fds_4088) { chips->fds->WriteReg7( data ); }
NES_POKE_D(Nsf,Fds_4089)902 		NES_POKE_D  (Nsf,Fds_4089) { chips->fds->WriteReg8( data ); }
NES_POKE_D(Nsf,Fds_408A)903 		NES_POKE_D  (Nsf,Fds_408A) { chips->fds->WriteReg9( data ); }
NES_PEEK(Nsf,Fds_4090)904 		NES_PEEK    (Nsf,Fds_4090) { return chips->fds->ReadVolumeGain(); }
NES_PEEK(Nsf,Fds_4092)905 		NES_PEEK    (Nsf,Fds_4092) { return chips->fds->ReadSweepGain();  }
NES_POKE_D(Nsf,Fds_5FF6)906 		NES_POKE_D  (Nsf,Fds_5FF6) { chips->fds->SwapBank( prg, 0, data ); }
NES_POKE_D(Nsf,Fds_5FF7)907 		NES_POKE_D  (Nsf,Fds_5FF7) { chips->fds->SwapBank( prg, 1, data ); }
NES_POKE_D(Nsf,Fds_5FF8)908 		NES_POKE_D  (Nsf,Fds_5FF8) { chips->fds->SwapBank( prg, 2, data ); }
NES_POKE_D(Nsf,Fds_5FF9)909 		NES_POKE_D  (Nsf,Fds_5FF9) { chips->fds->SwapBank( prg, 3, data ); }
NES_POKE_D(Nsf,Fds_5FFA)910 		NES_POKE_D  (Nsf,Fds_5FFA) { chips->fds->SwapBank( prg, 4, data ); }
NES_POKE_D(Nsf,Fds_5FFB)911 		NES_POKE_D  (Nsf,Fds_5FFB) { chips->fds->SwapBank( prg, 5, data ); }
NES_POKE_D(Nsf,Fds_5FFC)912 		NES_POKE_D  (Nsf,Fds_5FFC) { chips->fds->SwapBank( prg, 6, data ); }
NES_POKE_D(Nsf,Fds_5FFD)913 		NES_POKE_D  (Nsf,Fds_5FFD) { chips->fds->SwapBank( prg, 7, data ); }
NES_POKE_D(Nsf,Fds_5FFE)914 		NES_POKE_D  (Nsf,Fds_5FFE) { chips->fds->SwapBank( prg, 8, data ); }
NES_POKE_D(Nsf,Fds_5FFF)915 		NES_POKE_D  (Nsf,Fds_5FFF) { chips->fds->SwapBank( prg, 9, data ); }
NES_PEEK_A(Nsf,Fds_Ram)916 		NES_PEEK_A  (Nsf,Fds_Ram)  { return chips->fds->ram[address - 0x6000]; }
NES_POKE_AD(Nsf,Fds_Ram)917 		NES_POKE_AD (Nsf,Fds_Ram)  { chips->fds->ram[address - 0x6000] = data; }
918 
NES_POKE_D(Nsf,Mmc5_5000)919 		NES_POKE_D  (Nsf,Mmc5_5000) { chips->mmc5->WriteSquareReg0( 0, data ); }
NES_POKE_D(Nsf,Mmc5_5002)920 		NES_POKE_D  (Nsf,Mmc5_5002) { chips->mmc5->WriteSquareReg1( 0, data ); }
NES_POKE_D(Nsf,Mmc5_5003)921 		NES_POKE_D  (Nsf,Mmc5_5003) { chips->mmc5->WriteSquareReg2( 0, data ); }
NES_POKE_D(Nsf,Mmc5_5004)922 		NES_POKE_D  (Nsf,Mmc5_5004) { chips->mmc5->WriteSquareReg0( 1, data ); }
NES_POKE_D(Nsf,Mmc5_5006)923 		NES_POKE_D  (Nsf,Mmc5_5006) { chips->mmc5->WriteSquareReg1( 1, data ); }
NES_POKE_D(Nsf,Mmc5_5007)924 		NES_POKE_D  (Nsf,Mmc5_5007) { chips->mmc5->WriteSquareReg2( 1, data ); }
NES_POKE_D(Nsf,Mmc5_5010)925 		NES_POKE_D  (Nsf,Mmc5_5010) { chips->mmc5->WritePcmReg0( data ); }
NES_POKE_D(Nsf,Mmc5_5011)926 		NES_POKE_D  (Nsf,Mmc5_5011) { chips->mmc5->WritePcmReg1( data ); }
NES_POKE_D(Nsf,Mmc5_5015)927 		NES_POKE_D  (Nsf,Mmc5_5015) { chips->mmc5->WriteCtrl( data ); }
NES_PEEK(Nsf,Mmc5_5015)928 		NES_PEEK    (Nsf,Mmc5_5015) { return chips->mmc5->ReadCtrl(); }
NES_PEEK(Nsf,Mmc5_5205)929 		NES_PEEK    (Nsf,Mmc5_5205) { return (chips->mmc5->mul[0] * chips->mmc5->mul[1]) >> 0 & 0xFF; }
NES_PEEK(Nsf,Mmc5_5206)930 		NES_PEEK    (Nsf,Mmc5_5206) { return (chips->mmc5->mul[0] * chips->mmc5->mul[1]) >> 8 & 0xFF; }
NES_POKE_D(Nsf,Mmc5_5205)931 		NES_POKE_D  (Nsf,Mmc5_5205) { chips->mmc5->mul[0] = data; }
NES_POKE_D(Nsf,Mmc5_5206)932 		NES_POKE_D  (Nsf,Mmc5_5206) { chips->mmc5->mul[1] = data; }
NES_PEEK_A(Nsf,Mmc5_5C00)933 		NES_PEEK_A  (Nsf,Mmc5_5C00) { return chips->mmc5->exRam[address - 0x5C00]; }
NES_POKE_AD(Nsf,Mmc5_5C00)934 		NES_POKE_AD (Nsf,Mmc5_5C00) { chips->mmc5->exRam[address - 0x5C00] = data; }
935 
NES_POKE_D(Nsf,Vrc6_9000)936 		NES_POKE_D (Nsf,Vrc6_9000) { chips->vrc6->WriteSquareReg0( 0, data ); }
NES_POKE_D(Nsf,Vrc6_9001)937 		NES_POKE_D (Nsf,Vrc6_9001) { chips->vrc6->WriteSquareReg1( 0, data ); }
NES_POKE_D(Nsf,Vrc6_9002)938 		NES_POKE_D (Nsf,Vrc6_9002) { chips->vrc6->WriteSquareReg2( 0, data ); }
NES_POKE_D(Nsf,Vrc6_A000)939 		NES_POKE_D (Nsf,Vrc6_A000) { chips->vrc6->WriteSquareReg0( 1, data ); }
NES_POKE_D(Nsf,Vrc6_A001)940 		NES_POKE_D (Nsf,Vrc6_A001) { chips->vrc6->WriteSquareReg1( 1, data ); }
NES_POKE_D(Nsf,Vrc6_A002)941 		NES_POKE_D (Nsf,Vrc6_A002) { chips->vrc6->WriteSquareReg2( 1, data ); }
NES_POKE_D(Nsf,Vrc6_B000)942 		NES_POKE_D (Nsf,Vrc6_B000) { chips->vrc6->WriteSawReg0( data );       }
NES_POKE_D(Nsf,Vrc6_B001)943 		NES_POKE_D (Nsf,Vrc6_B001) { chips->vrc6->WriteSawReg1( data );       }
NES_POKE_D(Nsf,Vrc6_B002)944 		NES_POKE_D (Nsf,Vrc6_B002) { chips->vrc6->WriteSawReg2( data );       }
945 
NES_POKE_D(Nsf,Vrc7_9010)946 		NES_POKE_D (Nsf,Vrc7_9010) { chips->vrc7->SelectReg( data ); }
NES_POKE_D(Nsf,Vrc7_9030)947 		NES_POKE_D (Nsf,Vrc7_9030) { chips->vrc7->WriteReg( data ); }
948 
NES_POKE_D(Nsf,S5b_C)949 		NES_POKE_D (Nsf,S5b_C) { chips->s5b->SelectReg( data ); }
NES_POKE_D(Nsf,S5b_E)950 		NES_POKE_D (Nsf,S5b_E) { chips->s5b->WriteReg( data );  }
951 
NES_PEEK(Nsf,N163_48)952 		NES_PEEK   (Nsf,N163_48) { return chips->n163->ReadData(); }
NES_POKE_D(Nsf,N163_48)953 		NES_POKE_D (Nsf,N163_48) { chips->n163->WriteData( data );  }
NES_POKE_D(Nsf,N163_F8)954 		NES_POKE_D (Nsf,N163_F8) { chips->n163->WriteAddress( data );  }
955 	}
956 }
957