summaryrefslogtreecommitdiff
path: root/lib/rbcodec/codecs/libgme/nsf_emu.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rbcodec/codecs/libgme/nsf_emu.c')
-rw-r--r--lib/rbcodec/codecs/libgme/nsf_emu.c921
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
9can redistribute it and/or modify it under the terms of the GNU Lesser
10General Public License as published by the Free Software Foundation; either
11version 2.1 of the License, or (at your option) any later version. This
12module is distributed in the hope that it will be useful, but WITHOUT ANY
13WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
15details. You should have received a copy of the GNU Lesser General Public
16License along with this module; if not, write to the Free Software Foundation,
17Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
18
19#include "blargg_source.h"
20
21const char gme_wrong_file_type [] = "Wrong file type for this emulator";
22
23// number of frames until play interrupts init
24int const initial_play_delay = 7; // KikiKaikai needed this to work
25int const bank_size = 0x1000;
26int const rom_addr = 0x8000;
27
28static void clear_track_vars( struct Nsf_Emu* this )
29{
30 this->current_track = -1;
31 track_stop( &this->track_filter );
32}
33
34static int pcm_read( void* emu, int addr )
35{
36 return *Cpu_get_code( &((struct Nsf_Emu*) emu)->cpu, addr );
37}
38
39void 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
74static 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
84static 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
197static 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
203static bool pal_only( struct header_t* this )
204{
205 return (this->speed_flags & 3) == 1;
206}
207
208static int clock_rate( struct header_t* this )
209{
210 return pal_only( this ) ? (int)1662607.125 : (int)1789772.727272727;
211}
212
213static 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.
241addr_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
249static 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
256blargg_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
279blargg_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
318void 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
324void Nsf_clear_playlist( struct Nsf_Emu* this )
325{
326 Nsf_disable_playlist( this, true );
327}
328
329void 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
357static 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
397static 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
458blargg_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
473void 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
483void 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
505void 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
524static 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.
531static 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
538int 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
564void 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
659blargg_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
732void 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
786void 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
792static 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
820static 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
827int 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
834blargg_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
842blargg_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
848blargg_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
872void 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
878blargg_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
885blargg_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
913blargg_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}