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