Mercurial > audlegacy-plugins
comparison 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 |
comparison
equal
deleted
inserted
replaced
315:2294f3a6f136 | 316:fb513e10174e |
---|---|
1 // NES 6502 CPU emulator | |
1 | 2 |
2 // Nintendo Entertainment System (NES) 6502 CPU emulator | 3 // Game_Music_Emu 0.5.1 |
3 | |
4 // Game_Music_Emu 0.3.0 | |
5 | |
6 #ifndef NES_CPU_H | 4 #ifndef NES_CPU_H |
7 #define NES_CPU_H | 5 #define NES_CPU_H |
8 | 6 |
9 #include "blargg_common.h" | 7 #include "blargg_common.h" |
10 | 8 |
11 typedef long nes_time_t; // clock cycle count | 9 typedef blargg_long nes_time_t; // clock cycle count |
12 typedef unsigned nes_addr_t; // 16-bit address | 10 typedef unsigned nes_addr_t; // 16-bit address |
13 | 11 enum { future_nes_time = LONG_MAX / 2 + 1 }; |
14 class Nes_Emu; | |
15 | 12 |
16 class Nes_Cpu { | 13 class Nes_Cpu { |
14 public: | |
17 typedef BOOST::uint8_t uint8_t; | 15 typedef BOOST::uint8_t uint8_t; |
18 enum { page_bits = 11 }; | |
19 enum { page_count = 0x10000 >> page_bits }; | |
20 uint8_t const* code_map [page_count + 1]; | |
21 public: | |
22 Nes_Cpu(); | |
23 | 16 |
24 // Memory read/write function types. Reader must return value from 0 to 255. | 17 // Clear registers, map low memory and its three mirrors to address 0, |
25 typedef int (*reader_t)( Nes_Emu*, nes_addr_t ); | 18 // and mirror unmapped_page in remaining memory |
26 typedef void (*writer_t)( Nes_Emu*, nes_addr_t, int data ); | 19 void reset( void const* unmapped_page = 0 ); |
27 void set_emu( Nes_Emu* emu ) { callback_data = emu; } | |
28 | 20 |
29 // Clear registers, unmap memory, and map code pages to unmapped_page. | 21 // Map code memory (memory accessed via the program counter). Start and size |
30 void reset( const void* unmapped_page = NULL, reader_t read = NULL, writer_t write = NULL ); | 22 // must be multiple of page_size. If mirror is true, repeats code page |
23 // throughout address range. | |
24 enum { page_size = 0x800 }; | |
25 void map_code( nes_addr_t start, unsigned size, void const* code, bool mirror = false ); | |
31 | 26 |
32 // Memory mapping functions take a block of memory of specified 'start' address | 27 // Access emulated memory as CPU does |
33 // and 'size' in bytes. Both start address and size must be a multiple of page_size. | 28 uint8_t const* get_code( nes_addr_t ); |
34 enum { page_size = 1L << page_bits }; | |
35 | 29 |
36 // Map code memory (memory accessed via the program counter) | 30 // 2KB of RAM at address 0 |
37 void map_code( nes_addr_t start, unsigned long size, const void* code ); | 31 uint8_t low_mem [0x800]; |
38 | 32 |
39 // Set read function for address range | 33 // NES 6502 registers. Not kept updated during a call to run(). |
40 void set_reader( nes_addr_t start, unsigned long size, reader_t ); | |
41 | |
42 // Set write function for address range | |
43 void set_writer( nes_addr_t start, unsigned long size, writer_t ); | |
44 | |
45 // Set read and write functions for address range | |
46 void map_memory( nes_addr_t start, unsigned long size, reader_t, writer_t ); | |
47 | |
48 // Access memory as the emulated CPU does. | |
49 int read( nes_addr_t ); | |
50 void write( nes_addr_t, int data ); | |
51 uint8_t* get_code( nes_addr_t ); // non-const to allow debugger to modify code | |
52 | |
53 // Push a byte on the stack | |
54 void push_byte( int ); | |
55 | |
56 // NES 6502 registers. *Not* kept updated during a call to run(). | |
57 struct registers_t { | 34 struct registers_t { |
58 nes_addr_t pc; // more than 16 bits to allow overflow detection | 35 BOOST::uint16_t pc; |
59 BOOST::uint8_t a; | 36 BOOST::uint8_t a; |
60 BOOST::uint8_t x; | 37 BOOST::uint8_t x; |
61 BOOST::uint8_t y; | 38 BOOST::uint8_t y; |
62 BOOST::uint8_t status; | 39 BOOST::uint8_t status; |
63 BOOST::uint8_t sp; | 40 BOOST::uint8_t sp; |
64 }; | 41 }; |
65 registers_t r; | 42 registers_t r; |
66 | 43 |
67 // Reasons that run() returns | 44 // Set end_time and run CPU from current time. Returns true if execution |
68 enum result_t { | 45 // stopped due to encountering bad_opcode. |
69 result_cycles, // Requested number of cycles (or more) were executed | 46 bool run( nes_time_t end_time ); |
70 result_sei, // I flag just set and IRQ time would generate IRQ now | 47 |
71 result_cli, // I flag just cleared but IRQ should occur *after* next instr | 48 // Time of beginning of next instruction to be executed |
72 result_badop // unimplemented/illegal instruction | 49 nes_time_t time() const { return state->time + state->base; } |
50 void set_time( nes_time_t t ) { state->time = t - state->base; } | |
51 void adjust_time( int delta ) { state->time += delta; } | |
52 | |
53 nes_time_t irq_time() const { return irq_time_; } | |
54 void set_irq_time( nes_time_t ); | |
55 | |
56 nes_time_t end_time() const { return end_time_; } | |
57 void set_end_time( nes_time_t ); | |
58 | |
59 // Number of undefined instructions encountered and skipped | |
60 void clear_error_count() { error_count_ = 0; } | |
61 unsigned long error_count() const { return error_count_; } | |
62 | |
63 // CPU invokes bad opcode handler if it encounters this | |
64 enum { bad_opcode = 0xF2 }; | |
65 | |
66 public: | |
67 Nes_Cpu() { state = &state_; } | |
68 enum { page_bits = 11 }; | |
69 enum { page_count = 0x10000 >> page_bits }; | |
70 enum { irq_inhibit = 0x04 }; | |
71 private: | |
72 struct state_t { | |
73 uint8_t const* code_map [page_count + 1]; | |
74 nes_time_t base; | |
75 int time; | |
73 }; | 76 }; |
74 | 77 state_t* state; // points to state_ or a local copy within run() |
75 result_t run( nes_time_t end_time_ ); | 78 state_t state_; |
76 | |
77 nes_time_t time() const { return base_time + clock_count; } | |
78 void set_time( nes_time_t t ); | |
79 void end_frame( nes_time_t ); | |
80 nes_time_t end_time() const { return base_time + end_time_; } | |
81 nes_time_t irq_time() const { return base_time + irq_time_; } | |
82 void set_end_time( nes_time_t t ); | |
83 void set_irq_time( nes_time_t t ); | |
84 | |
85 // If PC exceeds 0xFFFF and encounters page_wrap_opcode, it will be silently wrapped. | |
86 enum { page_wrap_opcode = 0xF2 }; | |
87 | |
88 // One of the many opcodes that are undefined and stop CPU emulation. | |
89 enum { bad_opcode = 0xD2 }; | |
90 | |
91 // End of public interface | |
92 private: | |
93 // noncopyable | |
94 Nes_Cpu( const Nes_Cpu& ); | |
95 Nes_Cpu& operator = ( const Nes_Cpu& ); | |
96 | |
97 nes_time_t clock_limit; | |
98 nes_time_t base_time; | |
99 nes_time_t clock_count; | |
100 nes_time_t irq_time_; | 79 nes_time_t irq_time_; |
101 nes_time_t end_time_; | 80 nes_time_t end_time_; |
81 unsigned long error_count_; | |
102 | 82 |
103 Nes_Emu* callback_data; | 83 void set_code_page( int, void const* ); |
104 | 84 inline int update_end_time( nes_time_t end, nes_time_t irq ); |
105 enum { irq_inhibit = 0x04 }; | |
106 reader_t data_reader [page_count + 1]; // extra entry catches address overflow | |
107 writer_t data_writer [page_count + 1]; | |
108 void set_code_page( int, uint8_t const* ); | |
109 void update_clock_limit(); | |
110 | |
111 public: | |
112 // low_mem is a full page size so it can be mapped with code_map | |
113 uint8_t low_mem [page_size > 0x800 ? page_size : 0x800]; | |
114 }; | 85 }; |
115 | 86 |
116 inline BOOST::uint8_t* Nes_Cpu::get_code( nes_addr_t addr ) | 87 inline BOOST::uint8_t const* Nes_Cpu::get_code( nes_addr_t addr ) |
117 { | 88 { |
118 #if BLARGG_NONPORTABLE | 89 return state->code_map [addr >> page_bits] + addr |
119 return (uint8_t*) code_map [addr >> page_bits] + addr; | 90 #if !BLARGG_NONPORTABLE |
120 #else | 91 % (unsigned) page_size |
121 return (uint8_t*) code_map [addr >> page_bits] + (addr & (page_size - 1)); | |
122 #endif | 92 #endif |
93 ; | |
123 } | 94 } |
124 | 95 |
125 inline void Nes_Cpu::update_clock_limit() | 96 inline int Nes_Cpu::update_end_time( nes_time_t t, nes_time_t irq ) |
126 { | 97 { |
127 nes_time_t t = end_time_; | 98 if ( irq < t && !(r.status & irq_inhibit) ) t = irq; |
128 if ( t > irq_time_ && !(r.status & irq_inhibit) ) | 99 int delta = state->base - t; |
129 t = irq_time_; | 100 state->base = t; |
130 clock_limit = t; | 101 return delta; |
102 } | |
103 | |
104 inline void Nes_Cpu::set_irq_time( nes_time_t t ) | |
105 { | |
106 state->time += update_end_time( end_time_, (irq_time_ = t) ); | |
131 } | 107 } |
132 | 108 |
133 inline void Nes_Cpu::set_end_time( nes_time_t t ) | 109 inline void Nes_Cpu::set_end_time( nes_time_t t ) |
134 { | 110 { |
135 end_time_ = t - base_time; | 111 state->time += update_end_time( (end_time_ = t), irq_time_ ); |
136 update_clock_limit(); | |
137 } | |
138 | |
139 inline void Nes_Cpu::set_irq_time( nes_time_t t ) | |
140 { | |
141 irq_time_ = t - base_time; | |
142 update_clock_limit(); | |
143 } | |
144 | |
145 inline void Nes_Cpu::end_frame( nes_time_t end_time_ ) | |
146 { | |
147 base_time -= end_time_; | |
148 assert( time() >= 0 ); | |
149 } | |
150 | |
151 inline void Nes_Cpu::set_time( nes_time_t t ) | |
152 { | |
153 t -= time(); | |
154 clock_limit -= t; | |
155 end_time_ -= t; | |
156 irq_time_ -= t; | |
157 base_time += t; | |
158 } | |
159 | |
160 inline void Nes_Cpu::push_byte( int data ) | |
161 { | |
162 int sp = r.sp; | |
163 r.sp = (sp - 1) & 0xff; | |
164 low_mem [0x100 + sp] = data; | |
165 } | |
166 | |
167 inline void Nes_Cpu::map_memory( nes_addr_t addr, unsigned long s, reader_t r, writer_t w ) | |
168 { | |
169 set_reader( addr, s, r ); | |
170 set_writer( addr, s, w ); | |
171 } | 112 } |
172 | 113 |
173 #endif | 114 #endif |
174 |