diff src/console/Nes_Cpu.h @ 316:fb513e10174e trunk

[svn] - merge libconsole-blargg into mainline libconsole: + obsoletes plugins-ugly:sapplug
author nenolod
date Thu, 30 Nov 2006 19:54:33 -0800
parents 3da1b8942b8b
children 986f098da058
line wrap: on
line diff
--- a/src/console/Nes_Cpu.h	Wed Nov 29 14:42:11 2006 -0800
+++ b/src/console/Nes_Cpu.h	Thu Nov 30 19:54:33 2006 -0800
@@ -1,61 +1,38 @@
-
-// Nintendo Entertainment System (NES) 6502 CPU emulator
+// NES 6502 CPU emulator
 
-// Game_Music_Emu 0.3.0
-
+// Game_Music_Emu 0.5.1
 #ifndef NES_CPU_H
 #define NES_CPU_H
 
 #include "blargg_common.h"
 
-typedef long     nes_time_t; // clock cycle count
+typedef blargg_long nes_time_t; // clock cycle count
 typedef unsigned nes_addr_t; // 16-bit address
-
-class Nes_Emu;
+enum { future_nes_time = LONG_MAX / 2 + 1 };
 
 class Nes_Cpu {
+public:
 	typedef BOOST::uint8_t uint8_t;
-	enum { page_bits = 11 };
-	enum { page_count = 0x10000 >> page_bits };
-	uint8_t const* code_map [page_count + 1];
-public:
-	Nes_Cpu();
 	
-	// Memory read/write function types. Reader must return value from 0 to 255.
-	typedef int (*reader_t)( Nes_Emu*, nes_addr_t );
-	typedef void (*writer_t)( Nes_Emu*, nes_addr_t, int data );
-	void set_emu( Nes_Emu* emu ) { callback_data = emu; }
-	
-	// Clear registers, unmap memory, and map code pages to unmapped_page.
-	void reset( const void* unmapped_page = NULL, reader_t read = NULL, writer_t write = NULL );
-	
-	// Memory mapping functions take a block of memory of specified 'start' address
-	// and 'size' in bytes. Both start address and size must be a multiple of page_size.
-	enum { page_size = 1L << page_bits };
+	// Clear registers, map low memory and its three mirrors to address 0,
+	// and mirror unmapped_page in remaining memory
+	void reset( void const* unmapped_page = 0 );
 	
-	// Map code memory (memory accessed via the program counter)
-	void map_code( nes_addr_t start, unsigned long size, const void* code );
-	
-	// Set read function for address range
-	void set_reader( nes_addr_t start, unsigned long size, reader_t );
-	
-	// Set write function for address range
-	void set_writer( nes_addr_t start, unsigned long size, writer_t );
-	
-	// Set read and write functions for address range
-	void map_memory( nes_addr_t start, unsigned long size, reader_t, writer_t );
+	// Map code memory (memory accessed via the program counter). Start and size
+	// must be multiple of page_size. If mirror is true, repeats code page
+	// throughout address range.
+	enum { page_size = 0x800 };
+	void map_code( nes_addr_t start, unsigned size, void const* code, bool mirror = false );
 	
-	// Access memory as the emulated CPU does.
-	int  read( nes_addr_t );
-	void write( nes_addr_t, int data );
-	uint8_t* get_code( nes_addr_t ); // non-const to allow debugger to modify code
+	// Access emulated memory as CPU does
+	uint8_t const* get_code( nes_addr_t );
 	
-	// Push a byte on the stack
-	void push_byte( int );
+	// 2KB of RAM at address 0
+	uint8_t low_mem [0x800];
 	
-	// NES 6502 registers. *Not* kept updated during a call to run().
+	// NES 6502 registers. Not kept updated during a call to run().
 	struct registers_t {
-		nes_addr_t pc; // more than 16 bits to allow overflow detection
+		BOOST::uint16_t pc;
 		BOOST::uint8_t a;
 		BOOST::uint8_t x;
 		BOOST::uint8_t y;
@@ -64,111 +41,74 @@
 	};
 	registers_t r;
 	
-	// Reasons that run() returns
-	enum result_t {
-		result_cycles,  // Requested number of cycles (or more) were executed
-		result_sei,     // I flag just set and IRQ time would generate IRQ now
-		result_cli,     // I flag just cleared but IRQ should occur *after* next instr
-		result_badop    // unimplemented/illegal instruction
-	};
+	// Set end_time and run CPU from current time. Returns true if execution
+	// stopped due to encountering bad_opcode.
+	bool run( nes_time_t end_time );
 	
-	result_t run( nes_time_t end_time_ );
+	// Time of beginning of next instruction to be executed
+	nes_time_t time() const             { return state->time + state->base; }
+	void set_time( nes_time_t t )       { state->time = t - state->base; }
+	void adjust_time( int delta )       { state->time += delta; }
+	
+	nes_time_t irq_time() const         { return irq_time_; }
+	void set_irq_time( nes_time_t );
+	
+	nes_time_t end_time() const         { return end_time_; }
+	void set_end_time( nes_time_t );
 	
-	nes_time_t time() const             { return base_time + clock_count; }
-	void set_time( nes_time_t t );
-	void end_frame( nes_time_t );
-	nes_time_t end_time() const         { return base_time + end_time_; }
-	nes_time_t irq_time() const         { return base_time + irq_time_; }
-	void set_end_time( nes_time_t t );
-	void set_irq_time( nes_time_t t );
+	// Number of undefined instructions encountered and skipped
+	void clear_error_count()            { error_count_ = 0; }
+	unsigned long error_count() const   { return error_count_; }
 	
-	// If PC exceeds 0xFFFF and encounters page_wrap_opcode, it will be silently wrapped.
-	enum { page_wrap_opcode = 0xF2 };
+	// CPU invokes bad opcode handler if it encounters this
+	enum { bad_opcode = 0xF2 };
 	
-	// One of the many opcodes that are undefined and stop CPU emulation.
-	enum { bad_opcode = 0xD2 };
-	
-	// End of public interface
+public:
+	Nes_Cpu() { state = &state_; }
+	enum { page_bits = 11 };
+	enum { page_count = 0x10000 >> page_bits };
+	enum { irq_inhibit = 0x04 };
 private:
-	// noncopyable
-	Nes_Cpu( const Nes_Cpu& );
-	Nes_Cpu& operator = ( const Nes_Cpu& );
-	
-	nes_time_t clock_limit;
-	nes_time_t base_time;
-	nes_time_t clock_count;
+	struct state_t {
+		uint8_t const* code_map [page_count + 1];
+		nes_time_t base;
+		int time;
+	};
+	state_t* state; // points to state_ or a local copy within run()
+	state_t state_;
 	nes_time_t irq_time_;
 	nes_time_t end_time_;
-	
-	Nes_Emu* callback_data;
+	unsigned long error_count_;
 	
-	enum { irq_inhibit = 0x04 };
-	reader_t data_reader [page_count + 1]; // extra entry catches address overflow
-	writer_t data_writer [page_count + 1];
-	void set_code_page( int, uint8_t const* );
-	void update_clock_limit();
-	
-public:
-	// low_mem is a full page size so it can be mapped with code_map
-	uint8_t low_mem [page_size > 0x800 ? page_size : 0x800];
+	void set_code_page( int, void const* );
+	inline int update_end_time( nes_time_t end, nes_time_t irq );
 };
 
-inline BOOST::uint8_t* Nes_Cpu::get_code( nes_addr_t addr )
+inline BOOST::uint8_t const* Nes_Cpu::get_code( nes_addr_t addr )
 {
-	#if BLARGG_NONPORTABLE
-		return (uint8_t*) code_map [addr >> page_bits] + addr;
-	#else
-		return (uint8_t*) code_map [addr >> page_bits] + (addr & (page_size - 1));
+	return state->code_map [addr >> page_bits] + addr
+	#if !BLARGG_NONPORTABLE
+		% (unsigned) page_size
 	#endif
+	;
 }
-	
-inline void Nes_Cpu::update_clock_limit()
+
+inline int Nes_Cpu::update_end_time( nes_time_t t, nes_time_t irq )
 {
-	nes_time_t t = end_time_;
-	if ( t > irq_time_ && !(r.status & irq_inhibit) )
-		t = irq_time_;
-	clock_limit = t;
+	if ( irq < t && !(r.status & irq_inhibit) ) t = irq;
+	int delta = state->base - t;
+	state->base = t;
+	return delta;
+}
+
+inline void Nes_Cpu::set_irq_time( nes_time_t t )
+{
+	state->time += update_end_time( end_time_, (irq_time_ = t) );
 }
 
 inline void Nes_Cpu::set_end_time( nes_time_t t )
 {
-	end_time_ = t - base_time;
-	update_clock_limit();
-}
-
-inline void Nes_Cpu::set_irq_time( nes_time_t t )
-{
-	irq_time_ = t - base_time;
-	update_clock_limit();
-}
-
-inline void Nes_Cpu::end_frame( nes_time_t end_time_ )
-{
-	base_time -= end_time_;
-	assert( time() >= 0 );
-}
-
-inline void Nes_Cpu::set_time( nes_time_t t )
-{
-	t -= time();
-	clock_limit -= t;
-	end_time_   -= t;
-	irq_time_   -= t;
-	base_time   += t;
-}
-
-inline void Nes_Cpu::push_byte( int data )
-{
-	int sp = r.sp;
-	r.sp = (sp - 1) & 0xff;
-	low_mem [0x100 + sp] = data;
-}
-
-inline void Nes_Cpu::map_memory( nes_addr_t addr, unsigned long s, reader_t r, writer_t w )
-{
-	set_reader( addr, s, r );
-	set_writer( addr, s, w );
+	state->time += update_end_time( (end_time_ = t), irq_time_ );
 }
 
 #endif
-