Mercurial > spc_convert
comparison snes_spc/SNES_SPC.h @ 0:e38dacceb958
initial import
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Fri, 21 Oct 2011 05:53:11 -0700 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:e38dacceb958 |
---|---|
1 // SNES SPC-700 APU emulator | |
2 | |
3 // snes_spc 0.9.0 | |
4 #ifndef SNES_SPC_H | |
5 #define SNES_SPC_H | |
6 | |
7 #include "SPC_DSP.h" | |
8 #include "blargg_endian.h" | |
9 | |
10 struct SNES_SPC { | |
11 public: | |
12 typedef BOOST::uint8_t uint8_t; | |
13 | |
14 // Must be called once before using | |
15 blargg_err_t init(); | |
16 | |
17 // Sample pairs generated per second | |
18 enum { sample_rate = 32000 }; | |
19 | |
20 // Emulator use | |
21 | |
22 // Sets IPL ROM data. Library does not include ROM data. Most SPC music files | |
23 // don't need ROM, but a full emulator must provide this. | |
24 enum { rom_size = 0x40 }; | |
25 void init_rom( uint8_t const rom [rom_size] ); | |
26 | |
27 // Sets destination for output samples | |
28 typedef short sample_t; | |
29 void set_output( sample_t* out, int out_size ); | |
30 | |
31 // Number of samples written to output since last set | |
32 int sample_count() const; | |
33 | |
34 // Resets SPC to power-on state. This resets your output buffer, so you must | |
35 // call set_output() after this. | |
36 void reset(); | |
37 | |
38 // Emulates pressing reset switch on SNES. This resets your output buffer, so | |
39 // you must call set_output() after this. | |
40 void soft_reset(); | |
41 | |
42 // 1024000 SPC clocks per second, sample pair every 32 clocks | |
43 typedef int time_t; | |
44 enum { clock_rate = 1024000 }; | |
45 enum { clocks_per_sample = 32 }; | |
46 | |
47 // Emulated port read/write at specified time | |
48 enum { port_count = 4 }; | |
49 int read_port ( time_t, int port ); | |
50 void write_port( time_t, int port, int data ); | |
51 | |
52 // Runs SPC to end_time and starts a new time frame at 0 | |
53 void end_frame( time_t end_time ); | |
54 | |
55 // Sound control | |
56 | |
57 // Mutes voices corresponding to non-zero bits in mask (issues repeated KOFF events). | |
58 // Reduces emulation accuracy. | |
59 enum { voice_count = 8 }; | |
60 void mute_voices( int mask ); | |
61 | |
62 // If true, prevents channels and global volumes from being phase-negated. | |
63 // Only supported by fast DSP. | |
64 void disable_surround( bool disable = true ); | |
65 | |
66 // Sets tempo, where tempo_unit = normal, tempo_unit / 2 = half speed, etc. | |
67 enum { tempo_unit = 0x100 }; | |
68 void set_tempo( int ); | |
69 | |
70 // SPC music files | |
71 | |
72 // Loads SPC data into emulator | |
73 enum { spc_min_file_size = 0x10180 }; | |
74 enum { spc_file_size = 0x10200 }; | |
75 blargg_err_t load_spc( void const* in, long size ); | |
76 | |
77 // Clears echo region. Useful after loading an SPC as many have garbage in echo. | |
78 void clear_echo(); | |
79 | |
80 // Plays for count samples and write samples to out. Discards samples if out | |
81 // is NULL. Count must be a multiple of 2 since output is stereo. | |
82 blargg_err_t play( int count, sample_t* out ); | |
83 | |
84 // Skips count samples. Several times faster than play() when using fast DSP. | |
85 blargg_err_t skip( int count ); | |
86 | |
87 // State save/load (only available with accurate DSP) | |
88 | |
89 #if !SPC_NO_COPY_STATE_FUNCS | |
90 // Saves/loads state | |
91 enum { state_size = 67 * 1024L }; // maximum space needed when saving | |
92 typedef SPC_DSP::copy_func_t copy_func_t; | |
93 void copy_state( unsigned char** io, copy_func_t ); | |
94 | |
95 // Writes minimal header to spc_out | |
96 static void init_header( void* spc_out ); | |
97 | |
98 // Saves emulator state as SPC file data. Writes spc_file_size bytes to spc_out. | |
99 // Does not set up SPC header; use init_header() for that. | |
100 void save_spc( void* spc_out ); | |
101 | |
102 // Returns true if new key-on events occurred since last check. Useful for | |
103 // trimming silence while saving an SPC. | |
104 bool check_kon(); | |
105 #endif | |
106 | |
107 public: | |
108 BLARGG_DISABLE_NOTHROW | |
109 | |
110 typedef BOOST::uint16_t uint16_t; | |
111 | |
112 // Time relative to m_spc_time. Speeds up code a bit by eliminating need to | |
113 // constantly add m_spc_time to time from CPU. CPU uses time that ends at | |
114 // 0 to eliminate reloading end time every instruction. It pays off. | |
115 typedef int rel_time_t; | |
116 | |
117 struct Timer | |
118 { | |
119 rel_time_t next_time; // time of next event | |
120 int prescaler; | |
121 int period; | |
122 int divider; | |
123 int enabled; | |
124 int counter; | |
125 }; | |
126 enum { reg_count = 0x10 }; | |
127 enum { timer_count = 3 }; | |
128 enum { extra_size = SPC_DSP::extra_size }; | |
129 | |
130 enum { signature_size = 35 }; | |
131 | |
132 private: | |
133 SPC_DSP dsp; | |
134 | |
135 #if SPC_LESS_ACCURATE | |
136 static signed char const reg_times_ [256]; | |
137 signed char reg_times [256]; | |
138 #endif | |
139 | |
140 struct state_t | |
141 { | |
142 Timer timers [timer_count]; | |
143 | |
144 uint8_t smp_regs [2] [reg_count]; | |
145 | |
146 struct | |
147 { | |
148 int pc; | |
149 int a; | |
150 int x; | |
151 int y; | |
152 int psw; | |
153 int sp; | |
154 } cpu_regs; | |
155 | |
156 rel_time_t dsp_time; | |
157 time_t spc_time; | |
158 bool echo_accessed; | |
159 | |
160 int tempo; | |
161 int skipped_kon; | |
162 int skipped_koff; | |
163 const char* cpu_error; | |
164 | |
165 int extra_clocks; | |
166 sample_t* buf_begin; | |
167 sample_t const* buf_end; | |
168 sample_t* extra_pos; | |
169 sample_t extra_buf [extra_size]; | |
170 | |
171 int rom_enabled; | |
172 uint8_t rom [rom_size]; | |
173 uint8_t hi_ram [rom_size]; | |
174 | |
175 unsigned char cycle_table [256]; | |
176 | |
177 struct | |
178 { | |
179 // padding to neutralize address overflow | |
180 union { | |
181 uint8_t padding1 [0x100]; | |
182 uint16_t align; // makes compiler align data for 16-bit access | |
183 } padding1 [1]; | |
184 uint8_t ram [0x10000]; | |
185 uint8_t padding2 [0x100]; | |
186 } ram; | |
187 }; | |
188 state_t m; | |
189 | |
190 enum { rom_addr = 0xFFC0 }; | |
191 | |
192 enum { skipping_time = 127 }; | |
193 | |
194 // Value that padding should be filled with | |
195 enum { cpu_pad_fill = 0xFF }; | |
196 | |
197 enum { | |
198 r_test = 0x0, r_control = 0x1, | |
199 r_dspaddr = 0x2, r_dspdata = 0x3, | |
200 r_cpuio0 = 0x4, r_cpuio1 = 0x5, | |
201 r_cpuio2 = 0x6, r_cpuio3 = 0x7, | |
202 r_f8 = 0x8, r_f9 = 0x9, | |
203 r_t0target = 0xA, r_t1target = 0xB, r_t2target = 0xC, | |
204 r_t0out = 0xD, r_t1out = 0xE, r_t2out = 0xF | |
205 }; | |
206 | |
207 void timers_loaded(); | |
208 void enable_rom( int enable ); | |
209 void reset_buf(); | |
210 void save_extra(); | |
211 void load_regs( uint8_t const in [reg_count] ); | |
212 void ram_loaded(); | |
213 void regs_loaded(); | |
214 void reset_time_regs(); | |
215 void reset_common( int timer_counter_init ); | |
216 | |
217 Timer* run_timer_ ( Timer* t, rel_time_t ); | |
218 Timer* run_timer ( Timer* t, rel_time_t ); | |
219 int dsp_read ( rel_time_t ); | |
220 void dsp_write ( int data, rel_time_t ); | |
221 void cpu_write_smp_reg_( int data, rel_time_t, int addr ); | |
222 void cpu_write_smp_reg ( int data, rel_time_t, int addr ); | |
223 void cpu_write_high ( int data, int i, rel_time_t ); | |
224 void cpu_write ( int data, int addr, rel_time_t ); | |
225 int cpu_read_smp_reg ( int i, rel_time_t ); | |
226 int cpu_read ( int addr, rel_time_t ); | |
227 unsigned CPU_mem_bit ( uint8_t const* pc, rel_time_t ); | |
228 | |
229 bool check_echo_access ( int addr ); | |
230 uint8_t* run_until_( time_t end_time ); | |
231 | |
232 struct spc_file_t | |
233 { | |
234 char signature [signature_size]; | |
235 uint8_t has_id666; | |
236 uint8_t version; | |
237 uint8_t pcl, pch; | |
238 uint8_t a; | |
239 uint8_t x; | |
240 uint8_t y; | |
241 uint8_t psw; | |
242 uint8_t sp; | |
243 char text [212]; | |
244 uint8_t ram [0x10000]; | |
245 uint8_t dsp [128]; | |
246 uint8_t unused [0x40]; | |
247 uint8_t ipl_rom [0x40]; | |
248 }; | |
249 | |
250 static char const signature [signature_size + 1]; | |
251 | |
252 void save_regs( uint8_t out [reg_count] ); | |
253 }; | |
254 | |
255 #include <assert.h> | |
256 | |
257 inline int SNES_SPC::sample_count() const { return (m.extra_clocks >> 5) * 2; } | |
258 | |
259 inline int SNES_SPC::read_port( time_t t, int port ) | |
260 { | |
261 assert( (unsigned) port < port_count ); | |
262 return run_until_( t ) [port]; | |
263 } | |
264 | |
265 inline void SNES_SPC::write_port( time_t t, int port, int data ) | |
266 { | |
267 assert( (unsigned) port < port_count ); | |
268 run_until_( t ) [0x10 + port] = data; | |
269 } | |
270 | |
271 inline void SNES_SPC::mute_voices( int mask ) { dsp.mute_voices( mask ); } | |
272 | |
273 inline void SNES_SPC::disable_surround( bool disable ) { dsp.disable_surround( disable ); } | |
274 | |
275 #if !SPC_NO_COPY_STATE_FUNCS | |
276 inline bool SNES_SPC::check_kon() { return dsp.check_kon(); } | |
277 #endif | |
278 | |
279 #endif |