diff options
Diffstat (limited to 'lib/rbcodec/codecs/libgme/nsf_emu.c')
-rw-r--r-- | lib/rbcodec/codecs/libgme/nsf_emu.c | 921 |
1 files changed, 921 insertions, 0 deletions
diff --git a/lib/rbcodec/codecs/libgme/nsf_emu.c b/lib/rbcodec/codecs/libgme/nsf_emu.c new file mode 100644 index 0000000000..d9fc4e031d --- /dev/null +++ b/lib/rbcodec/codecs/libgme/nsf_emu.c | |||
@@ -0,0 +1,921 @@ | |||
1 | // Game_Music_Emu 0.6-pre. 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 [] = "Wrong file type for this emulator"; | ||
22 | |||
23 | // number of frames until play interrupts init | ||
24 | int const initial_play_delay = 7; // KikiKaikai needed this to work | ||
25 | int const bank_size = 0x1000; | ||
26 | int const rom_addr = 0x8000; | ||
27 | |||
28 | static void clear_track_vars( struct Nsf_Emu* this ) | ||
29 | { | ||
30 | this->current_track = -1; | ||
31 | track_stop( &this->track_filter ); | ||
32 | } | ||
33 | |||
34 | static int pcm_read( void* emu, int addr ) | ||
35 | { | ||
36 | return *Cpu_get_code( &((struct Nsf_Emu*) emu)->cpu, addr ); | ||
37 | } | ||
38 | |||
39 | void Nsf_init( struct Nsf_Emu* this ) | ||
40 | { | ||
41 | this->sample_rate = 0; | ||
42 | this->mute_mask_ = 0; | ||
43 | this->tempo = (int)(FP_ONE_TEMPO); | ||
44 | this->gain = (int)(FP_ONE_GAIN); | ||
45 | |||
46 | // defaults | ||
47 | this->tfilter = *track_get_setup( &this->track_filter ); | ||
48 | this->tfilter.max_initial = 2; | ||
49 | this->tfilter.lookahead = 6; | ||
50 | this->track_filter.silence_ignored_ = false; | ||
51 | |||
52 | // Set sound gain | ||
53 | Sound_set_gain( this, (int)(FP_ONE_GAIN*1.2) ); | ||
54 | |||
55 | // Init rom | ||
56 | Rom_init( &this->rom, bank_size ); | ||
57 | |||
58 | // Init & clear nsfe info | ||
59 | Info_init( &this->info ); | ||
60 | Info_unload( &this->info ); // TODO: extremely hacky! | ||
61 | |||
62 | Cpu_init( &this->cpu ); | ||
63 | Apu_init( &this->apu ); | ||
64 | Apu_dmc_reader( &this->apu, pcm_read, this ); | ||
65 | |||
66 | // Unload | ||
67 | this->voice_count = 0; | ||
68 | memset( this->voice_types, 0, sizeof this->voice_types ); | ||
69 | clear_track_vars( this ); | ||
70 | } | ||
71 | |||
72 | // Setup | ||
73 | |||
74 | static void append_voices( struct Nsf_Emu* this, int const types [], int count ) | ||
75 | { | ||
76 | assert( this->voice_count + count < max_voices ); | ||
77 | int i; | ||
78 | for ( i = 0; i < count; i++ ) { | ||
79 | this->voice_types [this->voice_count + i] = types [i]; | ||
80 | } | ||
81 | this->voice_count += count; | ||
82 | } | ||
83 | |||
84 | static blargg_err_t init_sound( struct Nsf_Emu* this ) | ||
85 | { | ||
86 | /* if ( header_.chip_flags & ~(fds_flag | namco_flag | vrc6_flag | fme7_flag) ) | ||
87 | warning( "Uses unsupported audio expansion hardware" ); **/ | ||
88 | |||
89 | { | ||
90 | static int const types [apu_osc_count] = { | ||
91 | wave_type+1, wave_type+2, mixed_type+1, noise_type+0, mixed_type+1 | ||
92 | }; | ||
93 | append_voices( this, types, apu_osc_count ); | ||
94 | } | ||
95 | |||
96 | int adjusted_gain = (this->gain * 4) / 3; | ||
97 | |||
98 | #ifdef NSF_EMU_APU_ONLY | ||
99 | { | ||
100 | if ( this->header_.chip_flags ) | ||
101 | set_warning( "Uses unsupported audio expansion hardware" ); | ||
102 | } | ||
103 | #else | ||
104 | { | ||
105 | if ( vrc6_enabled( this ) ) | ||
106 | { | ||
107 | Vrc6_init( &this->vrc6 ); | ||
108 | adjusted_gain = (adjusted_gain*3) / 4; | ||
109 | |||
110 | static int const types [vrc6_osc_count] = { | ||
111 | wave_type+3, wave_type+4, wave_type+5, | ||
112 | }; | ||
113 | append_voices( this, types, vrc6_osc_count ); | ||
114 | } | ||
115 | |||
116 | if ( fme7_enabled( this ) ) | ||
117 | { | ||
118 | Fme7_init( &this->fme7 ); | ||
119 | adjusted_gain = (adjusted_gain*3) / 4; | ||
120 | |||
121 | static int const types [fme7_osc_count] = { | ||
122 | wave_type+3, wave_type+4, wave_type+5, | ||
123 | }; | ||
124 | append_voices( this, types, fme7_osc_count ); | ||
125 | } | ||
126 | |||
127 | if ( mmc5_enabled( this ) ) | ||
128 | { | ||
129 | Mmc5_init( &this->mmc5 ); | ||
130 | adjusted_gain = (adjusted_gain*3) / 4; | ||
131 | |||
132 | |||
133 | static int const types [mmc5_osc_count] = { | ||
134 | wave_type+3, wave_type+4, mixed_type+2 | ||
135 | }; | ||
136 | append_voices( this, types, mmc5_osc_count ); | ||
137 | } | ||
138 | |||
139 | if ( fds_enabled( this ) ) | ||
140 | { | ||
141 | Fds_init( &this->fds ); | ||
142 | adjusted_gain = (adjusted_gain*3) / 4; | ||
143 | |||
144 | static int const types [fds_osc_count] = { | ||
145 | wave_type+0 | ||
146 | }; | ||
147 | append_voices( this, types, fds_osc_count ); | ||
148 | } | ||
149 | |||
150 | if ( namco_enabled( this ) ) | ||
151 | { | ||
152 | Namco_init( &this->namco ); | ||
153 | adjusted_gain = (adjusted_gain*3) / 4; | ||
154 | |||
155 | static int const types [namco_osc_count] = { | ||
156 | wave_type+3, wave_type+4, wave_type+5, wave_type+ 6, | ||
157 | wave_type+7, wave_type+8, wave_type+9, wave_type+10, | ||
158 | }; | ||
159 | append_voices( this, types, namco_osc_count ); | ||
160 | } | ||
161 | |||
162 | #ifndef NSF_EMU_NO_VRC7 | ||
163 | if ( vrc7_enabled( this ) ) | ||
164 | { | ||
165 | |||
166 | Vrc7_init( &this->vrc7 ); | ||
167 | Vrc7_set_rate( &this->vrc7, this->sample_rate ); | ||
168 | |||
169 | adjusted_gain = (adjusted_gain*3) / 4; | ||
170 | |||
171 | static int const types [vrc7_osc_count] = { | ||
172 | wave_type+3, wave_type+4, wave_type+5, wave_type+6, | ||
173 | wave_type+7, wave_type+8 | ||
174 | }; | ||
175 | append_voices( this, types, vrc7_osc_count ); | ||
176 | } | ||
177 | |||
178 | if ( vrc7_enabled( this ) ) Vrc7_volume( &this->vrc7, adjusted_gain ); | ||
179 | #endif | ||
180 | if ( namco_enabled( this ) ) Namco_volume( &this->namco, adjusted_gain ); | ||
181 | if ( vrc6_enabled( this ) ) Vrc6_volume( &this->vrc6, adjusted_gain ); | ||
182 | if ( fme7_enabled( this ) ) Fme7_volume( &this->fme7, adjusted_gain ); | ||
183 | if ( mmc5_enabled( this ) ) Apu_volume( &this->mmc5.apu, adjusted_gain ); | ||
184 | if ( fds_enabled( this ) ) Fds_volume( &this->fds, adjusted_gain ); | ||
185 | } | ||
186 | #endif | ||
187 | |||
188 | if ( adjusted_gain > this->gain ) | ||
189 | adjusted_gain = this->gain; | ||
190 | |||
191 | Apu_volume( &this->apu, adjusted_gain ); | ||
192 | |||
193 | return 0; | ||
194 | } | ||
195 | |||
196 | // Header stuff | ||
197 | static bool valid_tag( struct header_t* this ) | ||
198 | { | ||
199 | return 0 == memcmp( this->tag, "NESM\x1A", 5 ); | ||
200 | } | ||
201 | |||
202 | // True if file supports only PAL speed | ||
203 | static bool pal_only( struct header_t* this ) | ||
204 | { | ||
205 | return (this->speed_flags & 3) == 1; | ||
206 | } | ||
207 | |||
208 | static int clock_rate( struct header_t* this ) | ||
209 | { | ||
210 | return pal_only( this ) ? (int)1662607.125 : (int)1789772.727272727; | ||
211 | } | ||
212 | |||
213 | static int play_period( struct header_t* this ) | ||
214 | { | ||
215 | // NTSC | ||
216 | int clocks = 29780; | ||
217 | int value = 0x411A; | ||
218 | byte const* rate_ptr = this->ntsc_speed; | ||
219 | |||
220 | // PAL | ||
221 | if ( pal_only( this ) ) | ||
222 | { | ||
223 | clocks = 33247; | ||
224 | value = 0x4E20; | ||
225 | rate_ptr = this->pal_speed; | ||
226 | } | ||
227 | |||
228 | // Default rate | ||
229 | int rate = get_le16( rate_ptr ); | ||
230 | if ( rate == 0 ) | ||
231 | rate = value; | ||
232 | |||
233 | // Custom rate | ||
234 | if ( rate != value ) | ||
235 | clocks = (int) ((1LL * rate * clock_rate( this )) / 1000000); | ||
236 | |||
237 | return clocks; | ||
238 | } | ||
239 | |||
240 | // Gets address, given pointer to it in file header. If zero, returns rom_addr. | ||
241 | addr_t get_addr( byte const in [] ) | ||
242 | { | ||
243 | addr_t addr = get_le16( in ); | ||
244 | if ( addr == 0 ) | ||
245 | addr = rom_addr; | ||
246 | return addr; | ||
247 | } | ||
248 | |||
249 | static blargg_err_t check_nsf_header( struct header_t* h ) | ||
250 | { | ||
251 | if ( !valid_tag( h ) ) | ||
252 | return gme_wrong_file_type; | ||
253 | return 0; | ||
254 | } | ||
255 | |||
256 | blargg_err_t Nsf_load_mem( struct Nsf_Emu* this, void* data, long size ) | ||
257 | { | ||
258 | // Unload | ||
259 | Info_unload( &this->info ); // TODO: extremely hacky! | ||
260 | this->m3u.size = 0; | ||
261 | |||
262 | this->voice_count = 0; | ||
263 | clear_track_vars( this ); | ||
264 | |||
265 | assert( offsetof (struct header_t,unused [4]) == header_size ); | ||
266 | |||
267 | if ( !memcmp( data, "NESM\x1A", 5 ) ) { | ||
268 | Nsf_disable_playlist( this, true ); | ||
269 | |||
270 | RETURN_ERR( Rom_load( &this->rom, data, size, header_size, &this->header, 0 ) ); | ||
271 | return Nsf_post_load( this ); | ||
272 | } | ||
273 | |||
274 | blargg_err_t err = Info_load( &this->info, data, size, this ); | ||
275 | Nsf_disable_playlist( this, false ); | ||
276 | return err; | ||
277 | } | ||
278 | |||
279 | blargg_err_t Nsf_post_load( struct Nsf_Emu* this ) | ||
280 | { | ||
281 | RETURN_ERR( check_nsf_header( &this->header ) ); | ||
282 | |||
283 | /* if ( header_.vers != 1 ) | ||
284 | warning( "Unknown file version" ); */ | ||
285 | |||
286 | // set up data | ||
287 | addr_t load_addr = get_addr( this->header.load_addr ); | ||
288 | /* if ( load_addr < (fds_enabled() ? sram_addr : rom_addr) ) | ||
289 | warning( "Load address is too low" ); */ | ||
290 | |||
291 | Rom_set_addr( &this->rom, load_addr % this->rom.bank_size ); | ||
292 | |||
293 | /* if ( header_.vers != 1 ) | ||
294 | warning( "Unknown file version" ); */ | ||
295 | |||
296 | set_play_period( this, play_period( &this->header ) ); | ||
297 | |||
298 | // sound and memory | ||
299 | blargg_err_t err = init_sound( this ); | ||
300 | if ( err ) | ||
301 | return err; | ||
302 | |||
303 | // Set track_count | ||
304 | this->track_count = this->header.track_count; | ||
305 | |||
306 | // Change clock rate & setup buffer | ||
307 | this->clock_rate__ = clock_rate( &this->header ); | ||
308 | Buffer_clock_rate( &this->stereo_buf, this->clock_rate__ ); | ||
309 | RETURN_ERR( Buffer_set_channel_count( &this->stereo_buf, this->voice_count, this->voice_types ) ); | ||
310 | this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf ); | ||
311 | |||
312 | // Post load | ||
313 | Sound_set_tempo( this, this->tempo ); | ||
314 | Sound_mute_voices( this, this->mute_mask_ ); | ||
315 | return 0; | ||
316 | } | ||
317 | |||
318 | void Nsf_disable_playlist( struct Nsf_Emu* this, bool b ) | ||
319 | { | ||
320 | Info_disable_playlist( &this->info, b ); | ||
321 | this->track_count = this->info.track_count; | ||
322 | } | ||
323 | |||
324 | void Nsf_clear_playlist( struct Nsf_Emu* this ) | ||
325 | { | ||
326 | Nsf_disable_playlist( this, true ); | ||
327 | } | ||
328 | |||
329 | void write_bank( struct Nsf_Emu* this, int bank, int data ) | ||
330 | { | ||
331 | // Find bank in ROM | ||
332 | int offset = mask_addr( data * this->rom.bank_size, this->rom.mask ); | ||
333 | /* if ( offset >= rom.size() ) | ||
334 | warning( "invalid bank" ); */ | ||
335 | void const* rom_data = Rom_at_addr( &this->rom, offset ); | ||
336 | |||
337 | #ifndef NSF_EMU_APU_ONLY | ||
338 | if ( bank < bank_count - fds_banks && fds_enabled( this ) ) | ||
339 | { | ||
340 | // TODO: FDS bank switching is kind of hacky, might need to | ||
341 | // treat ROM as RAM so changes won't get lost when switching. | ||
342 | byte* out = sram( this ); | ||
343 | if ( bank >= fds_banks ) | ||
344 | { | ||
345 | out = fdsram( this ); | ||
346 | bank -= fds_banks; | ||
347 | } | ||
348 | memcpy( &out [bank * this->rom.bank_size], rom_data, this->rom.bank_size ); | ||
349 | return; | ||
350 | } | ||
351 | #endif | ||
352 | |||
353 | if ( bank >= fds_banks ) | ||
354 | Cpu_map_code( &this->cpu, (bank + 6) * this->rom.bank_size, this->rom.bank_size, rom_data, false ); | ||
355 | } | ||
356 | |||
357 | static void map_memory( struct Nsf_Emu* this ) | ||
358 | { | ||
359 | // Map standard things | ||
360 | Cpu_reset( &this->cpu, unmapped_code( this ) ); | ||
361 | Cpu_map_code( &this->cpu, 0, 0x2000, this->low_ram, low_ram_size ); // mirrored four times | ||
362 | Cpu_map_code( &this->cpu, sram_addr, sram_size, sram( this ), 0 ); | ||
363 | |||
364 | // Determine initial banks | ||
365 | byte banks [bank_count]; | ||
366 | static byte const zero_banks [sizeof this->header.banks] = { 0 }; | ||
367 | if ( memcmp( this->header.banks, zero_banks, sizeof zero_banks ) ) | ||
368 | { | ||
369 | banks [0] = this->header.banks [6]; | ||
370 | banks [1] = this->header.banks [7]; | ||
371 | memcpy( banks + fds_banks, this->header.banks, sizeof this->header.banks ); | ||
372 | } | ||
373 | else | ||
374 | { | ||
375 | // No initial banks, so assign them based on load_addr | ||
376 | int i, first_bank = (get_addr( this->header.load_addr ) - sram_addr) / this->rom.bank_size; | ||
377 | unsigned total_banks = this->rom.size / this->rom.bank_size; | ||
378 | for ( i = bank_count; --i >= 0; ) | ||
379 | { | ||
380 | int bank = i - first_bank; | ||
381 | if ( (unsigned) bank >= total_banks ) | ||
382 | bank = 0; | ||
383 | banks [i] = bank; | ||
384 | } | ||
385 | } | ||
386 | |||
387 | // Map banks | ||
388 | int i; | ||
389 | for ( i = (fds_enabled( this ) ? 0 : fds_banks); i < bank_count; ++i ) | ||
390 | write_bank( this, i, banks [i] ); | ||
391 | |||
392 | // Map FDS RAM | ||
393 | if ( fds_enabled( this ) ) | ||
394 | Cpu_map_code( &this->cpu, rom_addr, fdsram_size, fdsram( this ), 0 ); | ||
395 | } | ||
396 | |||
397 | static void set_voice( struct Nsf_Emu* this, int i, struct Blip_Buffer* buf, struct Blip_Buffer* left, struct Blip_Buffer* right) | ||
398 | { | ||
399 | #if defined(ROCKBOX) | ||
400 | (void) left; | ||
401 | (void) right; | ||
402 | #endif | ||
403 | |||
404 | if ( i < apu_osc_count ) | ||
405 | { | ||
406 | Apu_osc_output( &this->apu, i, buf ); | ||
407 | return; | ||
408 | } | ||
409 | i -= apu_osc_count; | ||
410 | |||
411 | #ifndef NSF_EMU_APU_ONLY | ||
412 | { | ||
413 | if ( vrc6_enabled( this ) && (i -= vrc6_osc_count) < 0 ) | ||
414 | { | ||
415 | Vrc6_osc_output( &this->vrc6, i + vrc6_osc_count, buf ); | ||
416 | return; | ||
417 | } | ||
418 | |||
419 | if ( fme7_enabled( this ) && (i -= fme7_osc_count) < 0 ) | ||
420 | { | ||
421 | Fme7_osc_output( &this->fme7, i + fme7_osc_count, buf ); | ||
422 | return; | ||
423 | } | ||
424 | |||
425 | if ( mmc5_enabled( this ) && (i -= mmc5_osc_count) < 0 ) | ||
426 | { | ||
427 | Mmc5_set_output( &this->mmc5, i + mmc5_osc_count, buf ); | ||
428 | return; | ||
429 | } | ||
430 | |||
431 | if ( fds_enabled( this ) && (i -= fds_osc_count) < 0 ) | ||
432 | { | ||
433 | Fds_set_output( &this->fds, i + fds_osc_count, buf ); | ||
434 | return; | ||
435 | } | ||
436 | |||
437 | if ( namco_enabled( this ) && (i -= namco_osc_count) < 0 ) | ||
438 | { | ||
439 | Namco_osc_output( &this->namco, i + namco_osc_count, buf ); | ||
440 | return; | ||
441 | } | ||
442 | |||
443 | #ifndef NSF_EMU_NO_VRC7 | ||
444 | if ( vrc7_enabled( this ) && (i -= vrc7_osc_count) < 0 ) | ||
445 | { | ||
446 | Vrc7_set_output( &this->vrc7, i + vrc7_osc_count, buf ); | ||
447 | return; | ||
448 | } | ||
449 | #endif | ||
450 | } | ||
451 | #endif | ||
452 | } | ||
453 | |||
454 | // Emulation | ||
455 | |||
456 | // Music Emu | ||
457 | |||
458 | blargg_err_t Nsf_set_sample_rate( struct Nsf_Emu* this, int rate ) | ||
459 | { | ||
460 | require( !this->sample_rate ); // sample rate can't be changed once set | ||
461 | Buffer_init( &this->stereo_buf ); | ||
462 | RETURN_ERR( Buffer_set_sample_rate( &this->stereo_buf, rate, 1000 / 20 ) ); | ||
463 | |||
464 | // Set bass frequency | ||
465 | Buffer_bass_freq( &this->stereo_buf, 80 ); | ||
466 | |||
467 | this->sample_rate = rate; | ||
468 | RETURN_ERR( track_init( &this->track_filter, this ) ); | ||
469 | this->tfilter.max_silence = 6 * stereo * this->sample_rate; | ||
470 | return 0; | ||
471 | } | ||
472 | |||
473 | void Sound_mute_voice( struct Nsf_Emu* this, int index, bool mute ) | ||
474 | { | ||
475 | require( (unsigned) index < (unsigned) this->voice_count ); | ||
476 | int bit = 1 << index; | ||
477 | int mask = this->mute_mask_ | bit; | ||
478 | if ( !mute ) | ||
479 | mask ^= bit; | ||
480 | Sound_mute_voices( this, mask ); | ||
481 | } | ||
482 | |||
483 | void Sound_mute_voices( struct Nsf_Emu* this, int mask ) | ||
484 | { | ||
485 | require( this->sample_rate ); // sample rate must be set first | ||
486 | this->mute_mask_ = mask; | ||
487 | |||
488 | int i; | ||
489 | for ( i = this->voice_count; i--; ) | ||
490 | { | ||
491 | if ( mask & (1 << i) ) | ||
492 | { | ||
493 | set_voice( this, i, 0, 0, 0 ); | ||
494 | } | ||
495 | else | ||
496 | { | ||
497 | struct channel_t ch = Buffer_channel( &this->stereo_buf, i ); | ||
498 | assert( (ch.center && ch.left && ch.right) || | ||
499 | (!ch.center && !ch.left && !ch.right) ); // all or nothing | ||
500 | set_voice( this, i, ch.center, ch.left, ch.right ); | ||
501 | } | ||
502 | } | ||
503 | } | ||
504 | |||
505 | void Sound_set_tempo( struct Nsf_Emu* this, int t ) | ||
506 | { | ||
507 | require( this->sample_rate ); // sample rate must be set first | ||
508 | int const min = (int)(FP_ONE_TEMPO*0.02); | ||
509 | int const max = (int)(FP_ONE_TEMPO*4.00); | ||
510 | if ( t < min ) t = min; | ||
511 | if ( t > max ) t = max; | ||
512 | this->tempo = t; | ||
513 | |||
514 | set_play_period( this, (int) ((play_period( &this->header ) * FP_ONE_TEMPO) / t) ); | ||
515 | |||
516 | Apu_set_tempo( &this->apu, t ); | ||
517 | |||
518 | #ifndef NSF_EMU_APU_ONLY | ||
519 | if ( fds_enabled( this ) ) | ||
520 | Fds_set_tempo( &this->fds, t ); | ||
521 | #endif | ||
522 | } | ||
523 | |||
524 | static inline void push_byte( struct Nsf_Emu* this, int b ) | ||
525 | { | ||
526 | this->low_ram [0x100 + this->cpu.r.sp--] = b; | ||
527 | } | ||
528 | |||
529 | // Jumps to routine, given pointer to address in file header. Pushes idle_addr | ||
530 | // as return address, NOT old PC. | ||
531 | static void jsr_then_stop( struct Nsf_Emu* this, byte const addr [] ) | ||
532 | { | ||
533 | this->cpu.r.pc = get_addr( addr ); | ||
534 | push_byte( this, (idle_addr - 1) >> 8 ); | ||
535 | push_byte( this, (idle_addr - 1) ); | ||
536 | } | ||
537 | |||
538 | int cpu_read( struct Nsf_Emu* this, addr_t addr ) | ||
539 | { | ||
540 | #ifndef NSF_EMU_APU_ONLY | ||
541 | { | ||
542 | if ( namco_enabled( this ) && addr == namco_data_reg_addr ) | ||
543 | return Namco_read_data( &this->namco ); | ||
544 | |||
545 | if ( fds_enabled( this ) && (unsigned) (addr - fds_io_addr) < fds_io_size ) | ||
546 | return Fds_read( &this->fds, Cpu_time( &this->cpu ), addr ); | ||
547 | |||
548 | if ( mmc5_enabled( this ) ) { | ||
549 | int i = addr - 0x5C00; | ||
550 | if ( (unsigned) i < mmc5_exram_size ) | ||
551 | return this->mmc5.exram [i]; | ||
552 | |||
553 | int m = addr - 0x5205; | ||
554 | if ( (unsigned) m < 2 ) | ||
555 | return (this->mmc5_mul [0] * this->mmc5_mul [1]) >> (m * 8) & 0xFF; | ||
556 | } | ||
557 | } | ||
558 | #endif | ||
559 | |||
560 | /* Unmapped read */ | ||
561 | return addr >> 8; | ||
562 | } | ||
563 | |||
564 | void cpu_write( struct Nsf_Emu* this, addr_t addr, int data ) | ||
565 | { | ||
566 | #ifndef NSF_EMU_APU_ONLY | ||
567 | { | ||
568 | if ( fds_enabled( this) && (unsigned) (addr - fds_io_addr) < fds_io_size ) | ||
569 | { | ||
570 | Fds_write( &this->fds, Cpu_time( &this->cpu ), addr, data ); | ||
571 | return; | ||
572 | } | ||
573 | |||
574 | if ( namco_enabled( this) ) | ||
575 | { | ||
576 | if ( addr == namco_addr_reg_addr ) | ||
577 | { | ||
578 | Namco_write_addr( &this->namco, data ); | ||
579 | return; | ||
580 | } | ||
581 | |||
582 | if ( addr == namco_data_reg_addr ) | ||
583 | { | ||
584 | Namco_write_data( &this->namco, Cpu_time( &this->cpu ), data ); | ||
585 | return; | ||
586 | } | ||
587 | } | ||
588 | |||
589 | if ( vrc6_enabled( this) ) | ||
590 | { | ||
591 | int reg = addr & (vrc6_addr_step - 1); | ||
592 | int osc = (unsigned) (addr - vrc6_base_addr) / vrc6_addr_step; | ||
593 | if ( (unsigned) osc < vrc6_osc_count && (unsigned) reg < vrc6_reg_count ) | ||
594 | { | ||
595 | Vrc6_write_osc( &this->vrc6, Cpu_time( &this->cpu ), osc, reg, data ); | ||
596 | return; | ||
597 | } | ||
598 | } | ||
599 | |||
600 | if ( fme7_enabled( this) && addr >= fme7_latch_addr ) | ||
601 | { | ||
602 | switch ( addr & fme7_addr_mask ) | ||
603 | { | ||
604 | case fme7_latch_addr: | ||
605 | Fme7_write_latch( &this->fme7, data ); | ||
606 | return; | ||
607 | |||
608 | case fme7_data_addr: | ||
609 | Fme7_write_data( &this->fme7, Cpu_time( &this->cpu ), data ); | ||
610 | return; | ||
611 | } | ||
612 | } | ||
613 | |||
614 | if ( mmc5_enabled( this) ) | ||
615 | { | ||
616 | if ( (unsigned) (addr - mmc5_regs_addr) < mmc5_regs_size ) | ||
617 | { | ||
618 | Mmc5_write_register( &this->mmc5, Cpu_time( &this->cpu ), addr, data ); | ||
619 | return; | ||
620 | } | ||
621 | |||
622 | int m = addr - 0x5205; | ||
623 | if ( (unsigned) m < 2 ) | ||
624 | { | ||
625 | this->mmc5_mul [m] = data; | ||
626 | return; | ||
627 | } | ||
628 | |||
629 | int i = addr - 0x5C00; | ||
630 | if ( (unsigned) i < mmc5_exram_size ) | ||
631 | { | ||
632 | this->mmc5.exram [i] = data; | ||
633 | return; | ||
634 | } | ||
635 | } | ||
636 | |||
637 | #ifndef NSF_EMU_NO_VRC7 | ||
638 | if ( vrc7_enabled( this) ) | ||
639 | { | ||
640 | if ( addr == 0x9010 ) | ||
641 | { | ||
642 | Vrc7_write_reg( &this->vrc7, data ); | ||
643 | return; | ||
644 | } | ||
645 | |||
646 | if ( (unsigned) (addr - 0x9028) <= 0x08 ) | ||
647 | { | ||
648 | Vrc7_write_data( &this->vrc7, Cpu_time( &this->cpu ), data ); | ||
649 | return; | ||
650 | } | ||
651 | } | ||
652 | #endif | ||
653 | } | ||
654 | #endif | ||
655 | |||
656 | // Unmapped_write | ||
657 | } | ||
658 | |||
659 | blargg_err_t Nsf_start_track( struct Nsf_Emu* this, int track ) | ||
660 | { | ||
661 | clear_track_vars( this ); | ||
662 | |||
663 | // Remap track if playlist available | ||
664 | if ( this->m3u.size > 0 ) { | ||
665 | struct entry_t* e = &this->m3u.entries[track]; | ||
666 | track = e->track; | ||
667 | } | ||
668 | else track = Info_remap_track( &this->info, track ); | ||
669 | |||
670 | this->current_track = track; | ||
671 | Buffer_clear( &this->stereo_buf ); | ||
672 | |||
673 | #ifndef NSF_EMU_APU_ONLY | ||
674 | if ( mmc5_enabled( this ) ) | ||
675 | { | ||
676 | this->mmc5_mul [0] = 0; | ||
677 | this->mmc5_mul [1] = 0; | ||
678 | memset( this->mmc5.exram, 0, mmc5_exram_size ); | ||
679 | } | ||
680 | |||
681 | if ( fds_enabled( this ) ) Fds_reset( &this->fds ); | ||
682 | if ( namco_enabled( this ) ) Namco_reset( &this->namco ); | ||
683 | if ( vrc6_enabled( this ) ) Vrc6_reset( &this->vrc6 ); | ||
684 | if ( fme7_enabled( this ) ) Fme7_reset( &this->fme7 ); | ||
685 | if ( mmc5_enabled( this ) ) Apu_reset( &this->mmc5.apu, false, 0 ); | ||
686 | #ifndef NSF_EMU_NO_VRC7 | ||
687 | if ( vrc7_enabled( this ) ) Vrc7_reset( &this->vrc7 ); | ||
688 | #endif | ||
689 | #endif | ||
690 | |||
691 | int speed_flags = 0; | ||
692 | #ifdef NSF_EMU_EXTRA_FLAGS | ||
693 | speed_flags = this->header.speed_flags; | ||
694 | #endif | ||
695 | |||
696 | Apu_reset( &this->apu, pal_only( &this->header ), (speed_flags & 0x20) ? 0x3F : 0 ); | ||
697 | Apu_write_register( &this->apu, 0, 0x4015, 0x0F ); | ||
698 | Apu_write_register( &this->apu, 0, 0x4017, (speed_flags & 0x10) ? 0x80 : 0 ); | ||
699 | |||
700 | memset( unmapped_code( this ), halt_opcode, unmapped_size ); | ||
701 | memset( this->low_ram, 0, low_ram_size ); | ||
702 | memset( sram( this ), 0, sram_size ); | ||
703 | |||
704 | map_memory( this ); | ||
705 | |||
706 | // Arrange time of first call to play routine | ||
707 | this->play_extra = 0; | ||
708 | this->next_play = this->play_period; | ||
709 | |||
710 | this->play_delay = initial_play_delay; | ||
711 | this->saved_state.pc = idle_addr; | ||
712 | |||
713 | // Setup for call to init routine | ||
714 | this->cpu.r.a = track; | ||
715 | this->cpu.r.x = pal_only( &this->header ); | ||
716 | this->cpu.r.sp = 0xFF; | ||
717 | jsr_then_stop( this, this->header.init_addr ); | ||
718 | /* if ( this->cpu.r.pc < get_addr( header.load_addr ) ) | ||
719 | warning( "Init address < load address" ); */ | ||
720 | |||
721 | // convert filter times to samples | ||
722 | struct setup_t s = this->tfilter; | ||
723 | s.max_initial *= this->sample_rate * stereo; | ||
724 | #ifdef GME_DISABLE_SILENCE_LOOKAHEAD | ||
725 | s.lookahead = 1; | ||
726 | #endif | ||
727 | track_setup( &this->track_filter, &s ); | ||
728 | |||
729 | return track_start( &this->track_filter ); | ||
730 | } | ||
731 | |||
732 | void run_once( struct Nsf_Emu* this, nes_time_t end ) | ||
733 | { | ||
734 | // Emulate until next play call if possible | ||
735 | if ( run_cpu_until( this, min( this->next_play, end ) ) ) | ||
736 | { | ||
737 | // Halt instruction encountered | ||
738 | |||
739 | if ( this->cpu.r.pc != idle_addr ) | ||
740 | { | ||
741 | // special_event( "illegal instruction" ); | ||
742 | Cpu_set_time( &this->cpu, this->cpu.end_time ); | ||
743 | return; | ||
744 | } | ||
745 | |||
746 | // Init/play routine returned | ||
747 | this->play_delay = 1; // play can now be called regularly | ||
748 | |||
749 | if ( this->saved_state.pc == idle_addr ) | ||
750 | { | ||
751 | // nothing to run | ||
752 | nes_time_t t = this->cpu.end_time; | ||
753 | if ( Cpu_time( &this->cpu ) < t ) | ||
754 | Cpu_set_time( &this->cpu, t ); | ||
755 | } | ||
756 | else | ||
757 | { | ||
758 | // continue init routine that was interrupted by play routine | ||
759 | this->cpu.r = this->saved_state; | ||
760 | this->saved_state.pc = idle_addr; | ||
761 | } | ||
762 | } | ||
763 | |||
764 | if ( Cpu_time( &this->cpu ) >= this->next_play ) | ||
765 | { | ||
766 | // Calculate time of next call to play routine | ||
767 | this->play_extra ^= 1; // extra clock every other call | ||
768 | this->next_play += this->play_period + this->play_extra; | ||
769 | |||
770 | // Call routine if ready | ||
771 | if ( this->play_delay && !--this->play_delay ) | ||
772 | { | ||
773 | // Save state if init routine is still running | ||
774 | if ( this->cpu.r.pc != idle_addr ) | ||
775 | { | ||
776 | check( this->saved_state.pc == idle_addr ); | ||
777 | this->saved_state = this->cpu.r; | ||
778 | // special_event( "play called during init" ); | ||
779 | } | ||
780 | |||
781 | jsr_then_stop( this, this->header.play_addr ); | ||
782 | } | ||
783 | } | ||
784 | } | ||
785 | |||
786 | void run_until( struct Nsf_Emu* this, nes_time_t end ) | ||
787 | { | ||
788 | while ( Cpu_time( &this->cpu ) < end ) | ||
789 | run_once( this, end ); | ||
790 | } | ||
791 | |||
792 | static void end_frame( struct Nsf_Emu* this, nes_time_t end ) | ||
793 | { | ||
794 | if ( Cpu_time( &this->cpu ) < end ) | ||
795 | run_until( this, end ); | ||
796 | Cpu_adjust_time( &this->cpu, -end ); | ||
797 | |||
798 | // Localize to new time frame | ||
799 | this->next_play -= end; | ||
800 | check( this->next_play >= 0 ); | ||
801 | if ( this->next_play < 0 ) | ||
802 | this->next_play = 0; | ||
803 | |||
804 | Apu_end_frame( &this->apu, end ); | ||
805 | |||
806 | #ifndef NSF_EMU_APU_ONLY | ||
807 | if ( fds_enabled( this ) ) Fds_end_frame( &this->fds, end ); | ||
808 | if ( fme7_enabled( this ) ) Fme7_end_frame( &this->fme7, end ); | ||
809 | if ( mmc5_enabled( this ) ) Apu_end_frame( &this->mmc5.apu, end ); | ||
810 | if ( namco_enabled( this ) ) Namco_end_frame( &this->namco, end ); | ||
811 | if ( vrc6_enabled( this ) ) Vrc6_end_frame( &this->vrc6, end ); | ||
812 | #ifndef NSF_EMU_NO_VRC7 | ||
813 | if ( vrc7_enabled( this ) ) Vrc7_end_frame( &this->vrc7, end ); | ||
814 | #endif | ||
815 | #endif | ||
816 | } | ||
817 | |||
818 | // Tell/Seek | ||
819 | |||
820 | static int msec_to_samples( int msec, int sample_rate ) | ||
821 | { | ||
822 | int sec = msec / 1000; | ||
823 | msec -= sec * 1000; | ||
824 | return (sec * sample_rate + msec * sample_rate / 1000) * stereo; | ||
825 | } | ||
826 | |||
827 | int Track_tell( struct Nsf_Emu* this ) | ||
828 | { | ||
829 | int rate = this->sample_rate * stereo; | ||
830 | int sec = track_sample_count( &this->track_filter ) / rate; | ||
831 | return sec * 1000 + (track_sample_count( &this->track_filter ) - sec * rate) * 1000 / rate; | ||
832 | } | ||
833 | |||
834 | blargg_err_t Track_seek( struct Nsf_Emu* this, int msec ) | ||
835 | { | ||
836 | int time = msec_to_samples( msec, this->sample_rate ); | ||
837 | if ( time < track_sample_count( &this->track_filter ) ) | ||
838 | RETURN_ERR( Nsf_start_track( this, this->current_track ) ); | ||
839 | return Track_skip( this, time - track_sample_count( &this->track_filter ) ); | ||
840 | } | ||
841 | |||
842 | blargg_err_t Track_skip( struct Nsf_Emu* this, int count ) | ||
843 | { | ||
844 | require( this->current_track >= 0 ); // start_track() must have been called already | ||
845 | return track_skip( &this->track_filter, count ); | ||
846 | } | ||
847 | |||
848 | blargg_err_t skip_( void *emu, int count ) | ||
849 | { | ||
850 | struct Nsf_Emu* this = (struct Nsf_Emu*) emu; | ||
851 | |||
852 | // for long skip, mute sound | ||
853 | const int threshold = 32768; | ||
854 | if ( count > threshold ) | ||
855 | { | ||
856 | int saved_mute = this->mute_mask_; | ||
857 | Sound_mute_voices( this, ~0 ); | ||
858 | |||
859 | int n = count - threshold/2; | ||
860 | n &= ~(2048-1); // round to multiple of 2048 | ||
861 | count -= n; | ||
862 | RETURN_ERR( skippy_( &this->track_filter, n ) ); | ||
863 | |||
864 | Sound_mute_voices( this, saved_mute ); | ||
865 | } | ||
866 | |||
867 | return skippy_( &this->track_filter, count ); | ||
868 | } | ||
869 | |||
870 | // Fading | ||
871 | |||
872 | void Track_set_fade( struct Nsf_Emu* this, int start_msec, int length_msec ) | ||
873 | { | ||
874 | track_set_fade( &this->track_filter, msec_to_samples( start_msec, this->sample_rate ), | ||
875 | length_msec * this->sample_rate / (1000 / stereo) ); | ||
876 | } | ||
877 | |||
878 | blargg_err_t Nsf_play( struct Nsf_Emu* this, int out_count, sample_t* out ) | ||
879 | { | ||
880 | require( this->current_track >= 0 ); | ||
881 | require( out_count % stereo == 0 ); | ||
882 | return track_play( &this->track_filter, out_count, out ); | ||
883 | } | ||
884 | |||
885 | blargg_err_t play_( void* emu, int count, sample_t* out ) | ||
886 | { | ||
887 | struct Nsf_Emu* this = (struct Nsf_Emu*) emu; | ||
888 | |||
889 | int remain = count; | ||
890 | while ( remain ) | ||
891 | { | ||
892 | Buffer_disable_immediate_removal( &this->stereo_buf ); | ||
893 | remain -= Buffer_read_samples( &this->stereo_buf, &out [count - remain], remain ); | ||
894 | if ( remain ) | ||
895 | { | ||
896 | if ( this->buf_changed_count != Buffer_channels_changed_count( &this->stereo_buf ) ) | ||
897 | { | ||
898 | this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf ); | ||
899 | |||
900 | // Remute voices | ||
901 | Sound_mute_voices( this, this->mute_mask_ ); | ||
902 | } | ||
903 | int msec = Buffer_length( &this->stereo_buf ); | ||
904 | blip_time_t clocks_emulated = msec * this->clock_rate__ / 1000 - 100; | ||
905 | RETURN_ERR( run_clocks( this, &clocks_emulated, msec ) ); | ||
906 | assert( clocks_emulated ); | ||
907 | Buffer_end_frame( &this->stereo_buf, clocks_emulated ); | ||
908 | } | ||
909 | } | ||
910 | return 0; | ||
911 | } | ||
912 | |||
913 | blargg_err_t run_clocks( struct Nsf_Emu* this, blip_time_t* duration, int msec ) | ||
914 | { | ||
915 | #if defined(ROCKBOX) | ||
916 | (void) msec; | ||
917 | #endif | ||
918 | |||
919 | end_frame( this, *duration ); | ||
920 | return 0; | ||
921 | } | ||