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