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