comparison src/console/Snes_Spc.cxx @ 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 1 // Game_Music_Emu 0.5.1. http://www.slack.net/~ant/
2 // Game_Music_Emu 0.3.0. http://www.slack.net/~ant/
3 2
4 #include "Snes_Spc.h" 3 #include "Snes_Spc.h"
5 4
6 #include <assert.h>
7 #include <string.h> 5 #include <string.h>
8 6
9 /* Copyright (C) 2004-2006 Shay Green. This module is free software; you 7 /* 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 8 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 9 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 10 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 11 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 12 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for 13 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
16 more details. You should have received a copy of the GNU Lesser General 14 details. You should have received a copy of the GNU Lesser General Public
17 Public License along with this module; if not, write to the Free Software 15 License along with this module; if not, write to the Free Software Foundation,
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ 16 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
19 17
20 #include BLARGG_SOURCE_BEGIN 18 #include "blargg_source.h"
21 19
22 // always in the future (CPU time can go over 0, but not by this much) 20 // always in the future (CPU time can go over 0, but not by this much)
23 int const timer_disabled_time = 127; 21 int const timer_disabled_time = 127;
24 22
25 Snes_Spc::Snes_Spc() : dsp( ram ), cpu( this, ram ) 23 Snes_Spc::Snes_Spc() : dsp( ram ), cpu( this, ram )
26 { 24 {
27 timer [0].shift = 7; // 8 kHz 25 set_tempo( 1.0 );
28 timer [1].shift = 7; // 8 kHz
29 timer [2].shift = 4; // 64 kHz
30 26
31 // Put STOP instruction past end of memory to catch PC overflow. 27 // Put STOP instruction past end of memory to catch PC overflow.
32 memset( ram + ram_size, 0xff, (sizeof ram) - ram_size ); 28 memset( ram + ram_size, 0xFF, (sizeof ram) - ram_size );
29
30 // A few tracks read from the last four bytes of IPL ROM
31 boot_rom [sizeof boot_rom - 2] = 0xC0;
32 boot_rom [sizeof boot_rom - 1] = 0xFF;
33 memset( boot_rom, 0, sizeof boot_rom - 2 );
34 }
35
36 void Snes_Spc::set_tempo( double t )
37 {
38 int unit = (int) (16.0 / t + 0.5);
39
40 timer [0].divisor = unit * 8; // 8 kHz
41 timer [1].divisor = unit * 8; // 8 kHz
42 timer [2].divisor = unit; // 64 kHz
33 } 43 }
34 44
35 // Load 45 // Load
36 46
37 blargg_err_t Snes_Spc::load_spc( const void* data, long size, bool clear_echo_ ) 47 void Snes_Spc::set_ipl_rom( void const* in )
48 {
49 memcpy( boot_rom, in, sizeof boot_rom );
50 }
51
52 blargg_err_t Snes_Spc::load_spc( const void* data, long size )
38 { 53 {
39 struct spc_file_t { 54 struct spc_file_t {
40 char signature [27]; 55 char signature [27];
41 char unused [10]; 56 char unused [10];
42 uint8_t pc [2]; 57 uint8_t pc [2];
46 uint8_t status; 61 uint8_t status;
47 uint8_t sp; 62 uint8_t sp;
48 char unused2 [212]; 63 char unused2 [212];
49 uint8_t ram [0x10000]; 64 uint8_t ram [0x10000];
50 uint8_t dsp [128]; 65 uint8_t dsp [128];
66 uint8_t ipl_rom [128];
51 }; 67 };
52 BOOST_STATIC_ASSERT( sizeof (spc_file_t) == spc_file_size ); 68 BOOST_STATIC_ASSERT( sizeof (spc_file_t) == spc_file_size + 128 );
53 69
54 const spc_file_t* spc = (spc_file_t*) data; 70 const spc_file_t* spc = (spc_file_t const*) data;
55 71
56 if ( size < spc_file_size ) 72 if ( size < spc_file_size )
57 return "Not an SPC file"; 73 return "Not an SPC file";
58 74
59 if ( strncmp( spc->signature, "SNES-SPC700 Sound File Data", 27 ) != 0 ) 75 if ( strncmp( spc->signature, "SNES-SPC700 Sound File Data", 27 ) != 0 )
60 return "Not an SPC file"; 76 return "Not an SPC file";
61 77
62 registers_t regs; 78 registers_t regs;
63 regs.pc = spc->pc [1] * 0x100 + spc->pc [0]; 79 regs.pc = spc->pc [1] * 0x100 + spc->pc [0];
64 regs.a = spc->a; 80 regs.a = spc->a;
65 regs.x = spc->x; 81 regs.x = spc->x;
66 regs.y = spc->y; 82 regs.y = spc->y;
67 regs.status = spc->status; 83 regs.status = spc->status;
68 regs.sp = spc->sp; 84 regs.sp = spc->sp;
85
86 if ( (unsigned long) size >= sizeof *spc )
87 set_ipl_rom( spc->ipl_rom );
69 88
70 const char* error = load_state( regs, spc->ram, spc->dsp ); 89 const char* error = load_state( regs, spc->ram, spc->dsp );
71 90
72 echo_accessed = false; 91 echo_accessed = false;
73 92
74 if ( clear_echo_ )
75 clear_echo();
76
77 return error; 93 return error;
78 } 94 }
79 95
80 void Snes_Spc::clear_echo() 96 void Snes_Spc::clear_echo()
81 { 97 {
82 if ( !(dsp.read( 0x6c ) & 0x20) ) 98 if ( !(dsp.read( 0x6C ) & 0x20) )
83 { 99 {
84 unsigned addr = 0x100 * dsp.read( 0x6d ); 100 unsigned addr = 0x100 * dsp.read( 0x6D );
85 unsigned size = 0x800 * dsp.read( 0x7d ); 101 unsigned size = 0x800 * dsp.read( 0x7D );
86 unsigned limit = ram_size - addr; 102 memset( ram + addr, 0xFF, min( size, ram_size - addr ) );
87 memset( ram + addr, 0xff, (size < limit) ? size : limit );
88 } 103 }
89 } 104 }
90 105
91 // Handle other file formats (emulator save states) in user code, not here. 106 // Handle other file formats (emulator save states) in user code, not here.
92 107
106 // ram 121 // ram
107 memcpy( ram, new_ram, ram_size ); 122 memcpy( ram, new_ram, ram_size );
108 memcpy( extra_ram, ram + rom_addr, sizeof extra_ram ); 123 memcpy( extra_ram, ram + rom_addr, sizeof extra_ram );
109 124
110 // boot rom (have to force enable_rom() to update it) 125 // boot rom (have to force enable_rom() to update it)
111 rom_enabled = !(ram [0xf1] & 0x80); 126 rom_enabled = !(ram [0xF1] & 0x80);
112 enable_rom( !rom_enabled ); 127 enable_rom( !rom_enabled );
113 128
114 // dsp 129 // dsp
115 dsp.reset(); 130 dsp.reset();
116 int i; 131 int i;
117 for ( i = 0; i < Spc_Dsp::register_count; i++ ) 132 for ( i = 0; i < Spc_Dsp::register_count; i++ )
118 dsp.write( i, ((uint8_t*) dsp_state) [i] ); 133 dsp.write( i, ((uint8_t const*) dsp_state) [i] );
119 134
120 // timers 135 // timers
121 for ( i = 0; i < timer_count; i++ ) 136 for ( i = 0; i < timer_count; i++ )
122 { 137 {
123 Timer& t = timer [i]; 138 Timer& t = timer [i];
124 139
125 t.next_tick = 0; 140 t.next_tick = 0;
126 t.enabled = (ram [0xf1] >> i) & 1; 141 t.enabled = (ram [0xF1] >> i) & 1;
127 if ( !t.enabled ) 142 if ( !t.enabled )
128 t.next_tick = timer_disabled_time; 143 t.next_tick = timer_disabled_time;
129 t.count = 0; 144 t.count = 0;
130 t.counter = ram [0xfd + i] & 15; 145 t.counter = ram [0xFD + i] & 15;
131 146
132 int p = ram [0xfa + i]; 147 int p = ram [0xFA + i];
133 t.period = p ? p : 0x100; 148 t.period = p ? p : 0x100;
134 } 149 }
135 150
136 // Handle registers which already give 0 when read by setting RAM and not changing it. 151 // 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. 152 // Put STOP instruction in registers which can be read, to catch attempted CPU execution.
138 ram [0xf0] = 0; 153 ram [0xF0] = 0;
139 ram [0xf1] = 0; 154 ram [0xF1] = 0;
140 ram [0xf3] = 0xff; 155 ram [0xF3] = 0xFF;
141 ram [0xfa] = 0; 156 ram [0xFA] = 0;
142 ram [0xfb] = 0; 157 ram [0xFB] = 0;
143 ram [0xfc] = 0; 158 ram [0xFC] = 0;
144 ram [0xfd] = 0xff; 159 ram [0xFD] = 0xFF;
145 ram [0xfe] = 0xff; 160 ram [0xFE] = 0xFF;
146 ram [0xff] = 0xff; 161 ram [0xFF] = 0xFF;
147 162
148 return NULL; // success 163 return 0; // success
149 } 164 }
150 165
151 // Hardware 166 // Hardware
152 167
153 // Current time starts negative and ends at 0 168 // Current time starts negative and ends at 0
164 { 179 {
165 if ( !enabled ) 180 if ( !enabled )
166 dprintf( "next_tick: %ld, time: %ld", (long) next_tick, (long) time ); 181 dprintf( "next_tick: %ld, time: %ld", (long) next_tick, (long) time );
167 assert( enabled ); // when disabled, next_tick should always be in the future 182 assert( enabled ); // when disabled, next_tick should always be in the future
168 183
169 int elapsed = ((time - next_tick) >> shift) + 1; 184 int elapsed = ((time - next_tick) / divisor) + 1;
170 next_tick += elapsed << shift; 185 next_tick += elapsed * divisor;
186
171 elapsed += count; 187 elapsed += count;
172 if ( elapsed >= period ) { // avoid costly divide 188 if ( elapsed >= period ) // avoid unnecessary division
189 {
173 int n = elapsed / period; 190 int n = elapsed / period;
174 elapsed -= n * period; 191 elapsed -= n * period;
175 counter = (counter + n) & 15; 192 counter = (counter + n) & 15;
176 } 193 }
177 count = elapsed; 194 count = elapsed;
201 218
202 // Debug-only check for read/write within echo buffer, since this might result in 219 // 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. 220 // 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 ) 221 inline void Snes_Spc::check_for_echo_access( spc_addr_t addr )
205 { 222 {
206 if ( !echo_accessed && !(dsp.read( 0x6c ) & 0x20) ) 223 if ( !echo_accessed && !(dsp.read( 0x6C ) & 0x20) )
207 { 224 {
208 // ** If echo accesses are found that require running the DSP, cache 225 // ** If echo accesses are found that require running the DSP, cache
209 // the start and end address on DSP writes to speed up checking. 226 // the start and end address on DSP writes to speed up checking.
210 227
211 unsigned start = 0x100 * dsp.read( 0x6d ); 228 unsigned start = 0x100 * dsp.read( 0x6D );
212 unsigned end = start + 0x800 * dsp.read( 0x7d ); 229 unsigned end = start + 0x800 * dsp.read( 0x7D );
213 if ( start <= addr && addr < end ) { 230 if ( start <= addr && addr < end ) {
214 echo_accessed = true; 231 echo_accessed = true;
215 dprintf( "Read/write at $%04X within echo buffer\n", (unsigned) addr ); 232 dprintf( "Read/write at $%04X within echo buffer\n", (unsigned) addr );
216 } 233 }
217 } 234 }
219 236
220 // Read 237 // Read
221 238
222 int Snes_Spc::read( spc_addr_t addr ) 239 int Snes_Spc::read( spc_addr_t addr )
223 { 240 {
224 // zero page ram is used most often 241 int result = ram [addr];
225 if ( addr < 0xf0 ) 242
226 return ram [addr]; 243 if ( (rom_addr <= addr && addr < 0xFFFC || addr >= 0xFFFE) && rom_enabled )
227 244 dprintf( "Read from ROM: %04X -> %02X\n", addr, result );
228 // dsp 245
229 if ( addr == 0xf3 ) { 246 if ( unsigned (addr - 0xF0) < 0x10 )
230 run_dsp( time() ); 247 {
231 if ( ram [0xf2] >= Spc_Dsp::register_count ) 248 assert( 0xF0 <= addr && addr <= 0xFF );
232 dprintf( "DSP read from $%02X\n", (int) ram [0xf2] ); 249
233 return dsp.read( ram [0xf2] & 0x7f ); 250 // counters
234 } 251 int i = addr - 0xFD;
235 252 if ( i >= 0 )
236 // counters 253 {
237 unsigned i = addr - 0xfd; // negative converts to large positive unsigned 254 Timer& t = timer [i];
238 if ( i < timer_count ) { 255 t.run_until( time() );
239 Timer& t = timer [i]; 256 int result = t.counter;
240 t.run_until( time() ); 257 t.counter = 0;
241 int result = t.counter; 258 return result;
242 t.counter = 0; 259 }
243 return result; 260
244 } 261 // dsp
245 262 if ( addr == 0xF3 )
246 if ( addr == 0xf0 || addr == 0xf1 || addr == 0xf8 || 263 {
247 addr == 0xf9 || addr == 0xfa ) 264 run_dsp( time() );
248 dprintf( "Read from register $%02X\n", (int) addr ); 265 if ( ram [0xF2] >= Spc_Dsp::register_count )
249 266 dprintf( "DSP read from $%02X\n", (int) ram [0xF2] );
250 // Registers which always read as 0 are handled by setting ram [reg] to 0 267 return dsp.read( ram [0xF2] & 0x7F );
251 // at startup then never changing that value. 268 }
252 269
253 check(( check_for_echo_access( addr ), true )); 270 if ( addr == 0xF0 || addr == 0xF1 || addr == 0xF8 ||
254 271 addr == 0xF9 || addr == 0xFA )
255 // ram 272 dprintf( "Read from register $%02X\n", (int) addr );
256 return ram [addr]; 273
274 // Registers which always read as 0 are handled by setting ram [reg] to 0
275 // at startup then never changing that value.
276
277 check(( check_for_echo_access( addr ), true ));
278 }
279
280 return result;
257 } 281 }
258 282
259 283
260 // Write 284 // 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 285
273 void Snes_Spc::enable_rom( bool enable ) 286 void Snes_Spc::enable_rom( bool enable )
274 { 287 {
275 if ( rom_enabled != enable ) 288 if ( rom_enabled != enable )
276 { 289 {
277 rom_enabled = enable; 290 rom_enabled = enable;
278 memcpy( ram + rom_addr, (enable ? boot_rom : extra_ram), rom_size ); 291 memcpy( ram + rom_addr, (enable ? boot_rom : extra_ram), rom_size );
292 // TODO: ROM can still get overwritten when DSP writes to echo buffer
279 } 293 }
280 } 294 }
281 295
282 void Snes_Spc::write( spc_addr_t addr, int data ) 296 void Snes_Spc::write( spc_addr_t addr, int data )
283 { 297 {
284 // first page is very common 298 // first page is very common
285 if ( addr < 0xf0 ) { 299 if ( addr < 0xF0 ) {
286 ram [addr] = data; 300 ram [addr] = (uint8_t) data;
287 } 301 }
288 else switch ( addr ) 302 else switch ( addr )
289 { 303 {
290 // RAM 304 // RAM
291 default: 305 default:
292 check(( check_for_echo_access( addr ), true )); 306 check(( check_for_echo_access( addr ), true ));
293 if ( addr < rom_addr ) { 307 if ( addr < rom_addr ) {
294 ram [addr] = data; 308 ram [addr] = (uint8_t) data;
295 } 309 }
296 else { 310 else {
297 extra_ram [addr - rom_addr] = data; 311 extra_ram [addr - rom_addr] = (uint8_t) data;
298 if ( !rom_enabled ) 312 if ( !rom_enabled )
299 ram [addr] = data; 313 ram [addr] = (uint8_t) data;
300 } 314 }
301 break; 315 break;
302 316
303 // DSP 317 // DSP
304 //case 0xf2: // mapped to RAM 318 //case 0xF2: // mapped to RAM
305 case 0xf3: { 319 case 0xF3: {
306 run_dsp( time() ); 320 run_dsp( time() );
307 int reg = ram [0xf2]; 321 int reg = ram [0xF2];
308 if ( next_dsp > 0 ) { 322 if ( next_dsp > 0 ) {
309 // skip mode 323 // skip mode
310 324
311 // key press 325 // key press
312 if ( reg == 0x4C ) 326 if ( reg == 0x4C )
325 dprintf( "DSP write to $%02X\n", (int) reg ); 339 dprintf( "DSP write to $%02X\n", (int) reg );
326 } 340 }
327 break; 341 break;
328 } 342 }
329 343
330 case 0xf0: // Test register 344 case 0xF0: // Test register
331 dprintf( "Wrote $%02X to $F0\n", (int) data ); 345 dprintf( "Wrote $%02X to $F0\n", (int) data );
332 break; 346 break;
333 347
334 // Config 348 // Config
335 case 0xf1: 349 case 0xF1:
336 { 350 {
337 // timers 351 // timers
338 for ( int i = 0; i < timer_count; i++ ) 352 for ( int i = 0; i < timer_count; i++ )
339 { 353 {
340 Timer& t = timer [i]; 354 Timer& t = timer [i];
351 } 365 }
352 } 366 }
353 367
354 // port clears 368 // port clears
355 if ( data & 0x10 ) { 369 if ( data & 0x10 ) {
356 ram [0xf4] = 0; 370 ram [0xF4] = 0;
357 ram [0xf5] = 0; 371 ram [0xF5] = 0;
358 } 372 }
359 if ( data & 0x20 ) { 373 if ( data & 0x20 ) {
360 ram [0xf6] = 0; 374 ram [0xF6] = 0;
361 ram [0xf7] = 0; 375 ram [0xF7] = 0;
362 } 376 }
363 377
364 enable_rom( data & 0x80 ); 378 enable_rom( data & 0x80 );
365 379
366 break; 380 break;
367 } 381 }
368 382
369 // Ports 383 // Ports
370 case 0xf4: 384 case 0xF4:
371 case 0xf5: 385 case 0xF5:
372 case 0xf6: 386 case 0xF6:
373 case 0xf7: 387 case 0xF7:
374 // to do: handle output ports 388 // to do: handle output ports
375 break; 389 break;
376 390
377 //case 0xf8: // verified on SNES that these are read/write (RAM) 391 //case 0xF8: // verified on SNES that these are read/write (RAM)
378 //case 0xf9: 392 //case 0xF9:
379 393
380 // Timers 394 // Timers
381 case 0xfa: 395 case 0xFA:
382 case 0xfb: 396 case 0xFB:
383 case 0xfc: { 397 case 0xFC: {
384 Timer& t = timer [addr - 0xfa]; 398 Timer& t = timer [addr - 0xFA];
385 if ( (t.period & 0xff) != data ) { 399 if ( (t.period & 0xFF) != data ) {
386 t.run_until( time() ); 400 t.run_until( time() );
387 t.period = data ? data : 0x100; 401 t.period = data ? data : 0x100;
388 } 402 }
389 break; 403 break;
390 } 404 }
391 405
392 // Counters (cleared on write) 406 // Counters (cleared on write)
393 case 0xfd: 407 case 0xFD:
394 case 0xfe: 408 case 0xFE:
395 case 0xff: 409 case 0xFF:
396 dprintf( "Wrote to counter $%02X\n", (int) addr ); 410 dprintf( "Wrote to counter $%02X\n", (int) addr );
397 timer [addr - 0xfd].counter = 0; 411 timer [addr - 0xFD].counter = 0;
398 break; 412 break;
399 } 413 }
400 } 414 }
401 415
402 // Play 416 // Play
411 425
412 // keep track of any keys pressed/released (and not subsequently released) 426 // keep track of any keys pressed/released (and not subsequently released)
413 keys_pressed = 0; 427 keys_pressed = 0;
414 keys_released = 0; 428 keys_released = 0;
415 // sentinel tells play to ignore DSP 429 // sentinel tells play to ignore DSP
416 BLARGG_RETURN_ERR( play( count - sync_count, skip_sentinel ) ); 430 RETURN_ERR( play( count - sync_count, skip_sentinel ) );
417 431
418 // press/release keys now 432 // press/release keys now
419 dsp.write( 0x5C, keys_released & ~keys_pressed ); 433 dsp.write( 0x5C, keys_released & ~keys_pressed );
420 dsp.write( 0x4C, keys_pressed ); 434 dsp.write( 0x4C, keys_pressed );
421 435
456 int elapsed = cpu.run( duration - extra_cycles ); 470 int elapsed = cpu.run( duration - extra_cycles );
457 if ( elapsed > 0 ) 471 if ( elapsed > 0 )
458 { 472 {
459 dprintf( "Unhandled instruction $%02X, pc = $%04X\n", 473 dprintf( "Unhandled instruction $%02X, pc = $%04X\n",
460 (int) cpu.read( cpu.r.pc ), (unsigned) cpu.r.pc ); 474 (int) cpu.read( cpu.r.pc ), (unsigned) cpu.r.pc );
461 return "Emulation error"; 475 return "Emulation error (illegal/unsupported instruction)";
462 } 476 }
463 extra_cycles = -elapsed; 477 extra_cycles = -elapsed;
464 478
465 // Catch DSP up to present. 479 // Catch DSP up to present.
466 run_dsp( 0 ); 480 run_dsp( 0 );
467 if ( out ) { 481 if ( out ) {
468 assert( next_dsp == clocks_per_sample ); 482 assert( next_dsp == clocks_per_sample );
469 assert( out == skip_sentinel || sample_buf - out == count ); 483 assert( out == skip_sentinel || sample_buf - out == count );
470 } 484 }
471 buf_end = NULL; 485 buf_end = 0;
472 486
473 return blargg_success; 487 return 0;
474 } 488 }
475