diff options
author | Andree Buschmann <AndreeBuschmann@t-online.de> | 2011-08-07 20:01:04 +0000 |
---|---|---|
committer | Andree Buschmann <AndreeBuschmann@t-online.de> | 2011-08-07 20:01:04 +0000 |
commit | acb0917556fc33681c1df5a530cf754193e67705 (patch) | |
tree | 052a47097009a210e4aed9c207bd6aa4828cc000 /apps/codecs/libgme/nsf_emu.c | |
parent | 93c6f1329a5691a8be158cefe15641bd1daf9ef8 (diff) | |
download | rockbox-acb0917556fc33681c1df5a530cf754193e67705.tar.gz rockbox-acb0917556fc33681c1df5a530cf754193e67705.zip |
Submit initial patch from FS#12176. Adds support for several new game music formats (AY, GBS, HES, KSS, SGC, VGM and VGZ) and replaces the current NSF and NSFE with a new implementation based on a port of the Game Music Emu library 'GME'. This first submit does not cover the full functionality provided by the author's original patch: Coleco-SGV is not supported, some GME-specific m3u-support has been removed and IRAM is not used yet. Further changes are very likely to follow this submit. Thanks to Mauricio Garrido.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@30264 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/codecs/libgme/nsf_emu.c')
-rw-r--r-- | apps/codecs/libgme/nsf_emu.c | 1105 |
1 files changed, 1105 insertions, 0 deletions
diff --git a/apps/codecs/libgme/nsf_emu.c b/apps/codecs/libgme/nsf_emu.c new file mode 100644 index 0000000000..c805780cb1 --- /dev/null +++ b/apps/codecs/libgme/nsf_emu.c | |||
@@ -0,0 +1,1105 @@ | |||
1 | // Game_Music_Emu 0.5.5. http://www.slack.net/~ant/ | ||
2 | |||
3 | #include "nsf_emu.h" | ||
4 | #include "multi_buffer.h" | ||
5 | |||
6 | #include "blargg_endian.h" | ||
7 | |||
8 | /* Copyright (C) 2003-2006 Shay Green. This module is free software; you | ||
9 | can redistribute it and/or modify it under the terms of the GNU Lesser | ||
10 | General Public License as published by the Free Software Foundation; either | ||
11 | version 2.1 of the License, or (at your option) any later version. This | ||
12 | module is distributed in the hope that it will be useful, but WITHOUT ANY | ||
13 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | ||
14 | FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more | ||
15 | details. You should have received a copy of the GNU Lesser General Public | ||
16 | License along with this module; if not, write to the Free Software Foundation, | ||
17 | Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ | ||
18 | |||
19 | #include "blargg_source.h" | ||
20 | |||
21 | const char gme_wrong_file_type [] ICONST_ATTR = "Wrong file type for this emulator"; | ||
22 | long const clock_divisor = 12; | ||
23 | |||
24 | int const stereo = 2; // number of channels for stereo | ||
25 | int const silence_max = 6; // seconds | ||
26 | int const silence_threshold = 0x10; | ||
27 | long const fade_block_size = 512; | ||
28 | int const fade_shift = 8; // fade ends with gain at 1.0 / (1 << fade_shift) | ||
29 | |||
30 | // number of frames until play interrupts init | ||
31 | int const initial_play_delay = 7; // KikiKaikai needed this to work | ||
32 | int const rom_addr = 0x8000; | ||
33 | |||
34 | void clear_track_vars( struct Nsf_Emu* this ) | ||
35 | { | ||
36 | this->current_track = -1; | ||
37 | this->out_time = 0; | ||
38 | this->emu_time = 0; | ||
39 | this->emu_track_ended_ = true; | ||
40 | this->track_ended = true; | ||
41 | this->fade_start = INT_MAX / 2 + 1; | ||
42 | this->fade_step = 1; | ||
43 | this->silence_time = 0; | ||
44 | this->silence_count = 0; | ||
45 | this->buf_remain = 0; | ||
46 | } | ||
47 | |||
48 | static int pcm_read( void* emu, addr_t addr ) | ||
49 | { | ||
50 | return *Cpu_get_code( &((struct Nsf_Emu*) emu)->cpu, addr ); | ||
51 | } | ||
52 | |||
53 | void Nsf_init( struct Nsf_Emu* this ) | ||
54 | { | ||
55 | this->sample_rate = 0; | ||
56 | this->mute_mask_ = 0; | ||
57 | this->tempo = 1.0; | ||
58 | this->gain = 1.0; | ||
59 | |||
60 | // defaults | ||
61 | this->max_initial_silence = 2; | ||
62 | this->ignore_silence = false; | ||
63 | this->voice_count = 0; | ||
64 | |||
65 | // Set sound gain | ||
66 | Sound_set_gain( this, 1.2 ); | ||
67 | |||
68 | // Unload | ||
69 | clear_track_vars( this ); | ||
70 | |||
71 | // Init rom | ||
72 | Rom_init( &this->rom, 0x1000 ); | ||
73 | |||
74 | // Init & clear nsfe info | ||
75 | Info_init( &this->info ); | ||
76 | Info_unload( &this->info ); // TODO: extremely hacky! | ||
77 | |||
78 | Cpu_init( &this->cpu ); | ||
79 | Apu_init( &this->apu ); | ||
80 | Apu_dmc_reader( &this->apu, pcm_read, this ); | ||
81 | } | ||
82 | |||
83 | // Setup | ||
84 | |||
85 | blargg_err_t init_sound( struct Nsf_Emu* this ) | ||
86 | { | ||
87 | /* if ( header_.chip_flags & ~(fds_flag | namco_flag | vrc6_flag | fme7_flag) ) | ||
88 | warning( "Uses unsupported audio expansion hardware" ); **/ | ||
89 | |||
90 | this->voice_count = apu_osc_count; | ||
91 | |||
92 | double adjusted_gain = 1.0 / 0.75 * this->gain; | ||
93 | |||
94 | #ifdef NSF_EMU_APU_ONLY | ||
95 | { | ||
96 | if ( this->header_.chip_flags ) | ||
97 | set_warning( "Uses unsupported audio expansion hardware" ); | ||
98 | } | ||
99 | #else | ||
100 | { | ||
101 | if ( vrc6_enabled( this ) ) | ||
102 | { | ||
103 | Vrc6_init( &this->vrc6 ); | ||
104 | adjusted_gain *= 0.75; | ||
105 | |||
106 | this->voice_count += vrc6_osc_count; | ||
107 | } | ||
108 | |||
109 | if ( fme7_enabled( this ) ) | ||
110 | { | ||
111 | Fme7_init( &this->fme7 ); | ||
112 | adjusted_gain *= 0.75; | ||
113 | |||
114 | this->voice_count += fme7_osc_count; | ||
115 | } | ||
116 | |||
117 | if ( mmc5_enabled( this ) ) | ||
118 | { | ||
119 | Mmc5_init( &this->mmc5 ); | ||
120 | adjusted_gain *= 0.75; | ||
121 | |||
122 | this->voice_count += mmc5_osc_count; | ||
123 | } | ||
124 | |||
125 | if ( fds_enabled( this ) ) | ||
126 | { | ||
127 | Fds_init( &this->fds ); | ||
128 | adjusted_gain *= 0.75; | ||
129 | |||
130 | this->voice_count += fds_osc_count ; | ||
131 | } | ||
132 | |||
133 | if ( namco_enabled( this ) ) | ||
134 | { | ||
135 | Namco_init( &this->namco ); | ||
136 | adjusted_gain *= 0.75; | ||
137 | |||
138 | this->voice_count += namco_osc_count; | ||
139 | } | ||
140 | |||
141 | if ( vrc7_enabled( this ) ) | ||
142 | { | ||
143 | #ifndef NSF_EMU_NO_VRC7 | ||
144 | Vrc7_init( &this->vrc7 ); | ||
145 | Vrc7_set_rate( &this->vrc7, this->sample_rate ); | ||
146 | #endif | ||
147 | |||
148 | adjusted_gain *= 0.75; | ||
149 | |||
150 | this->voice_count += vrc7_osc_count; | ||
151 | } | ||
152 | |||
153 | if ( vrc7_enabled( this ) ) Vrc7_volume( &this->vrc7, adjusted_gain ); | ||
154 | if ( namco_enabled( this ) ) Namco_volume( &this->namco, adjusted_gain ); | ||
155 | if ( vrc6_enabled( this ) ) Vrc6_volume( &this->vrc6, adjusted_gain ); | ||
156 | if ( fme7_enabled( this ) ) Fme7_volume( &this->fme7, adjusted_gain ); | ||
157 | if ( mmc5_enabled( this ) ) Apu_volume( &this->mmc5.apu, adjusted_gain ); | ||
158 | if ( fds_enabled( this ) ) Fds_volume( &this->fds, adjusted_gain ); | ||
159 | } | ||
160 | #endif | ||
161 | |||
162 | if ( adjusted_gain > this->gain ) | ||
163 | adjusted_gain = this->gain; | ||
164 | |||
165 | Apu_volume( &this->apu, adjusted_gain ); | ||
166 | |||
167 | return 0; | ||
168 | } | ||
169 | |||
170 | // Header stuff | ||
171 | bool valid_tag( struct header_t* this ) | ||
172 | { | ||
173 | return 0 == memcmp( this->tag, "NESM\x1A", 5 ); | ||
174 | } | ||
175 | |||
176 | // True if file supports only PAL speed | ||
177 | static bool pal_only( struct header_t* this ) | ||
178 | { | ||
179 | return (this->speed_flags & 3) == 1; | ||
180 | } | ||
181 | |||
182 | static double clock_rate( struct header_t* this ) | ||
183 | { | ||
184 | return pal_only( this ) ? 1662607.125 : 1789772.727272727; | ||
185 | } | ||
186 | |||
187 | int play_period( struct header_t* this ) | ||
188 | { | ||
189 | // NTSC | ||
190 | int clocks = 29780; | ||
191 | int value = 0x411A; | ||
192 | byte const* rate_ptr = this->ntsc_speed; | ||
193 | |||
194 | // PAL | ||
195 | if ( pal_only( this ) ) | ||
196 | { | ||
197 | clocks = 33247; | ||
198 | value = 0x4E20; | ||
199 | rate_ptr = this->pal_speed; | ||
200 | } | ||
201 | |||
202 | // Default rate | ||
203 | int rate = get_le16( rate_ptr ); | ||
204 | if ( rate == 0 ) | ||
205 | rate = value; | ||
206 | |||
207 | // Custom rate | ||
208 | if ( rate != value ) | ||
209 | clocks = (int) (rate * clock_rate( this ) * (1.0/1000000.0)); | ||
210 | |||
211 | return clocks; | ||
212 | } | ||
213 | |||
214 | // Gets address, given pointer to it in file header. If zero, returns rom_addr. | ||
215 | addr_t get_addr( byte const in [] ) | ||
216 | { | ||
217 | addr_t addr = get_le16( in ); | ||
218 | if ( addr == 0 ) | ||
219 | addr = rom_addr; | ||
220 | return addr; | ||
221 | } | ||
222 | |||
223 | static blargg_err_t check_nsf_header( struct header_t* h ) | ||
224 | { | ||
225 | if ( !valid_tag( h ) ) | ||
226 | return gme_wrong_file_type; | ||
227 | return 0; | ||
228 | } | ||
229 | |||
230 | blargg_err_t Nsf_load( struct Nsf_Emu* this, void* data, long size ) | ||
231 | { | ||
232 | // Unload | ||
233 | Info_unload( &this->info ); // TODO: extremely hacky! | ||
234 | this->m3u.size = 0; | ||
235 | |||
236 | this->voice_count = 0; | ||
237 | clear_track_vars( this ); | ||
238 | |||
239 | assert( offsetof (struct header_t,unused [4]) == header_size ); | ||
240 | |||
241 | if ( !memcmp( data, "NESM\x1A", 5 ) ) { | ||
242 | Nsf_disable_playlist( this, true ); | ||
243 | |||
244 | RETURN_ERR( Rom_load( &this->rom, data, size, header_size, &this->header, 0 ) ); | ||
245 | return Nsf_post_load( this ); | ||
246 | } | ||
247 | |||
248 | blargg_err_t err = Info_load( &this->info, data, size, this ); | ||
249 | Nsf_disable_playlist( this, false ); | ||
250 | return err; | ||
251 | } | ||
252 | |||
253 | blargg_err_t Nsf_post_load( struct Nsf_Emu* this ) | ||
254 | { | ||
255 | RETURN_ERR( check_nsf_header( &this->header ) ); | ||
256 | |||
257 | /* if ( header_.vers != 1 ) | ||
258 | warning( "Unknown file version" ); */ | ||
259 | |||
260 | // set up data | ||
261 | addr_t load_addr = get_le16( this->header.load_addr ); | ||
262 | |||
263 | /* if ( load_addr < (fds_enabled() ? sram_addr : rom_addr) ) | ||
264 | warning( "Load address is too low" ); */ | ||
265 | |||
266 | Rom_set_addr( &this->rom, load_addr % this->rom.bank_size ); | ||
267 | |||
268 | /* if ( header_.vers != 1 ) | ||
269 | warning( "Unknown file version" ); */ | ||
270 | |||
271 | set_play_period( this, play_period( &this->header ) ); | ||
272 | |||
273 | // sound and memory | ||
274 | blargg_err_t err = init_sound( this ); | ||
275 | if ( err ) | ||
276 | return err; | ||
277 | |||
278 | // Post load | ||
279 | Sound_set_tempo( this, this->tempo ); | ||
280 | |||
281 | // Remute voices | ||
282 | Sound_mute_voices( this, this->mute_mask_ ); | ||
283 | |||
284 | // Set track_count | ||
285 | this->track_count = this->header.track_count; | ||
286 | |||
287 | // Change clock rate & setup buffer | ||
288 | this->clock_rate__ = (long) (clock_rate( &this->header ) + 0.5); | ||
289 | Buffer_clock_rate( &this->stereo_buf, this->clock_rate__ ); | ||
290 | this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf ); | ||
291 | return 0; | ||
292 | } | ||
293 | |||
294 | void Nsf_disable_playlist( struct Nsf_Emu* this, bool b ) | ||
295 | { | ||
296 | Info_disable_playlist( &this->info, b ); | ||
297 | this->track_count = this->info.track_count; | ||
298 | } | ||
299 | |||
300 | void Nsf_clear_playlist( struct Nsf_Emu* this ) | ||
301 | { | ||
302 | Nsf_disable_playlist( this, true ); | ||
303 | } | ||
304 | |||
305 | void write_bank( struct Nsf_Emu* this, int bank, int data ) | ||
306 | { | ||
307 | // Find bank in ROM | ||
308 | int offset = mask_addr( data * this->rom.bank_size, this->rom.mask ); | ||
309 | /* if ( offset >= rom.size() ) | ||
310 | warning( "invalid bank" ); */ | ||
311 | void const* rom_data = Rom_at_addr( &this->rom, offset ); | ||
312 | |||
313 | #ifndef NSF_EMU_APU_ONLY | ||
314 | if ( bank < bank_count - fds_banks && fds_enabled( this ) ) | ||
315 | { | ||
316 | // TODO: FDS bank switching is kind of hacky, might need to | ||
317 | // treat ROM as RAM so changes won't get lost when switching. | ||
318 | byte* out = sram( this ); | ||
319 | if ( bank >= fds_banks ) | ||
320 | { | ||
321 | out = fdsram( this ); | ||
322 | bank -= fds_banks; | ||
323 | } | ||
324 | memcpy( &out [bank * this->rom.bank_size], rom_data, this->rom.bank_size ); | ||
325 | return; | ||
326 | } | ||
327 | #endif | ||
328 | |||
329 | if ( bank >= fds_banks ) | ||
330 | Cpu_map_code( &this->cpu, (bank + 6) * this->rom.bank_size, this->rom.bank_size, rom_data, false ); | ||
331 | } | ||
332 | |||
333 | void map_memory( struct Nsf_Emu* this ) | ||
334 | { | ||
335 | // Map standard things | ||
336 | Cpu_reset( &this->cpu, unmapped_code( this ) ); | ||
337 | Cpu_map_code( &this->cpu, 0, 0x2000, this->low_ram, low_ram_size ); // mirrored four times | ||
338 | Cpu_map_code( &this->cpu, sram_addr, sram_size, sram( this ), 0 ); | ||
339 | |||
340 | // Determine initial banks | ||
341 | byte banks [bank_count]; | ||
342 | static byte const zero_banks [sizeof this->header.banks] = { 0 }; | ||
343 | if ( memcmp( this->header.banks, zero_banks, sizeof zero_banks ) ) | ||
344 | { | ||
345 | banks [0] = this->header.banks [6]; | ||
346 | banks [1] = this->header.banks [7]; | ||
347 | memcpy( banks + fds_banks, this->header.banks, sizeof this->header.banks ); | ||
348 | } | ||
349 | else | ||
350 | { | ||
351 | // No initial banks, so assign them based on load_addr | ||
352 | int i, first_bank = (get_addr( this->header.load_addr ) - sram_addr) / this->rom.bank_size; | ||
353 | unsigned total_banks = this->rom.size / this->rom.bank_size; | ||
354 | for ( i = bank_count; --i >= 0; ) | ||
355 | { | ||
356 | int bank = i - first_bank; | ||
357 | if ( (unsigned) bank >= total_banks ) | ||
358 | bank = 0; | ||
359 | banks [i] = bank; | ||
360 | } | ||
361 | } | ||
362 | |||
363 | // Map banks | ||
364 | int i; | ||
365 | for ( i = (fds_enabled( this ) ? 0 : fds_banks); i < bank_count; ++i ) | ||
366 | write_bank( this, i, banks [i] ); | ||
367 | |||
368 | // Map FDS RAM | ||
369 | if ( fds_enabled( this ) ) | ||
370 | Cpu_map_code( &this->cpu, rom_addr, fdsram_size, fdsram( this ), 0 ); | ||
371 | } | ||
372 | |||
373 | void set_voice( struct Nsf_Emu* this, int i, struct Blip_Buffer* buf, struct Blip_Buffer* left, struct Blip_Buffer* right) | ||
374 | { | ||
375 | #if defined(ROCKBOX) | ||
376 | (void) left; | ||
377 | (void) right; | ||
378 | #endif | ||
379 | |||
380 | if ( i < apu_osc_count ) | ||
381 | { | ||
382 | Apu_osc_output( &this->apu, i, buf ); | ||
383 | return; | ||
384 | } | ||
385 | i -= apu_osc_count; | ||
386 | |||
387 | #ifndef NSF_EMU_APU_ONLY | ||
388 | { | ||
389 | if ( vrc6_enabled( this ) && (i -= vrc6_osc_count) < 0 ) | ||
390 | { | ||
391 | Vrc6_osc_output( &this->vrc6, i + vrc6_osc_count, buf ); | ||
392 | return; | ||
393 | } | ||
394 | |||
395 | if ( fme7_enabled( this ) && (i -= fme7_osc_count) < 0 ) | ||
396 | { | ||
397 | Fme7_osc_output( &this->fme7, i + fme7_osc_count, buf ); | ||
398 | return; | ||
399 | } | ||
400 | |||
401 | if ( mmc5_enabled( this ) && (i -= mmc5_osc_count) < 0 ) | ||
402 | { | ||
403 | Mmc5_set_output( &this->mmc5, i + mmc5_osc_count, buf ); | ||
404 | return; | ||
405 | } | ||
406 | |||
407 | if ( fds_enabled( this ) && (i -= fds_osc_count) < 0 ) | ||
408 | { | ||
409 | Fds_set_output( &this->fds, i + fds_osc_count, buf ); | ||
410 | return; | ||
411 | } | ||
412 | |||
413 | if ( namco_enabled( this ) && (i -= namco_osc_count) < 0 ) | ||
414 | { | ||
415 | Namco_osc_output( &this->namco, i + namco_osc_count, buf ); | ||
416 | return; | ||
417 | } | ||
418 | |||
419 | if ( vrc7_enabled( this ) && (i -= vrc7_osc_count) < 0 ) | ||
420 | { | ||
421 | Vrc7_set_output( &this->vrc7, i + vrc7_osc_count, buf ); | ||
422 | return; | ||
423 | } | ||
424 | } | ||
425 | #endif | ||
426 | } | ||
427 | |||
428 | // Emulation | ||
429 | |||
430 | // Music Emu | ||
431 | |||
432 | blargg_err_t Nsf_set_sample_rate( struct Nsf_Emu* this, long rate ) | ||
433 | { | ||
434 | require( !this->sample_rate ); // sample rate can't be changed once set | ||
435 | Buffer_init( &this->stereo_buf ); | ||
436 | RETURN_ERR( Buffer_set_sample_rate( &this->stereo_buf, rate, 1000 / 20 ) ); | ||
437 | |||
438 | // Set bass frequency | ||
439 | Buffer_bass_freq( &this->stereo_buf, 80 ); | ||
440 | |||
441 | this->sample_rate = rate; | ||
442 | return 0; | ||
443 | } | ||
444 | |||
445 | void Sound_mute_voice( struct Nsf_Emu* this, int index, bool mute ) | ||
446 | { | ||
447 | require( (unsigned) index < (unsigned) this->voice_count ); | ||
448 | int bit = 1 << index; | ||
449 | int mask = this->mute_mask_ | bit; | ||
450 | if ( !mute ) | ||
451 | mask ^= bit; | ||
452 | Sound_mute_voices( this, mask ); | ||
453 | } | ||
454 | |||
455 | void Sound_mute_voices( struct Nsf_Emu* this, int mask ) | ||
456 | { | ||
457 | require( this->sample_rate ); // sample rate must be set first | ||
458 | this->mute_mask_ = mask; | ||
459 | |||
460 | int i; | ||
461 | for ( i = this->voice_count; i--; ) | ||
462 | { | ||
463 | if ( mask & (1 << i) ) | ||
464 | { | ||
465 | set_voice( this, i, 0, 0, 0 ); | ||
466 | } | ||
467 | else | ||
468 | { | ||
469 | struct channel_t ch = Buffer_channel( &this->stereo_buf ); | ||
470 | assert( (ch.center && ch.left && ch.right) || | ||
471 | (!ch.center && !ch.left && !ch.right) ); // all or nothing | ||
472 | set_voice( this, i, ch.center, ch.left, ch.right ); | ||
473 | } | ||
474 | } | ||
475 | } | ||
476 | |||
477 | void Sound_set_tempo( struct Nsf_Emu* this, double t ) | ||
478 | { | ||
479 | require( this->sample_rate ); // sample rate must be set first | ||
480 | double const min = 0.02; | ||
481 | double const max = 4.00; | ||
482 | if ( t < min ) t = min; | ||
483 | if ( t > max ) t = max; | ||
484 | this->tempo = t; | ||
485 | |||
486 | set_play_period( this, (int) (play_period( &this->header ) / t) ); | ||
487 | |||
488 | Apu_set_tempo( &this->apu, t ); | ||
489 | |||
490 | #ifndef NSF_EMU_APU_ONLY | ||
491 | if ( fds_enabled( this ) ) | ||
492 | Fds_set_tempo( &this->fds, t ); | ||
493 | #endif | ||
494 | } | ||
495 | |||
496 | inline void push_byte( struct Nsf_Emu* this, int b ) | ||
497 | { | ||
498 | this->low_ram [0x100 + this->cpu.r.sp--] = b; | ||
499 | } | ||
500 | |||
501 | // Jumps to routine, given pointer to address in file header. Pushes idle_addr | ||
502 | // as return address, NOT old PC. | ||
503 | void jsr_then_stop( struct Nsf_Emu* this, byte const addr [] ) | ||
504 | { | ||
505 | this->cpu.r.pc = get_addr( addr ); | ||
506 | push_byte( this, (idle_addr - 1) >> 8 ); | ||
507 | push_byte( this, (idle_addr - 1) ); | ||
508 | } | ||
509 | |||
510 | int cpu_read( struct Nsf_Emu* this, addr_t addr ) | ||
511 | { | ||
512 | #ifndef NSF_EMU_APU_ONLY | ||
513 | { | ||
514 | if ( namco_enabled( this ) && addr == namco_data_reg_addr ) | ||
515 | return Namco_read_data( &this->namco ); | ||
516 | |||
517 | if ( fds_enabled( this ) && (unsigned) (addr - fds_io_addr) < fds_io_size ) | ||
518 | return Fds_read( &this->fds, Cpu_time( &this->cpu ), addr ); | ||
519 | |||
520 | if ( mmc5_enabled( this ) ) { | ||
521 | int i = addr - 0x5C00; | ||
522 | if ( (unsigned) i < mmc5_exram_size ) | ||
523 | return this->mmc5.exram [i]; | ||
524 | |||
525 | int m = addr - 0x5205; | ||
526 | if ( (unsigned) m < 2 ) | ||
527 | return (this->mmc5_mul [0] * this->mmc5_mul [1]) >> (m * 8) & 0xFF; | ||
528 | } | ||
529 | } | ||
530 | #endif | ||
531 | |||
532 | /* Unmapped read */ | ||
533 | return addr >> 8; | ||
534 | } | ||
535 | |||
536 | int unmapped_read( struct Nsf_Emu* this, addr_t addr ) | ||
537 | { | ||
538 | (void) this; | ||
539 | |||
540 | switch ( addr ) | ||
541 | { | ||
542 | case 0x2002: | ||
543 | case 0x4016: | ||
544 | case 0x4017: | ||
545 | return addr >> 8; | ||
546 | } | ||
547 | |||
548 | // Unmapped read | ||
549 | return addr >> 8; | ||
550 | } | ||
551 | |||
552 | void cpu_write( struct Nsf_Emu* this, addr_t addr, int data ) | ||
553 | { | ||
554 | #ifndef NSF_EMU_APU_ONLY | ||
555 | { | ||
556 | nes_time_t time = Cpu_time( &this->cpu ); | ||
557 | if ( fds_enabled( this) && (unsigned) (addr - fds_io_addr) < fds_io_size ) | ||
558 | { | ||
559 | Fds_write( &this->fds, time, addr, data ); | ||
560 | return; | ||
561 | } | ||
562 | |||
563 | if ( namco_enabled( this) ) | ||
564 | { | ||
565 | if ( addr == namco_addr_reg_addr ) | ||
566 | { | ||
567 | Namco_write_addr( &this->namco, data ); | ||
568 | return; | ||
569 | } | ||
570 | |||
571 | if ( addr == namco_data_reg_addr ) | ||
572 | { | ||
573 | Namco_write_data( &this->namco, time, data ); | ||
574 | return; | ||
575 | } | ||
576 | } | ||
577 | |||
578 | if ( vrc6_enabled( this) ) | ||
579 | { | ||
580 | int reg = addr & (vrc6_addr_step - 1); | ||
581 | int osc = (unsigned) (addr - vrc6_base_addr) / vrc6_addr_step; | ||
582 | if ( (unsigned) osc < vrc6_osc_count && (unsigned) reg < vrc6_reg_count ) | ||
583 | { | ||
584 | Vrc6_write_osc( &this->vrc6, time, osc, reg, data ); | ||
585 | return; | ||
586 | } | ||
587 | } | ||
588 | |||
589 | if ( fme7_enabled( this) && addr >= fme7_latch_addr ) | ||
590 | { | ||
591 | switch ( addr & fme7_addr_mask ) | ||
592 | { | ||
593 | case fme7_latch_addr: | ||
594 | Fme7_write_latch( &this->fme7, data ); | ||
595 | return; | ||
596 | |||
597 | case fme7_data_addr: | ||
598 | Fme7_write_data( &this->fme7, time, data ); | ||
599 | return; | ||
600 | } | ||
601 | } | ||
602 | |||
603 | if ( mmc5_enabled( this) ) | ||
604 | { | ||
605 | if ( (unsigned) (addr - mmc5_regs_addr) < mmc5_regs_size ) | ||
606 | { | ||
607 | Mmc5_write_register( &this->mmc5, time, addr, data ); | ||
608 | return; | ||
609 | } | ||
610 | |||
611 | int m = addr - 0x5205; | ||
612 | if ( (unsigned) m < 2 ) | ||
613 | { | ||
614 | this->mmc5_mul [m] = data; | ||
615 | return; | ||
616 | } | ||
617 | |||
618 | int i = addr - 0x5C00; | ||
619 | if ( (unsigned) i < mmc5_exram_size ) | ||
620 | { | ||
621 | this->mmc5.exram [i] = data; | ||
622 | return; | ||
623 | } | ||
624 | } | ||
625 | |||
626 | if ( vrc7_enabled( this) ) | ||
627 | { | ||
628 | if ( addr == 0x9010 ) | ||
629 | { | ||
630 | Vrc7_write_reg( &this->vrc7, data ); | ||
631 | return; | ||
632 | } | ||
633 | |||
634 | if ( (unsigned) (addr - 0x9028) <= 0x08 ) | ||
635 | { | ||
636 | Vrc7_write_data( &this->vrc7, time, data ); | ||
637 | return; | ||
638 | } | ||
639 | } | ||
640 | } | ||
641 | #endif | ||
642 | |||
643 | // Unmapped_write | ||
644 | } | ||
645 | |||
646 | void unmapped_write( struct Nsf_Emu* this, addr_t addr, int data ) | ||
647 | { | ||
648 | (void) data; | ||
649 | |||
650 | switch ( addr ) | ||
651 | { | ||
652 | case 0x8000: // some write to $8000 and $8001 repeatedly | ||
653 | case 0x8001: | ||
654 | case 0x4800: // probably namco sound mistakenly turned on in MCK | ||
655 | case 0xF800: | ||
656 | case 0xFFF8: // memory mapper? | ||
657 | return; | ||
658 | } | ||
659 | |||
660 | if ( mmc5_enabled( this ) && addr == 0x5115 ) return; | ||
661 | |||
662 | // FDS memory | ||
663 | if ( fds_enabled( this ) && (unsigned) (addr - 0x8000) < 0x6000 ) return; | ||
664 | } | ||
665 | |||
666 | void fill_buf( struct Nsf_Emu* this ); | ||
667 | blargg_err_t Nsf_start_track( struct Nsf_Emu* this, int track ) | ||
668 | { | ||
669 | clear_track_vars( this ); | ||
670 | |||
671 | // Remap track if playlist available | ||
672 | if ( this->m3u.size > 0 ) { | ||
673 | struct entry_t* e = &this->m3u.entries[track]; | ||
674 | track = e->track; | ||
675 | } | ||
676 | else track = Info_remap_track( &this->info, track ); | ||
677 | |||
678 | this->current_track = track; | ||
679 | Buffer_clear( &this->stereo_buf ); | ||
680 | |||
681 | #ifndef NSF_EMU_APU_ONLY | ||
682 | if ( mmc5_enabled( this ) ) | ||
683 | { | ||
684 | this->mmc5_mul [0] = 0; | ||
685 | this->mmc5_mul [1] = 0; | ||
686 | memset( this->mmc5.exram, 0, mmc5_exram_size ); | ||
687 | } | ||
688 | |||
689 | if ( fds_enabled( this ) ) Fds_reset( &this->fds ); | ||
690 | if ( namco_enabled( this ) ) Namco_reset( &this->namco ); | ||
691 | if ( vrc6_enabled( this ) ) Vrc6_reset( &this->vrc6 ); | ||
692 | if ( fme7_enabled( this ) ) Fme7_reset( &this->fme7 ); | ||
693 | if ( mmc5_enabled( this ) ) Apu_reset( &this->mmc5.apu, false, 0 ); | ||
694 | if ( vrc7_enabled( this ) ) Vrc7_reset( &this->vrc7 ); | ||
695 | #endif | ||
696 | |||
697 | int speed_flags = 0; | ||
698 | #ifdef NSF_EMU_EXTRA_FLAGS | ||
699 | speed_flags = this->header.speed_flags; | ||
700 | #endif | ||
701 | |||
702 | Apu_reset( &this->apu, pal_only( &this->header ), (speed_flags & 0x20) ? 0x3F : 0 ); | ||
703 | Apu_write_register( &this->apu, 0, 0x4015, 0x0F ); | ||
704 | Apu_write_register( &this->apu, 0, 0x4017, (speed_flags & 0x10) ? 0x80 : 0 ); | ||
705 | |||
706 | memset( unmapped_code( this ), halt_opcode, unmapped_size ); | ||
707 | memset( this->low_ram, 0, low_ram_size ); | ||
708 | memset( sram( this ), 0, sram_size ); | ||
709 | |||
710 | map_memory( this ); | ||
711 | |||
712 | // Arrange time of first call to play routine | ||
713 | this->play_extra = 0; | ||
714 | this->next_play = this->play_period; | ||
715 | |||
716 | this->play_delay = initial_play_delay; | ||
717 | this->saved_state.pc = idle_addr; | ||
718 | |||
719 | // Setup for call to init routine | ||
720 | this->cpu.r.a = track; | ||
721 | this->cpu.r.x = pal_only( &this->header ); | ||
722 | this->cpu.r.sp = 0xFF; | ||
723 | jsr_then_stop( this, this->header.init_addr ); | ||
724 | /* if ( this->cpu.r.pc < get_addr( header.load_addr ) ) | ||
725 | warning( "Init address < load address" ); */ | ||
726 | |||
727 | this->emu_track_ended_ = false; | ||
728 | this->track_ended = false; | ||
729 | |||
730 | if ( !this->ignore_silence ) | ||
731 | { | ||
732 | // play until non-silence or end of track | ||
733 | long end; | ||
734 | for ( end = this->max_initial_silence * stereo * this->sample_rate; this->emu_time < end; ) | ||
735 | { | ||
736 | fill_buf( this ); | ||
737 | if ( this->buf_remain | (int) this->emu_track_ended_ ) | ||
738 | break; | ||
739 | } | ||
740 | |||
741 | this->emu_time = this->buf_remain; | ||
742 | this->out_time = 0; | ||
743 | this->silence_time = 0; | ||
744 | this->silence_count = 0; | ||
745 | } | ||
746 | /* return track_ended() ? warning() : 0; */ | ||
747 | return 0; | ||
748 | } | ||
749 | |||
750 | void run_once( struct Nsf_Emu* this, nes_time_t end ) | ||
751 | { | ||
752 | // Emulate until next play call if possible | ||
753 | if ( run_cpu_until( this, min( this->next_play, end ) ) ) | ||
754 | { | ||
755 | // Halt instruction encountered | ||
756 | |||
757 | if ( this->cpu.r.pc != idle_addr ) | ||
758 | { | ||
759 | // special_event( "illegal instruction" ); | ||
760 | Cpu_set_time( &this->cpu, this->cpu.end_time ); | ||
761 | return; | ||
762 | } | ||
763 | |||
764 | // Init/play routine returned | ||
765 | this->play_delay = 1; // play can now be called regularly | ||
766 | |||
767 | if ( this->saved_state.pc == idle_addr ) | ||
768 | { | ||
769 | // nothing to run | ||
770 | nes_time_t t = this->cpu.end_time; | ||
771 | if ( Cpu_time( &this->cpu ) < t ) | ||
772 | Cpu_set_time( &this->cpu, t ); | ||
773 | } | ||
774 | else | ||
775 | { | ||
776 | // continue init routine that was interrupted by play routine | ||
777 | this->cpu.r = this->saved_state; | ||
778 | this->saved_state.pc = idle_addr; | ||
779 | } | ||
780 | } | ||
781 | |||
782 | if ( Cpu_time( &this->cpu ) >= this->next_play ) | ||
783 | { | ||
784 | // Calculate time of next call to play routine | ||
785 | this->play_extra ^= 1; // extra clock every other call | ||
786 | this->next_play += this->play_period + this->play_extra; | ||
787 | |||
788 | // Call routine if ready | ||
789 | if ( this->play_delay && !--this->play_delay ) | ||
790 | { | ||
791 | // Save state if init routine is still running | ||
792 | if ( this->cpu.r.pc != idle_addr ) | ||
793 | { | ||
794 | check( this->saved_state.pc == idle_addr ); | ||
795 | this->saved_state = this->cpu.r; | ||
796 | // special_event( "play called during init" ); | ||
797 | } | ||
798 | |||
799 | jsr_then_stop( this, this->header.play_addr ); | ||
800 | } | ||
801 | } | ||
802 | } | ||
803 | |||
804 | void run_until( struct Nsf_Emu* this, nes_time_t end ) | ||
805 | { | ||
806 | while ( Cpu_time( &this->cpu ) < end ) | ||
807 | run_once( this, end ); | ||
808 | } | ||
809 | |||
810 | void end_frame( struct Nsf_Emu* this, nes_time_t end ) | ||
811 | { | ||
812 | if ( Cpu_time( &this->cpu ) < end ) | ||
813 | run_until( this, end ); | ||
814 | Cpu_adjust_time( &this->cpu, -end ); | ||
815 | |||
816 | // Localize to new time frame | ||
817 | this->next_play -= end; | ||
818 | check( this->next_play >= 0 ); | ||
819 | if ( this->next_play < 0 ) | ||
820 | this->next_play = 0; | ||
821 | |||
822 | Apu_end_frame( &this->apu, end ); | ||
823 | |||
824 | #ifndef NSF_EMU_APU_ONLY | ||
825 | if ( fds_enabled( this ) ) Fds_end_frame( &this->fds, end ); | ||
826 | if ( fme7_enabled( this ) ) Fme7_end_frame( &this->fme7, end ); | ||
827 | if ( mmc5_enabled( this ) ) Apu_end_frame( &this->mmc5.apu, end ); | ||
828 | if ( namco_enabled( this ) ) Namco_end_frame( &this->namco, end ); | ||
829 | if ( vrc6_enabled( this ) ) Vrc6_end_frame( &this->vrc6, end ); | ||
830 | if ( vrc7_enabled( this ) ) Vrc7_end_frame( &this->vrc7, end ); | ||
831 | #endif | ||
832 | } | ||
833 | |||
834 | // Tell/Seek | ||
835 | |||
836 | blargg_long msec_to_samples( long sample_rate, blargg_long msec ) | ||
837 | { | ||
838 | blargg_long sec = msec / 1000; | ||
839 | msec -= sec * 1000; | ||
840 | return (sec * sample_rate + msec * sample_rate / 1000) * stereo; | ||
841 | } | ||
842 | |||
843 | long Track_tell( struct Nsf_Emu* this ) | ||
844 | { | ||
845 | blargg_long rate = this->sample_rate * stereo; | ||
846 | blargg_long sec = this->out_time / rate; | ||
847 | return sec * 1000 + (this->out_time - sec * rate) * 1000 / rate; | ||
848 | } | ||
849 | |||
850 | blargg_err_t Track_seek( struct Nsf_Emu* this, long msec ) | ||
851 | { | ||
852 | blargg_long time = msec_to_samples( this->sample_rate, msec ); | ||
853 | if ( time < this->out_time ) | ||
854 | RETURN_ERR( Nsf_start_track( this, this->current_track ) ); | ||
855 | return Track_skip( this, time - this->out_time ); | ||
856 | } | ||
857 | |||
858 | blargg_err_t skip_( struct Nsf_Emu* this, long count ) ICODE_ATTR; | ||
859 | blargg_err_t Track_skip( struct Nsf_Emu* this, long count ) | ||
860 | { | ||
861 | require( this->current_track >= 0 ); // start_track() must have been called already | ||
862 | this->out_time += count; | ||
863 | |||
864 | // remove from silence and buf first | ||
865 | { | ||
866 | long n = min( count, this->silence_count ); | ||
867 | this->silence_count -= n; | ||
868 | count -= n; | ||
869 | |||
870 | n = min( count, this->buf_remain ); | ||
871 | this->buf_remain -= n; | ||
872 | count -= n; | ||
873 | } | ||
874 | |||
875 | if ( count && !this->emu_track_ended_ ) | ||
876 | { | ||
877 | this->emu_time += count; | ||
878 | // End track if error | ||
879 | if ( skip_( this, count ) ) | ||
880 | this->emu_track_ended_ = true; | ||
881 | } | ||
882 | |||
883 | if ( !(this->silence_count | this->buf_remain) ) // caught up to emulator, so update track ended | ||
884 | this->track_ended |= this->emu_track_ended_; | ||
885 | |||
886 | return 0; | ||
887 | } | ||
888 | |||
889 | blargg_err_t play_( struct Nsf_Emu* this, long count, sample_t* out ) ICODE_ATTR; | ||
890 | blargg_err_t skip_( struct Nsf_Emu* this, long count ) | ||
891 | { | ||
892 | // for long skip, mute sound | ||
893 | const long threshold = 30000; | ||
894 | if ( count > threshold ) | ||
895 | { | ||
896 | int saved_mute = this->mute_mask_; | ||
897 | Sound_mute_voices( this, ~0 ); | ||
898 | |||
899 | while ( count > threshold / 2 && !this->emu_track_ended_ ) | ||
900 | { | ||
901 | RETURN_ERR( play_( this, buf_size, this->buf ) ); | ||
902 | count -= buf_size; | ||
903 | } | ||
904 | |||
905 | Sound_mute_voices( this, saved_mute ); | ||
906 | } | ||
907 | |||
908 | while ( count && !this->emu_track_ended_ ) | ||
909 | { | ||
910 | long n = buf_size; | ||
911 | if ( n > count ) | ||
912 | n = count; | ||
913 | count -= n; | ||
914 | RETURN_ERR( play_( this, n, this->buf ) ); | ||
915 | } | ||
916 | return 0; | ||
917 | } | ||
918 | |||
919 | // Fading | ||
920 | |||
921 | void Track_set_fade( struct Nsf_Emu* this, long start_msec, long length_msec ) | ||
922 | { | ||
923 | this->fade_step = this->sample_rate * length_msec / (fade_block_size * fade_shift * 1000 / stereo); | ||
924 | this->fade_start = msec_to_samples( this->sample_rate, start_msec ); | ||
925 | } | ||
926 | |||
927 | // unit / pow( 2.0, (double) x / step ) | ||
928 | static int int_log( blargg_long x, int step, int unit ) | ||
929 | { | ||
930 | int shift = x / step; | ||
931 | int fraction = (x - shift * step) * unit / step; | ||
932 | return ((unit - fraction) + (fraction >> 1)) >> shift; | ||
933 | } | ||
934 | |||
935 | void handle_fade( struct Nsf_Emu* this, long out_count, sample_t* out ) | ||
936 | { | ||
937 | int i; | ||
938 | for ( i = 0; i < out_count; i += fade_block_size ) | ||
939 | { | ||
940 | int const shift = 14; | ||
941 | int const unit = 1 << shift; | ||
942 | int gain = int_log( (this->out_time + i - this->fade_start) / fade_block_size, | ||
943 | this->fade_step, unit ); | ||
944 | if ( gain < (unit >> fade_shift) ) | ||
945 | this->track_ended = this->emu_track_ended_ = true; | ||
946 | |||
947 | sample_t* io = &out [i]; | ||
948 | int count; | ||
949 | for ( count = min( fade_block_size, out_count - i ); count; --count ) | ||
950 | { | ||
951 | *io = (sample_t) ((*io * gain) >> shift); | ||
952 | ++io; | ||
953 | } | ||
954 | } | ||
955 | } | ||
956 | |||
957 | // Silence detection | ||
958 | |||
959 | void emu_play( struct Nsf_Emu* this, long count, sample_t* out ) ICODE_ATTR; | ||
960 | void emu_play( struct Nsf_Emu* this, long count, sample_t* out ) | ||
961 | { | ||
962 | check( current_track_ >= 0 ); | ||
963 | this->emu_time += count; | ||
964 | if ( this->current_track >= 0 && !this->emu_track_ended_ ) { | ||
965 | |||
966 | // End track if error | ||
967 | if ( play_( this, count, out ) ) | ||
968 | this->emu_track_ended_ = true; | ||
969 | } | ||
970 | else | ||
971 | memset( out, 0, count * sizeof *out ); | ||
972 | } | ||
973 | |||
974 | // number of consecutive silent samples at end | ||
975 | static long count_silence( sample_t* begin, long size ) | ||
976 | { | ||
977 | sample_t first = *begin; | ||
978 | *begin = silence_threshold; // sentinel | ||
979 | sample_t* p = begin + size; | ||
980 | while ( (unsigned) (*--p + silence_threshold / 2) <= (unsigned) silence_threshold ) { } | ||
981 | *begin = first; | ||
982 | return size - (p - begin); | ||
983 | } | ||
984 | |||
985 | // fill internal buffer and check it for silence | ||
986 | void fill_buf( struct Nsf_Emu* this ) | ||
987 | { | ||
988 | assert( !this->buf_remain ); | ||
989 | if ( !this->emu_track_ended_ ) | ||
990 | { | ||
991 | emu_play( this, buf_size, this->buf ); | ||
992 | long silence = count_silence( this->buf, buf_size ); | ||
993 | if ( silence < buf_size ) | ||
994 | { | ||
995 | this->silence_time = this->emu_time - silence; | ||
996 | this->buf_remain = buf_size; | ||
997 | return; | ||
998 | } | ||
999 | } | ||
1000 | this->silence_count += buf_size; | ||
1001 | } | ||
1002 | |||
1003 | blargg_err_t Nsf_play( struct Nsf_Emu* this, long out_count, sample_t* out ) | ||
1004 | { | ||
1005 | if ( this->track_ended ) | ||
1006 | { | ||
1007 | memset( out, 0, out_count * sizeof *out ); | ||
1008 | } | ||
1009 | else | ||
1010 | { | ||
1011 | require( this->current_track >= 0 ); | ||
1012 | require( out_count % stereo == 0 ); | ||
1013 | |||
1014 | assert( this->emu_time >= this->out_time ); | ||
1015 | |||
1016 | long pos = 0; | ||
1017 | if ( this->silence_count ) | ||
1018 | { | ||
1019 | // during a run of silence, run emulator at >=2x speed so it gets ahead | ||
1020 | long ahead_time = this->silence_lookahead * (this->out_time + out_count - this->silence_time) + this->silence_time; | ||
1021 | while ( this->emu_time < ahead_time && !(this->buf_remain | this->emu_track_ended_) ) | ||
1022 | fill_buf( this ); | ||
1023 | |||
1024 | // fill with silence | ||
1025 | pos = min( this->silence_count, out_count ); | ||
1026 | memset( out, 0, pos * sizeof *out ); | ||
1027 | this->silence_count -= pos; | ||
1028 | |||
1029 | if ( this->emu_time - this->silence_time > silence_max * stereo * this->sample_rate ) | ||
1030 | { | ||
1031 | this->track_ended = this->emu_track_ended_ = true; | ||
1032 | this->silence_count = 0; | ||
1033 | this->buf_remain = 0; | ||
1034 | } | ||
1035 | } | ||
1036 | |||
1037 | if ( this->buf_remain ) | ||
1038 | { | ||
1039 | // empty silence buf | ||
1040 | long n = min( this->buf_remain, out_count - pos ); | ||
1041 | memcpy( &out [pos], this->buf + (buf_size - this->buf_remain), n * sizeof *out ); | ||
1042 | this->buf_remain -= n; | ||
1043 | pos += n; | ||
1044 | } | ||
1045 | |||
1046 | // generate remaining samples normally | ||
1047 | long remain = out_count - pos; | ||
1048 | if ( remain ) | ||
1049 | { | ||
1050 | emu_play( this, remain, out + pos ); | ||
1051 | this->track_ended |= this->emu_track_ended_; | ||
1052 | |||
1053 | if ( !this->ignore_silence || this->out_time > this->fade_start ) | ||
1054 | { | ||
1055 | // check end for a new run of silence | ||
1056 | long silence = count_silence( out + pos, remain ); | ||
1057 | if ( silence < remain ) | ||
1058 | this->silence_time = this->emu_time - silence; | ||
1059 | |||
1060 | if ( this->emu_time - this->silence_time >= buf_size ) | ||
1061 | fill_buf( this ); // cause silence detection on next play() | ||
1062 | } | ||
1063 | } | ||
1064 | |||
1065 | if ( this->out_time > this->fade_start ) | ||
1066 | handle_fade( this, out_count, out ); | ||
1067 | } | ||
1068 | this->out_time += out_count; | ||
1069 | return 0; | ||
1070 | } | ||
1071 | |||
1072 | blargg_err_t play_( struct Nsf_Emu* this, long count, sample_t* out ) | ||
1073 | { | ||
1074 | long remain = count; | ||
1075 | while ( remain ) | ||
1076 | { | ||
1077 | remain -= Buffer_read_samples( &this->stereo_buf, &out [count - remain], remain ); | ||
1078 | if ( remain ) | ||
1079 | { | ||
1080 | if ( this->buf_changed_count != Buffer_channels_changed_count( &this->stereo_buf ) ) | ||
1081 | { | ||
1082 | this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf ); | ||
1083 | |||
1084 | // Remute voices | ||
1085 | Sound_mute_voices( this, this->mute_mask_ ); | ||
1086 | } | ||
1087 | int msec = Buffer_length( &this->stereo_buf ); | ||
1088 | blip_time_t clocks_emulated = (blargg_long) msec * this->clock_rate__ / 1000 - 100; | ||
1089 | RETURN_ERR( run_clocks( this, &clocks_emulated, msec ) ); | ||
1090 | assert( clocks_emulated ); | ||
1091 | Buffer_end_frame( &this->stereo_buf, clocks_emulated ); | ||
1092 | } | ||
1093 | } | ||
1094 | return 0; | ||
1095 | } | ||
1096 | |||
1097 | blargg_err_t run_clocks( struct Nsf_Emu* this, blip_time_t* duration, int msec ) | ||
1098 | { | ||
1099 | #if defined(ROCKBOX) | ||
1100 | (void) msec; | ||
1101 | #endif | ||
1102 | |||
1103 | end_frame( this, *duration ); | ||
1104 | return 0; | ||
1105 | } | ||