1 // NES CPU emulator
2 
3 // $package
4 #ifndef NES_CPU_H
5 #define NES_CPU_H
6 
7 #include "blargg_common.h"
8 
9 class Nes_Cpu {
10 public:
11 	typedef BOOST::uint8_t byte;
12 	typedef int time_t;
13 	typedef int addr_t;
14 	enum { future_time = INT_MAX/2 + 1 };
15 
16 	// Clears registers and maps all pages to unmapped_page
17 	void reset( void const* unmapped_page = NULL );
18 
19 	// Maps code memory (memory accessed via the program counter). Start and size
20 	// must be multiple of page_size. If mirror_size is non-zero, the first
21 	// mirror_size bytes are repeated over the range. mirror_size must be a
22 	// multiple of page_size.
23 	enum { page_bits = 11 };
24 	enum { page_size = 1 << page_bits };
25 	void map_code( addr_t start, int size, void const* code, int mirror_size = 0 );
26 
27 	// Accesses emulated memory as CPU does
28 	byte const* get_code( addr_t ) const;
29 
30 	// NES 6502 registers. NOT kept updated during emulation.
31 	struct registers_t {
32 		BOOST::uint16_t pc;
33 		byte a;
34 		byte x;
35 		byte y;
36 		byte flags;
37 		byte sp;
38 	};
39 	registers_t r;
40 
41 	// Time of beginning of next instruction to be executed
time()42 	time_t time() const             { return cpu_state->time + cpu_state->base; }
set_time(time_t t)43 	void set_time( time_t t )       { cpu_state->time = t - cpu_state->base; }
adjust_time(int delta)44 	void adjust_time( int delta )   { cpu_state->time += delta; }
45 
46 	// Clocks past end (negative if before)
time_past_end()47 	int time_past_end() const       { return cpu_state->time; }
48 
49 	// Time of next IRQ
irq_time()50 	time_t irq_time() const         { return irq_time_; }
51 	void set_irq_time( time_t );
52 
53 	// Emulation stops once time >= end_time
end_time()54 	time_t end_time() const         { return end_time_; }
55 	void set_end_time( time_t );
56 
57 	// Number of unimplemented instructions encountered and skipped
clear_error_count()58 	void clear_error_count()        { error_count_ = 0; }
error_count()59 	unsigned error_count() const    { return error_count_; }
count_error()60 	void count_error()              { error_count_++; }
61 
62 	// Unmapped page should be filled with this
63 	enum { halt_opcode = 0x22 };
64 
65 	enum { irq_inhibit_mask = 0x04 };
66 
67 	// Can read this many bytes past end of a page
68 	enum { cpu_padding = 8 };
69 
70 private:
71 	// noncopyable
72 	Nes_Cpu( const Nes_Cpu& );
73 	Nes_Cpu& operator = ( const Nes_Cpu& );
74 
75 
76 // Implementation
77 public:
Nes_Cpu()78 	Nes_Cpu() { cpu_state = &cpu_state_; }
79 	enum { page_count = 0x10000 >> page_bits };
80 
81 	struct cpu_state_t {
82 		byte const* code_map [page_count + 1];
83 		time_t base;
84 		int time;
85 	};
86 	cpu_state_t* cpu_state; // points to cpu_state_ or a local copy
87 	cpu_state_t cpu_state_;
88 	time_t irq_time_;
89 	time_t end_time_;
90 	unsigned error_count_;
91 
92 private:
93 	void set_code_page( int, void const* );
94 	inline void update_end_time( time_t end, time_t irq );
95 };
96 
97 #define NES_CPU_PAGE( addr ) ((unsigned) (addr) >> Nes_Cpu::page_bits)
98 
99 #if BLARGG_NONPORTABLE
100 	#define NES_CPU_OFFSET( addr ) (addr)
101 #else
102 	#define NES_CPU_OFFSET( addr ) ((addr) & (Nes_Cpu::page_size - 1))
103 #endif
104 
get_code(addr_t addr)105 inline BOOST::uint8_t const* Nes_Cpu::get_code( addr_t addr ) const
106 {
107 	return cpu_state_.code_map [NES_CPU_PAGE( addr )] + NES_CPU_OFFSET( addr );
108 }
109 
update_end_time(time_t end,time_t irq)110 inline void Nes_Cpu::update_end_time( time_t end, time_t irq )
111 {
112 	if ( end > irq && !(r.flags & irq_inhibit_mask) )
113 		end = irq;
114 
115 	cpu_state->time += cpu_state->base - end;
116 	cpu_state->base = end;
117 }
118 
set_irq_time(time_t t)119 inline void Nes_Cpu::set_irq_time( time_t t )
120 {
121 	irq_time_ = t;
122 	update_end_time( end_time_, t );
123 }
124 
set_end_time(time_t t)125 inline void Nes_Cpu::set_end_time( time_t t )
126 {
127 	end_time_ = t;
128 	update_end_time( t, irq_time_ );
129 }
130 
131 #endif
132