1 
2 // Nes_Emu 0.7.0. http://www.slack.net/~ant/
3 
4 #include "Nes_Core.h"
5 
6 #include <string.h>
7 #include "Nes_Mapper.h"
8 #include "Nes_State.h"
9 
10 /* Copyright (C) 2004-2006 Shay Green. This module is free software; you
11 can redistribute it and/or modify it under the terms of the GNU Lesser
12 General Public License as published by the Free Software Foundation; either
13 version 2.1 of the License, or (at your option) any later version. This
14 module is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
17 more details. You should have received a copy of the GNU Lesser General
18 Public License along with this module; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
20 
21 #include "blargg_source.h"
22 
23 extern const char unsupported_mapper [] = "Unsupported mapper";
24 
25 bool const wait_states_enabled = true;
26 bool const single_instruction_mode = false; // for debugging irq/nmi timing issues
27 
28 const int unmapped_fill = Nes_Cpu::page_wrap_opcode;
29 
30 unsigned const low_ram_size = 0x800;
31 unsigned const low_ram_end  = 0x2000;
32 unsigned const sram_end     = 0x8000;
33 
Nes_Core()34 Nes_Core::Nes_Core() : ppu( this )
35 {
36 	cart = NULL;
37 	impl = NULL;
38 	mapper = NULL;
39 	memset( &nes, 0, sizeof nes );
40 	memset( &joypad, 0, sizeof joypad );
41 }
42 
init()43 const char * Nes_Core::init()
44 {
45 	if ( !impl )
46 	{
47 		CHECK_ALLOC( impl = BLARGG_NEW impl_t );
48 		impl->apu.dmc_reader( read_dmc, this );
49 		impl->apu.irq_notifier( apu_irq_changed, this );
50 	}
51 
52 	return 0;
53 }
54 
close()55 void Nes_Core::close()
56 {
57 	cart = NULL;
58 	delete mapper;
59 	mapper = NULL;
60 
61 	ppu.close_chr();
62 
63 	disable_rendering();
64 }
65 
open(Nes_Cart const * new_cart)66 const char * Nes_Core::open( Nes_Cart const* new_cart )
67 {
68 	close();
69 
70 	RETURN_ERR( init() );
71 
72 	mapper = Nes_Mapper::create( new_cart, this );
73 	if ( !mapper )
74 		return unsupported_mapper;
75 
76 	RETURN_ERR( ppu.open_chr( new_cart->chr(), new_cart->chr_size() ) );
77 
78 	cart = new_cart;
79 	memset( impl->unmapped_page, unmapped_fill, sizeof impl->unmapped_page );
80 	reset( true, true );
81 
82 	return 0;
83 }
84 
~Nes_Core()85 Nes_Core::~Nes_Core()
86 {
87 	close();
88 	delete impl;
89 }
90 
save_state(Nes_State_ * out) const91 void Nes_Core::save_state( Nes_State_* out ) const
92 {
93 	out->clear();
94 
95 	out->nes = nes;
96 	out->nes_valid = true;
97 
98 	*out->cpu = cpu::r;
99 	out->cpu_valid = true;
100 
101 	*out->joypad = joypad;
102 	out->joypad_valid = true;
103 
104 	impl->apu.save_state( out->apu );
105 	out->apu_valid = true;
106 
107 	ppu.save_state( out );
108 
109 	memcpy( out->ram, cpu::low_mem, out->ram_size );
110 	out->ram_valid = true;
111 
112 	out->sram_size = 0;
113 	if ( sram_present )
114 	{
115 		out->sram_size = sizeof impl->sram;
116 		memcpy( out->sram, impl->sram, out->sram_size );
117 	}
118 
119 	out->mapper->size = 0;
120 	mapper->save_state( *out->mapper );
121 	out->mapper_valid = true;
122 }
123 
save_state(Nes_State * out) const124 void Nes_Core::save_state( Nes_State* out ) const
125 {
126 	save_state( reinterpret_cast<Nes_State_*>(out) );
127 }
128 
load_state(Nes_State_ const & in)129 void Nes_Core::load_state( Nes_State_ const& in )
130 {
131 	disable_rendering();
132 	error_count = 0;
133 
134 	if ( in.nes_valid )
135 		nes = in.nes;
136 
137 	// always use frame count
138 	ppu.burst_phase = 0; // avoids shimmer when seeking to same time over and over
139 	nes.frame_count = in.nes.frame_count;
140 	if ( (frame_count_t) nes.frame_count == invalid_frame_count )
141 		nes.frame_count = 0;
142 
143 	if ( in.cpu_valid )
144 		cpu::r = *in.cpu;
145 
146 	if ( in.joypad_valid )
147 		joypad = *in.joypad;
148 
149 	if ( in.apu_valid )
150 	{
151 		impl->apu.load_state( *in.apu );
152 		// prevent apu from running extra at beginning of frame
153 		impl->apu.end_frame( -(int) nes.timestamp / ppu_overclock );
154 	}
155 	else
156 	{
157 		impl->apu.reset();
158 	}
159 
160 	ppu.load_state( in );
161 
162 	if ( in.ram_valid )
163 		memcpy( cpu::low_mem, in.ram, in.ram_size );
164 
165 	sram_present = false;
166 	if ( in.sram_size )
167 	{
168 		sram_present = true;
169 		memcpy( impl->sram, in.sram, min( (int) in.sram_size, (int) sizeof impl->sram ) );
170 		enable_sram( true ); // mapper can override (read-only, unmapped, etc.)
171 	}
172 
173 	if ( in.mapper_valid ) // restore last since it might reconfigure things
174 		mapper->load_state( *in.mapper );
175 }
176 
enable_prg_6000()177 void Nes_Core::enable_prg_6000()
178 {
179 	sram_writable = 0;
180 	sram_readable = 0;
181 	lrom_readable = 0x8000;
182 }
183 
enable_sram(bool b,bool read_only)184 void Nes_Core::enable_sram( bool b, bool read_only )
185 {
186 	sram_writable = 0;
187 	if ( b )
188 	{
189 		if ( !sram_present )
190 		{
191 			sram_present = true;
192 			memset( impl->sram, 0xFF, impl->sram_size );
193 		}
194 		sram_readable = sram_end;
195 		if ( !read_only )
196 			sram_writable = sram_end;
197 		cpu::map_code( 0x6000, impl->sram_size, impl->sram );
198 	}
199 	else
200 	{
201 		sram_readable = 0;
202 		for ( int i = 0; i < impl->sram_size; i += cpu::page_size )
203 			cpu::map_code( 0x6000 + i, cpu::page_size, impl->unmapped_page );
204 	}
205 }
206 
207 // Unmapped memory
208 
209 #ifndef NDEBUG
210 static nes_addr_t last_unmapped_addr;
211 #endif
212 
log_unmapped(nes_addr_t addr,int data)213 void Nes_Core::log_unmapped( nes_addr_t addr, int data )
214 {
215 }
216 
cpu_adjust_time(int n)217 inline void Nes_Core::cpu_adjust_time( int n )
218 {
219 	ppu_2002_time   -= n;
220 	cpu_time_offset += n;
221 	cpu::reduce_limit( n );
222 }
223 
224 // I/O and sound
225 
read_dmc(void * data,nes_addr_t addr)226 int Nes_Core::read_dmc( void* data, nes_addr_t addr )
227 {
228 	Nes_Core* emu = (Nes_Core*) data;
229 	int result = *emu->cpu::get_code( addr );
230 	if ( wait_states_enabled )
231 		emu->cpu_adjust_time( 4 );
232 	return result;
233 }
234 
apu_irq_changed(void * emu)235 void Nes_Core::apu_irq_changed( void* emu )
236 {
237 	((Nes_Core*) emu)->irq_changed();
238 }
239 
write_io(nes_addr_t addr,int data)240 void Nes_Core::write_io( nes_addr_t addr, int data )
241 {
242 	// sprite dma
243 	if ( addr == 0x4014 )
244 	{
245 		ppu.dma_sprites( clock(), cpu::get_code( data * 0x100 ) );
246 		cpu_adjust_time( 513 );
247 		return;
248 	}
249 
250 	// joypad strobe
251 	if ( addr == 0x4016 )
252 	{
253 		// if strobe goes low, latch data
254 		if ( joypad.w4016 & 1 & ~data )
255 		{
256 			joypad_read_count++;
257 			joypad.joypad_latches [0] = current_joypad [0];
258 			joypad.joypad_latches [1] = current_joypad [1];
259 		}
260 		joypad.w4016 = data;
261 		return;
262 	}
263 
264 	// apu
265 	if ( unsigned (addr - impl->apu.start_addr) <= impl->apu.end_addr - impl->apu.start_addr )
266 	{
267 		impl->apu.write_register( clock(), addr, data );
268 		if ( wait_states_enabled )
269 		{
270 			if ( addr == 0x4010 || (addr == 0x4015 && (data & 0x10)) )
271 			{
272 				impl->apu.run_until( clock() + 1 );
273 				event_changed();
274 			}
275 		}
276 		return;
277 	}
278 
279 	#ifndef NDEBUG
280 		log_unmapped( addr, data );
281 	#endif
282 }
283 
read_io(nes_addr_t addr)284 int Nes_Core::read_io( nes_addr_t addr )
285 {
286 	if ( (addr & 0xFFFE) == 0x4016 )
287 	{
288 		// to do: to aid with recording, doesn't emulate transparent latch,
289 		// so a game that held strobe at 1 and read $4016 or $4017 would not get
290 		// the current A status as occurs on a NES
291 		unsigned long result = joypad.joypad_latches [addr & 1];
292 		if ( !(joypad.w4016 & 1) )
293 			joypad.joypad_latches [addr & 1] = (result >> 1) | 0x80000000;
294 		return result & 1;
295 	}
296 
297 	if ( addr == Nes_Apu::status_addr )
298 		return impl->apu.read_status( clock() );
299 
300 	#ifndef NDEBUG
301 		log_unmapped( addr );
302 	#endif
303 
304 	return addr >> 8; // simulate open bus
305 }
306 
307 // CPU
308 
309 const int irq_inhibit_mask = 0x04;
310 
read_vector(nes_addr_t addr)311 nes_addr_t Nes_Core::read_vector( nes_addr_t addr )
312 {
313 	uint8_t const* p = cpu::get_code( addr );
314 	return p [1] * 0x100 + p [0];
315 }
316 
reset(bool full_reset,bool erase_battery_ram)317 void Nes_Core::reset( bool full_reset, bool erase_battery_ram )
318 {
319 	if ( full_reset )
320 	{
321 		cpu::reset( impl->unmapped_page );
322 		cpu_time_offset = -1;
323 		clock_ = 0;
324 
325 		// Low RAM
326 		memset( cpu::low_mem, 0xFF, low_ram_size );
327 		cpu::low_mem [8] = 0xf7;
328 		cpu::low_mem [9] = 0xef;
329 		cpu::low_mem [10] = 0xdf;
330 		cpu::low_mem [15] = 0xbf;
331 
332 		// SRAM
333 		lrom_readable = 0;
334 		sram_present = true;
335 		enable_sram( false );
336 		if ( !cart->has_battery_ram() || erase_battery_ram )
337 			memset( impl->sram, 0xFF, impl->sram_size );
338 
339 		joypad.joypad_latches [0] = 0;
340 		joypad.joypad_latches [1] = 0;
341 
342 		nes.frame_count = 0;
343 	}
344 
345 	// to do: emulate partial reset
346 
347 	ppu.reset( full_reset );
348 	impl->apu.reset();
349 
350 	mapper->reset();
351 
352 	cpu::r.pc = read_vector( 0xFFFC );
353 	cpu::r.sp = 0xfd;
354 	cpu::r.a = 0;
355 	cpu::r.x = 0;
356 	cpu::r.y = 0;
357 	cpu::r.status = irq_inhibit_mask;
358 	nes.timestamp = 0;
359 	error_count = 0;
360 }
361 
vector_interrupt(nes_addr_t vector)362 void Nes_Core::vector_interrupt( nes_addr_t vector )
363 {
364 	cpu::push_byte( cpu::r.pc >> 8 );
365 	cpu::push_byte( cpu::r.pc & 0xFF );
366 	cpu::push_byte( cpu::r.status | 0x20 ); // reserved bit is set
367 
368 	cpu_adjust_time( 7 );
369 	cpu::r.status |= irq_inhibit_mask;
370 	cpu::r.pc = read_vector( vector );
371 }
372 
earliest_irq(nes_time_t present)373 inline nes_time_t Nes_Core::earliest_irq( nes_time_t present )
374 {
375 	return min( impl->apu.earliest_irq( present ), mapper->next_irq( present ) );
376 }
377 
irq_changed()378 void Nes_Core::irq_changed()
379 {
380 	cpu_set_irq_time( earliest_irq( cpu_time() ) );
381 }
382 
ppu_frame_length(nes_time_t present)383 inline nes_time_t Nes_Core::ppu_frame_length( nes_time_t present )
384 {
385 	nes_time_t t = ppu.frame_length();
386 	if ( t > present )
387 		return t;
388 
389 	ppu.render_bg_until( clock() ); // to do: why this call to clock() rather than using present?
390 	return ppu.frame_length();
391 }
392 
earliest_event(nes_time_t present)393 inline nes_time_t Nes_Core::earliest_event( nes_time_t present )
394 {
395 	// PPU frame
396 	nes_time_t t = ppu_frame_length( present );
397 
398 	// DMC
399 	if ( wait_states_enabled )
400 		t = min( t, impl->apu.next_dmc_read_time() + 1 );
401 
402 	// NMI
403 	t = min( t, ppu.nmi_time() );
404 
405 	if ( single_instruction_mode )
406 		t = min( t, present + 1 );
407 
408 	return t;
409 }
410 
event_changed()411 void Nes_Core::event_changed()
412 {
413 	cpu_set_end_time( earliest_event( cpu_time() ) );
414 }
415 
416 #undef NES_EMU_CPU_HOOK
417 #ifndef NES_EMU_CPU_HOOK
418 	#define NES_EMU_CPU_HOOK( cpu, end_time ) cpu::run( end_time )
419 #endif
420 
emulate_frame_()421 nes_time_t Nes_Core::emulate_frame_()
422 {
423 	Nes_Cpu::result_t last_result = cpu::result_cycles;
424 	int extra_instructions = 0;
425 	while ( true )
426 	{
427 		// Add DMC wait-states to CPU time
428 		if ( wait_states_enabled )
429 		{
430 			impl->apu.run_until( cpu_time() );
431 			clock_ = cpu_time_offset;
432 		}
433 
434 		nes_time_t present = cpu_time();
435 		if ( present >= ppu_frame_length( present ) )
436 		{
437 			if ( ppu.nmi_time() <= present )
438 			{
439 				// NMI will occur next, so delayed CLI and SEI don't need to be handled.
440 				// If NMI will occur normally ($2000.7 and $2002.7 set), let it occur
441 				// next frame, otherwise vector it now.
442 
443 				if ( !(ppu.w2000 & 0x80 & ppu.r2002) )
444 				{
445 					/* vectored NMI at end of frame */
446 					vector_interrupt( 0xFFFA );
447 					present += 7;
448 				}
449 				return present;
450 			}
451 
452 			if ( extra_instructions > 2 )
453 			{
454 				return present;
455 			}
456 
457 			if ( last_result != cpu::result_cli && last_result != cpu::result_sei &&
458 					(ppu.nmi_time() >= 0x10000 || (ppu.w2000 & 0x80 & ppu.r2002)) )
459 				return present;
460 
461 			/* Executing extra instructions for frame */
462 			extra_instructions++; // execute one more instruction
463 		}
464 
465 		// NMI
466 		if ( present >= ppu.nmi_time() )
467 		{
468 			ppu.acknowledge_nmi();
469 			vector_interrupt( 0xFFFA );
470 			last_result = cpu::result_cycles; // most recent sei/cli won't be delayed now
471 		}
472 
473 		// IRQ
474 		nes_time_t irq_time = earliest_irq( present );
475 		cpu_set_irq_time( irq_time );
476 		if ( present >= irq_time && (!(cpu::r.status & irq_inhibit_mask) ||
477 				last_result == cpu::result_sei) )
478 		{
479 			if ( last_result != cpu::result_cli )
480 			{
481 				/* IRQ vectored */
482 				mapper->run_until( present );
483 				vector_interrupt( 0xFFFE );
484 			}
485 			else
486 			{
487 				// CLI delays IRQ
488 				cpu_set_irq_time( present + 1 );
489 			}
490 		}
491 
492 		// CPU
493 		nes_time_t end_time = earliest_event( present );
494 		if ( extra_instructions )
495 			end_time = present + 1;
496 		unsigned long cpu_error_count = cpu::error_count();
497 		last_result = NES_EMU_CPU_HOOK( cpu, end_time - cpu_time_offset - 1 );
498 		cpu_adjust_time( cpu::time() );
499 		clock_ = cpu_time_offset;
500 		error_count += cpu::error_count() - cpu_error_count;
501 	}
502 }
503 
emulate_frame()504 nes_time_t Nes_Core::emulate_frame()
505 {
506 	joypad_read_count = 0;
507 
508 	cpu_time_offset = ppu.begin_frame( nes.timestamp ) - 1;
509 	ppu_2002_time = 0;
510 	clock_ = cpu_time_offset;
511 
512 	// TODO: clean this fucking mess up
513 	impl->apu.run_until_( emulate_frame_() );
514 	clock_ = cpu_time_offset;
515 	impl->apu.run_until_( cpu_time() );
516 
517 	nes_time_t ppu_frame_length = ppu.frame_length();
518 	nes_time_t length = cpu_time();
519 	nes.timestamp = ppu.end_frame( length );
520 	mapper->end_frame( length );
521 	impl->apu.end_frame( ppu_frame_length );
522 
523 	disable_rendering();
524 	nes.frame_count++;
525 
526 	return ppu_frame_length;
527 }
528 
add_mapper_intercept(nes_addr_t addr,unsigned size,bool read,bool write)529 void Nes_Core::add_mapper_intercept( nes_addr_t addr, unsigned size, bool read, bool write )
530 {
531 	int end = (addr + size + (page_size - 1)) >> page_bits;
532 	for ( int page = addr >> page_bits; page < end; page++ )
533 	{
534 		data_reader_mapped [page] |= read;
535 		data_writer_mapped [page] |= write;
536 	}
537 }
538