comparison Plugins/Input/console/Snes_Spc.cpp @ 493:c04dff121e1d trunk

[svn] hostile merge, phase 2: reimport based on new plugin code
author nenolod
date Tue, 24 Jan 2006 20:19:01 -0800
parents
children f12d7e208b43
comparison
equal deleted inserted replaced
492:ccb68bad47b2 493:c04dff121e1d
1
2 // Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
3
4 #include "Snes_Spc.h"
5
6 #include <assert.h>
7 #include <string.h>
8
9 /* Copyright (C) 2004-2006 Shay Green. This module is free software; you
10 can redistribute it and/or modify it under the terms of the GNU Lesser
11 General Public License as published by the Free Software Foundation; either
12 version 2.1 of the License, or (at your option) any later version. This
13 module is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
16 more details. You should have received a copy of the GNU Lesser General
17 Public License along with this module; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
19
20 #include BLARGG_SOURCE_BEGIN
21
22 // always in the future (CPU time can go over 0, but not by this much)
23 int const timer_disabled_time = 127;
24
25 Snes_Spc::Snes_Spc() : dsp( ram ), cpu( this, ram )
26 {
27 timer [0].shift = 7; // 8 kHz
28 timer [1].shift = 7; // 8 kHz
29 timer [2].shift = 4; // 64 kHz
30
31 // Put STOP instruction past end of memory to catch PC overflow.
32 memset( ram + ram_size, 0xff, (sizeof ram) - ram_size );
33 }
34
35 // Load
36
37 blargg_err_t Snes_Spc::load_spc( const void* data, long size, bool clear_echo_ )
38 {
39 struct spc_file_t {
40 char signature [27];
41 char unused [10];
42 uint8_t pc [2];
43 uint8_t a;
44 uint8_t x;
45 uint8_t y;
46 uint8_t status;
47 uint8_t sp;
48 char unused2 [212];
49 uint8_t ram [0x10000];
50 uint8_t dsp [128];
51 };
52 BOOST_STATIC_ASSERT( sizeof (spc_file_t) == spc_file_size );
53
54 const spc_file_t* spc = (spc_file_t*) data;
55
56 if ( size < spc_file_size )
57 return "Not an SPC file";
58
59 if ( strncmp( spc->signature, "SNES-SPC700 Sound File Data", 27 ) != 0 )
60 return "Not an SPC file";
61
62 registers_t regs;
63 regs.pc = spc->pc [1] * 0x100 + spc->pc [0];
64 regs.a = spc->a;
65 regs.x = spc->x;
66 regs.y = spc->y;
67 regs.status = spc->status;
68 regs.sp = spc->sp;
69
70 const char* error = load_state( regs, spc->ram, spc->dsp );
71
72 echo_accessed = false;
73
74 if ( clear_echo_ )
75 clear_echo();
76
77 return error;
78 }
79
80 void Snes_Spc::clear_echo()
81 {
82 if ( !(dsp.read( 0x6c ) & 0x20) )
83 {
84 unsigned addr = 0x100 * dsp.read( 0x6d );
85 unsigned size = 0x800 * dsp.read( 0x7d );
86 unsigned limit = ram_size - addr;
87 memset( ram + addr, 0xff, (size < limit) ? size : limit );
88 }
89 }
90
91 // Handle other file formats (emulator save states) in user code, not here.
92
93 blargg_err_t Snes_Spc::load_state( const registers_t& cpu_state, const void* new_ram,
94 const void* dsp_state )
95 {
96 // cpu
97 cpu.r = cpu_state;
98
99 // Allow DSP to generate one sample before code starts
100 // (Tengai Makyo Zero, Tenjin's Table Toss first notes are lost since it
101 // clears KON 31 cycles from starting execution. It works on the SNES
102 // since the SPC player adds a few extra cycles delay after restoring
103 // KON from the DSP registers at the end of an SPC file).
104 extra_cycles = 32;
105
106 // ram
107 memcpy( ram, new_ram, ram_size );
108 memcpy( extra_ram, ram + rom_addr, sizeof extra_ram );
109
110 // boot rom (have to force enable_rom() to update it)
111 rom_enabled = !(ram [0xf1] & 0x80);
112 enable_rom( !rom_enabled );
113
114 // dsp
115 dsp.reset();
116 int i;
117 for ( i = 0; i < Spc_Dsp::register_count; i++ )
118 dsp.write( i, ((uint8_t*) dsp_state) [i] );
119
120 // timers
121 for ( i = 0; i < timer_count; i++ )
122 {
123 Timer& t = timer [i];
124
125 t.next_tick = 0;
126 t.enabled = (ram [0xf1] >> i) & 1;
127 if ( !t.enabled )
128 t.next_tick = timer_disabled_time;
129 t.count = 0;
130 t.counter = ram [0xfd + i] & 15;
131
132 int p = ram [0xfa + i];
133 t.period = p ? p : 0x100;
134 }
135
136 // Handle registers which already give 0 when read by setting RAM and not changing it.
137 // Put STOP instruction in registers which can be read, to catch attempted CPU execution.
138 ram [0xf0] = 0;
139 ram [0xf1] = 0;
140 ram [0xf3] = 0xff;
141 ram [0xfa] = 0;
142 ram [0xfb] = 0;
143 ram [0xfc] = 0;
144 ram [0xfd] = 0xff;
145 ram [0xfe] = 0xff;
146 ram [0xff] = 0xff;
147
148 return NULL; // success
149 }
150
151 // Hardware
152
153 // Current time starts negative and ends at 0
154 inline spc_time_t Snes_Spc::time() const
155 {
156 return -cpu.remain();
157 }
158
159 // Keep track of next time to run and avoid a function call if it hasn't been reached.
160
161 // Timers
162
163 void Snes_Spc::Timer::run_until_( spc_time_t time )
164 {
165 if ( !enabled )
166 dprintf( "next_tick: %ld, time: %ld", (long) next_tick, (long) time );
167 assert( enabled ); // when disabled, next_tick should always be in the future
168
169 int elapsed = ((time - next_tick) >> shift) + 1;
170 next_tick += elapsed << shift;
171 elapsed += count;
172 if ( elapsed >= period ) { // avoid costly divide
173 int n = elapsed / period;
174 elapsed -= n * period;
175 counter = (counter + n) & 15;
176 }
177 count = elapsed;
178 }
179
180 // DSP
181
182 const int clocks_per_sample = 32; // 1.024 MHz CPU clock / 32000 samples per second
183
184 void Snes_Spc::run_dsp_( spc_time_t time )
185 {
186 int count = ((time - next_dsp) >> 5) + 1; // divide by clocks_per_sample
187 sample_t* buf = sample_buf;
188 if ( buf ) {
189 sample_buf = buf + count * 2; // stereo
190 assert( sample_buf <= buf_end );
191 }
192 next_dsp += count * clocks_per_sample;
193 dsp.run( count, buf );
194 }
195
196 inline void Snes_Spc::run_dsp( spc_time_t time )
197 {
198 if ( time >= next_dsp )
199 run_dsp_( time );
200 }
201
202 // Debug-only check for read/write within echo buffer, since this might result in
203 // inaccurate emulation due to the DSP not being caught up to the present.
204 inline void Snes_Spc::check_for_echo_access( spc_addr_t addr )
205 {
206 if ( !echo_accessed && !(dsp.read( 0x6c ) & 0x20) )
207 {
208 // ** If echo accesses are found that require running the DSP, cache
209 // the start and end address on DSP writes to speed up checking.
210
211 unsigned start = 0x100 * dsp.read( 0x6d );
212 unsigned end = start + 0x800 * dsp.read( 0x7d );
213 if ( start <= addr && addr < end ) {
214 echo_accessed = true;
215 dprintf( "Read/write at $%04X within echo buffer\n", (unsigned) addr );
216 }
217 }
218 }
219
220 // Read
221
222 int Snes_Spc::read( spc_addr_t addr )
223 {
224 // zero page ram is used most often
225 if ( addr < 0xf0 )
226 return ram [addr];
227
228 // dsp
229 if ( addr == 0xf3 ) {
230 run_dsp( time() );
231 if ( ram [0xf2] >= Spc_Dsp::register_count )
232 dprintf( "DSP read from $%02X\n", (int) ram [0xf2] );
233 return dsp.read( ram [0xf2] & 0x7f );
234 }
235
236 // counters
237 unsigned i = addr - 0xfd; // negative converts to large positive unsigned
238 if ( i < timer_count ) {
239 Timer& t = timer [i];
240 t.run_until( time() );
241 int result = t.counter;
242 t.counter = 0;
243 return result;
244 }
245
246 if ( addr == 0xf0 || addr == 0xf1 || addr == 0xf8 ||
247 addr == 0xf9 || addr == 0xfa )
248 dprintf( "Read from register $%02X\n", (int) addr );
249
250 // Registers which always read as 0 are handled by setting ram [reg] to 0
251 // at startup then never changing that value.
252
253 check(( check_for_echo_access( addr ), true ));
254
255 // ram
256 return ram [addr];
257 }
258
259
260 // Write
261
262 const unsigned char Snes_Spc::boot_rom [rom_size] = { // verified
263 0xCD, 0xEF, 0xBD, 0xE8, 0x00, 0xC6, 0x1D, 0xD0,
264 0xFC, 0x8F, 0xAA, 0xF4, 0x8F, 0xBB, 0xF5, 0x78,
265 0xCC, 0xF4, 0xD0, 0xFB, 0x2F, 0x19, 0xEB, 0xF4,
266 0xD0, 0xFC, 0x7E, 0xF4, 0xD0, 0x0B, 0xE4, 0xF5,
267 0xCB, 0xF4, 0xD7, 0x00, 0xFC, 0xD0, 0xF3, 0xAB,
268 0x01, 0x10, 0xEF, 0x7E, 0xF4, 0x10, 0xEB, 0xBA,
269 0xF6, 0xDA, 0x00, 0xBA, 0xF4, 0xC4, 0xF4, 0xDD,
270 0x5D, 0xD0, 0xDB, 0x1F, 0x00, 0x00, 0xC0, 0xFF
271 };
272
273 void Snes_Spc::enable_rom( bool enable )
274 {
275 if ( rom_enabled != enable )
276 {
277 rom_enabled = enable;
278 memcpy( ram + rom_addr, (enable ? boot_rom : extra_ram), rom_size );
279 }
280 }
281
282 void Snes_Spc::write( spc_addr_t addr, int data )
283 {
284 // first page is very common
285 if ( addr < 0xf0 ) {
286 ram [addr] = data;
287 }
288 else switch ( addr )
289 {
290 // RAM
291 default:
292 check(( check_for_echo_access( addr ), true ));
293 if ( addr < rom_addr ) {
294 ram [addr] = data;
295 }
296 else {
297 extra_ram [addr - rom_addr] = data;
298 if ( !rom_enabled )
299 ram [addr] = data;
300 }
301 break;
302
303 // DSP
304 //case 0xf2: // mapped to RAM
305 case 0xf3: {
306 run_dsp( time() );
307 int reg = ram [0xf2];
308 if ( next_dsp > 0 ) {
309 // skip mode
310
311 // key press
312 if ( reg == 0x4C )
313 keys_pressed |= data & ~dsp.read( 0x5C );
314
315 // key release
316 if ( reg == 0x5C ) {
317 keys_released |= data;
318 keys_pressed &= ~data;
319 }
320 }
321 if ( reg < Spc_Dsp::register_count ) {
322 dsp.write( reg, data );
323 }
324 else {
325 dprintf( "DSP write to $%02X\n", (int) reg );
326 }
327 break;
328 }
329
330 case 0xf0: // Test register
331 dprintf( "Wrote $%02X to $F0\n", (int) data );
332 break;
333
334 // Config
335 case 0xf1:
336 {
337 // timers
338 for ( int i = 0; i < timer_count; i++ )
339 {
340 Timer& t = timer [i];
341 if ( !(data & (1 << i)) ) {
342 t.enabled = 0;
343 t.next_tick = timer_disabled_time;
344 }
345 else if ( !t.enabled ) {
346 // just enabled
347 t.enabled = 1;
348 t.counter = 0;
349 t.count = 0;
350 t.next_tick = time();
351 }
352 }
353
354 // port clears
355 if ( data & 0x10 ) {
356 ram [0xf4] = 0;
357 ram [0xf5] = 0;
358 }
359 if ( data & 0x20 ) {
360 ram [0xf6] = 0;
361 ram [0xf7] = 0;
362 }
363
364 enable_rom( data & 0x80 );
365
366 break;
367 }
368
369 // Ports
370 case 0xf4:
371 case 0xf5:
372 case 0xf6:
373 case 0xf7:
374 // to do: handle output ports
375 break;
376
377 //case 0xf8: // verified on SNES that these are read/write (RAM)
378 //case 0xf9:
379
380 // Timers
381 case 0xfa:
382 case 0xfb:
383 case 0xfc: {
384 Timer& t = timer [addr - 0xfa];
385 if ( (t.period & 0xff) != data ) {
386 t.run_until( time() );
387 t.period = data ? data : 0x100;
388 }
389 break;
390 }
391
392 // Counters (cleared on write)
393 case 0xfd:
394 case 0xfe:
395 case 0xff:
396 dprintf( "Wrote to counter $%02X\n", (int) addr );
397 timer [addr - 0xfd].counter = 0;
398 break;
399 }
400 }
401
402 // Play
403
404 blargg_err_t Snes_Spc::skip( long count )
405 {
406 if ( count > 4 * 32000L )
407 {
408 // don't run DSP for long durations (2-3 times faster)
409
410 const long sync_count = 32000L * 2;
411
412 // keep track of any keys pressed/released (and not subsequently released)
413 keys_pressed = 0;
414 keys_released = 0;
415 // sentinel tells play to ignore DSP
416 BLARGG_RETURN_ERR( play( count - sync_count, skip_sentinel ) );
417
418 // press/release keys now
419 dsp.write( 0x5C, keys_released & ~keys_pressed );
420 dsp.write( 0x4C, keys_pressed );
421
422 clear_echo();
423
424 // play the last few seconds normally to help synchronize DSP
425 count = sync_count;
426 }
427
428 return play( count );
429 }
430
431 blargg_err_t Snes_Spc::play( long count, sample_t* out )
432 {
433 require( count % 2 == 0 ); // output is always in pairs of samples
434
435 // CPU time() runs from -duration to 0
436 spc_time_t duration = (count / 2) * clocks_per_sample;
437
438 // DSP output is made on-the-fly when the CPU reads/writes DSP registers
439 sample_buf = out;
440 buf_end = out + (out && out != skip_sentinel ? count : 0);
441 next_dsp = (out == skip_sentinel) ? clocks_per_sample : -duration + clocks_per_sample;
442
443 // Localize timer next_tick times and run them to the present to prevent a running
444 // but ignored timer's next_tick from getting too far behind and overflowing.
445 for ( int i = 0; i < timer_count; i++ )
446 {
447 Timer& t = timer [i];
448 if ( t.enabled )
449 {
450 t.next_tick -= duration;
451 t.run_until( -duration );
452 }
453 }
454
455 // Run CPU for duration, reduced by any extra cycles from previous run
456 int elapsed = cpu.run( duration - extra_cycles );
457 if ( elapsed > 0 )
458 {
459 dprintf( "Unhandled instruction $%02X, pc = $%04X\n",
460 (int) cpu.read( cpu.r.pc ), (unsigned) cpu.r.pc );
461 return "Emulation error";
462 }
463 extra_cycles = -elapsed;
464
465 // Catch DSP up to present.
466 run_dsp( 0 );
467 if ( out ) {
468 assert( next_dsp == clocks_per_sample );
469 assert( out == skip_sentinel || sample_buf - out == count );
470 }
471 buf_end = NULL;
472
473 return blargg_success;
474 }
475