Mercurial > audlegacy-plugins
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 |