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