// NES 6502 CPU emulator // Game_Music_Emu 0.5.2 #ifndef NES_CPU_H #define NES_CPU_H #include "blargg_common.h" typedef blargg_long nes_time_t; // clock cycle count typedef unsigned nes_addr_t; // 16-bit address enum { future_nes_time = LONG_MAX / 2 + 1 }; class Nes_Cpu { public: typedef BOOST::uint8_t uint8_t; // 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). 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 emulated memory as CPU does uint8_t const* get_code( nes_addr_t ); // 2KB of RAM at address 0 uint8_t low_mem [0x800]; // NES 6502 registers. Not kept updated during a call to run(). struct registers_t { BOOST::uint16_t pc; BOOST::uint8_t a; BOOST::uint8_t x; BOOST::uint8_t y; BOOST::uint8_t status; BOOST::uint8_t sp; }; registers_t r; // 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 ); // 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 ); // Number of undefined instructions encountered and skipped void clear_error_count() { error_count_ = 0; } unsigned long error_count() const { return error_count_; } // CPU invokes bad opcode handler if it encounters this enum { bad_opcode = 0xF2 }; public: Nes_Cpu() { state = &state_; } enum { page_bits = 11 }; enum { page_count = 0x10000 >> page_bits }; enum { irq_inhibit = 0x04 }; private: 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_; unsigned long error_count_; void set_code_page( int, void const* ); inline int update_end_time( nes_time_t end, nes_time_t irq ); }; inline BOOST::uint8_t const* Nes_Cpu::get_code( nes_addr_t addr ) { return state->code_map [addr >> page_bits] + addr #if !BLARGG_NONPORTABLE % (unsigned) page_size #endif ; } inline int Nes_Cpu::update_end_time( nes_time_t t, nes_time_t irq ) { 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 ) { state->time += update_end_time( (end_time_ = t), irq_time_ ); } #endif