diff options
Diffstat (limited to 'apps/codecs/libgme/kss_emu.c')
-rw-r--r-- | apps/codecs/libgme/kss_emu.c | 883 |
1 files changed, 883 insertions, 0 deletions
diff --git a/apps/codecs/libgme/kss_emu.c b/apps/codecs/libgme/kss_emu.c new file mode 100644 index 0000000000..b01034234a --- /dev/null +++ b/apps/codecs/libgme/kss_emu.c | |||
@@ -0,0 +1,883 @@ | |||
1 | // Game_Music_Emu 0.5.5. http://www.slack.net/~ant/ | ||
2 | |||
3 | #include "kss_emu.h" | ||
4 | |||
5 | #include "blargg_endian.h" | ||
6 | |||
7 | /* Copyright (C) 2006 Shay Green. This module is free software; you | ||
8 | can redistribute it and/or modify it under the terms of the GNU Lesser | ||
9 | General Public License as published by the Free Software Foundation; either | ||
10 | version 2.1 of the License, or (at your option) any later version. This | ||
11 | module is distributed in the hope that it will be useful, but WITHOUT ANY | ||
12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | ||
13 | FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more | ||
14 | details. You should have received a copy of the GNU Lesser General Public | ||
15 | License along with this module; if not, write to the Free Software Foundation, | ||
16 | Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ | ||
17 | |||
18 | #include "blargg_source.h" | ||
19 | |||
20 | long const clock_rate = 3579545; | ||
21 | |||
22 | const char gme_wrong_file_type [] = "Wrong file type for this emulator"; | ||
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 | void clear_track_vars( struct Kss_Emu* this ) | ||
31 | { | ||
32 | this->current_track = -1; | ||
33 | this->out_time = 0; | ||
34 | this->emu_time = 0; | ||
35 | this->emu_track_ended_ = true; | ||
36 | this->track_ended = true; | ||
37 | this->fade_start = INT_MAX / 2 + 1; | ||
38 | this->fade_step = 1; | ||
39 | this->silence_time = 0; | ||
40 | this->silence_count = 0; | ||
41 | this->buf_remain = 0; | ||
42 | // warning(); // clear warning | ||
43 | } | ||
44 | |||
45 | static blargg_err_t init_opl_apu( enum opl_type_t type, struct Opl_Apu* out ) | ||
46 | { | ||
47 | blip_time_t const period = 72; | ||
48 | int const rate = clock_rate / period; | ||
49 | return Opl_init( out, rate * period, rate, period, type ); | ||
50 | } | ||
51 | |||
52 | void Kss_init( struct Kss_Emu* this ) | ||
53 | { | ||
54 | this->sample_rate = 0; | ||
55 | this->mute_mask_ = 0; | ||
56 | this->tempo = 1.0; | ||
57 | this->gain = 1.0; | ||
58 | this->chip_flags = 0; | ||
59 | |||
60 | // defaults | ||
61 | this->max_initial_silence = 2; | ||
62 | this->silence_lookahead = 6; | ||
63 | this->ignore_silence = false; | ||
64 | |||
65 | this->voice_count = 0; | ||
66 | clear_track_vars( this ); | ||
67 | |||
68 | memset( this->unmapped_read, 0xFF, sizeof this->unmapped_read ); | ||
69 | |||
70 | // Init all stuff | ||
71 | Buffer_init( &this->stereo_buffer ); | ||
72 | |||
73 | Z80_init( &this->cpu ); | ||
74 | Rom_init( &this->rom, page_size ); | ||
75 | |||
76 | // Initialize all apus just once (?) | ||
77 | Sms_apu_init( &this->sms.psg); | ||
78 | Ay_apu_init( &this->msx.psg ); | ||
79 | Scc_init( &this->msx.scc ); | ||
80 | |||
81 | #ifndef KSS_EMU_NO_FMOPL | ||
82 | init_opl_apu( type_smsfmunit, &this->sms.fm ); | ||
83 | init_opl_apu( type_msxmusic, &this->msx.music ); | ||
84 | init_opl_apu( type_msxaudio, &this->msx.audio ); | ||
85 | #endif | ||
86 | } | ||
87 | |||
88 | // Track info | ||
89 | |||
90 | static blargg_err_t check_kss_header( void const* header ) | ||
91 | { | ||
92 | if ( memcmp( header, "KSCC", 4 ) && memcmp( header, "KSSX", 4 ) ) | ||
93 | return gme_wrong_file_type; | ||
94 | return 0; | ||
95 | } | ||
96 | |||
97 | // Setup | ||
98 | |||
99 | void update_gain( struct Kss_Emu* this ) | ||
100 | { | ||
101 | double g = this->gain; | ||
102 | if ( msx_music_enabled( this ) || msx_audio_enabled( this ) | ||
103 | || sms_fm_enabled( this ) ) | ||
104 | { | ||
105 | g *= 0.75; | ||
106 | } | ||
107 | else | ||
108 | { | ||
109 | if ( this->scc_accessed ) | ||
110 | g *= 1.2; | ||
111 | } | ||
112 | |||
113 | if ( sms_psg_enabled( this ) ) Sms_apu_volume( &this->sms.psg, g ); | ||
114 | if ( sms_fm_enabled( this ) ) Opl_volume( &this->sms.fm, g ); | ||
115 | if ( msx_psg_enabled( this ) ) Ay_apu_volume( &this->msx.psg, g ); | ||
116 | if ( msx_scc_enabled( this ) ) Scc_volume( &this->msx.scc, g ); | ||
117 | if ( msx_music_enabled( this ) ) Opl_volume( &this->msx.music, g ); | ||
118 | if ( msx_audio_enabled( this ) ) Opl_volume( &this->msx.audio, g ); | ||
119 | } | ||
120 | |||
121 | blargg_err_t Kss_load_mem( struct Kss_Emu* this, const void* data, long size ) | ||
122 | { | ||
123 | /* warning( core.warning() ); */ | ||
124 | memset( &this->header, 0, sizeof this->header ); | ||
125 | assert( offsetof (header_t,msx_audio_vol) == header_size - 1 ); | ||
126 | RETURN_ERR( Rom_load( &this->rom, data, size, header_base_size, &this->header, 0 ) ); | ||
127 | |||
128 | RETURN_ERR( check_kss_header( this->header.tag ) ); | ||
129 | |||
130 | this->chip_flags = 0; | ||
131 | this->header.last_track [0] = 255; | ||
132 | if ( this->header.tag [3] == 'C' ) | ||
133 | { | ||
134 | if ( this->header.extra_header ) | ||
135 | { | ||
136 | this->header.extra_header = 0; | ||
137 | /* warning( "Unknown data in header" ); */ | ||
138 | } | ||
139 | if ( this->header.device_flags & ~0x0F ) | ||
140 | { | ||
141 | this->header.device_flags &= 0x0F; | ||
142 | /* warning( "Unknown data in header" ); */ | ||
143 | } | ||
144 | } | ||
145 | else if ( this->header.extra_header ) | ||
146 | { | ||
147 | if ( this->header.extra_header != header_ext_size ) | ||
148 | { | ||
149 | this->header.extra_header = 0; | ||
150 | /* warning( "Invalid extra_header_size" ); */ | ||
151 | } | ||
152 | else | ||
153 | { | ||
154 | memcpy( this->header.data_size, this->rom.file_data, header_ext_size ); | ||
155 | } | ||
156 | } | ||
157 | |||
158 | #ifndef NDEBUG | ||
159 | { | ||
160 | int ram_mode = this->header.device_flags & 0x84; // MSX | ||
161 | if ( this->header.device_flags & 0x02 ) // SMS | ||
162 | ram_mode = (this->header.device_flags & 0x88); | ||
163 | |||
164 | if ( ram_mode ) | ||
165 | blargg_dprintf_( "RAM not supported\n" ); // TODO: support | ||
166 | } | ||
167 | #endif | ||
168 | |||
169 | this->track_count = get_le16( this->header.last_track ) + 1; | ||
170 | this->m3u.size = 0; | ||
171 | |||
172 | this->scc_enabled = false; | ||
173 | if ( this->header.device_flags & 0x02 ) // Sega Master System | ||
174 | { | ||
175 | int const osc_count = sms_osc_count + opl_osc_count; | ||
176 | |||
177 | // sms.psg | ||
178 | this->voice_count = sms_osc_count; | ||
179 | this->chip_flags |= sms_psg_flag; | ||
180 | |||
181 | // sms.fm | ||
182 | if ( this->header.device_flags & 0x01 ) | ||
183 | { | ||
184 | this->voice_count = osc_count; | ||
185 | this->chip_flags |= sms_fm_flag; | ||
186 | } | ||
187 | } | ||
188 | else // MSX | ||
189 | { | ||
190 | int const osc_count = ay_osc_count + opl_osc_count; | ||
191 | |||
192 | // msx.psg | ||
193 | this->voice_count = ay_osc_count; | ||
194 | this->chip_flags |= msx_psg_flag; | ||
195 | |||
196 | /* if ( this->header.device_flags & 0x10 ) | ||
197 | warning( "MSX stereo not supported" ); */ | ||
198 | |||
199 | // msx.music | ||
200 | if ( this->header.device_flags & 0x01 ) | ||
201 | { | ||
202 | this->voice_count = osc_count; | ||
203 | this->chip_flags |= msx_music_flag; | ||
204 | } | ||
205 | |||
206 | #ifndef KSS_EMU_NO_FMOPL | ||
207 | // msx.audio | ||
208 | if ( this->header.device_flags & 0x08 ) | ||
209 | { | ||
210 | this->voice_count = osc_count; | ||
211 | this->chip_flags |= msx_audio_flag; | ||
212 | } | ||
213 | #endif | ||
214 | |||
215 | if ( !(this->header.device_flags & 0x80) ) | ||
216 | { | ||
217 | if ( !(this->header.device_flags & 0x84) ) | ||
218 | this->scc_enabled = scc_enabled_true; | ||
219 | |||
220 | // msx.scc | ||
221 | this->chip_flags |= msx_scc_flag; | ||
222 | this->voice_count = ay_osc_count + scc_osc_count; | ||
223 | } | ||
224 | } | ||
225 | |||
226 | this->silence_lookahead = 6; | ||
227 | if ( sms_fm_enabled( this ) || msx_music_enabled( this ) || msx_audio_enabled( this ) ) | ||
228 | { | ||
229 | if ( !Opl_supported() ) | ||
230 | ; /* warning( "FM sound not supported" ); */ | ||
231 | else | ||
232 | this->silence_lookahead = 3; // Opl_Apu is really slow | ||
233 | } | ||
234 | |||
235 | this->clock_rate_ = clock_rate; | ||
236 | Buffer_clock_rate( &this->stereo_buffer, clock_rate ); | ||
237 | this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buffer ); | ||
238 | |||
239 | Sound_set_tempo( this, this->tempo ); | ||
240 | Sound_mute_voices( this, this->mute_mask_ ); | ||
241 | return 0; | ||
242 | } | ||
243 | |||
244 | void set_voice( struct Kss_Emu* this, int i, struct Blip_Buffer* center, struct Blip_Buffer* left, struct Blip_Buffer* right ) | ||
245 | { | ||
246 | if ( sms_psg_enabled( this ) ) // Sega Master System | ||
247 | { | ||
248 | i -= sms_osc_count; | ||
249 | if ( i < 0 ) | ||
250 | { | ||
251 | Sms_apu_set_output( &this->sms.psg, i + sms_osc_count, center, left, right ); | ||
252 | return; | ||
253 | } | ||
254 | |||
255 | if ( sms_fm_enabled( this ) && i < opl_osc_count ) | ||
256 | Opl_set_output( &this->sms.fm, center ); | ||
257 | } | ||
258 | else if ( msx_psg_enabled( this ) ) // MSX | ||
259 | { | ||
260 | i -= ay_osc_count; | ||
261 | if ( i < 0 ) | ||
262 | { | ||
263 | Ay_apu_set_output( &this->msx.psg, i + ay_osc_count, center ); | ||
264 | return; | ||
265 | } | ||
266 | |||
267 | if ( msx_scc_enabled( this ) && i < scc_osc_count ) Scc_set_output( &this->msx.scc, i, center ); | ||
268 | if ( msx_music_enabled( this ) && i < opl_osc_count ) Opl_set_output( &this->msx.music, center ); | ||
269 | if ( msx_audio_enabled( this ) && i < opl_osc_count ) Opl_set_output( &this->msx.audio, center ); | ||
270 | } | ||
271 | } | ||
272 | |||
273 | // Emulation | ||
274 | |||
275 | void jsr( struct Kss_Emu* this, byte const addr [] ) | ||
276 | { | ||
277 | this->ram [--this->cpu.r.sp] = idle_addr >> 8; | ||
278 | this->ram [--this->cpu.r.sp] = idle_addr & 0xFF; | ||
279 | this->cpu.r.pc = get_le16( addr ); | ||
280 | } | ||
281 | |||
282 | void set_bank( struct Kss_Emu* this, int logical, int physical ) | ||
283 | { | ||
284 | int const bank_size = (16 * 1024L) >> (this->header.bank_mode >> 7 & 1); | ||
285 | |||
286 | int addr = 0x8000; | ||
287 | if ( logical && bank_size == 8 * 1024 ) | ||
288 | addr = 0xA000; | ||
289 | |||
290 | physical -= this->header.first_bank; | ||
291 | if ( (unsigned) physical >= (unsigned) this->bank_count ) | ||
292 | { | ||
293 | byte* data = this->ram + addr; | ||
294 | Z80_map_mem( &this->cpu, addr, bank_size, data, data ); | ||
295 | } | ||
296 | else | ||
297 | { | ||
298 | int offset, phys = physical * bank_size; | ||
299 | for ( offset = 0; offset < bank_size; offset += page_size ) | ||
300 | Z80_map_mem( &this->cpu, addr + offset, page_size, | ||
301 | this->unmapped_write, Rom_at_addr( &this->rom, phys + offset ) ); | ||
302 | |||
303 | } | ||
304 | } | ||
305 | |||
306 | void cpu_write( struct Kss_Emu* this, addr_t addr, int data ) | ||
307 | { | ||
308 | *Z80_write( &this->cpu, addr ) = data; | ||
309 | if ( (addr & this->scc_enabled) == 0x8000 ) { | ||
310 | // TODO: SCC+ support | ||
311 | |||
312 | data &= 0xFF; | ||
313 | switch ( addr ) | ||
314 | { | ||
315 | case 0x9000: | ||
316 | set_bank( this, 0, data ); | ||
317 | return; | ||
318 | |||
319 | case 0xB000: | ||
320 | set_bank( this, 1, data ); | ||
321 | return; | ||
322 | |||
323 | case 0xBFFE: // selects between mapping areas (we just always enable both) | ||
324 | if ( data == 0 || data == 0x20 ) | ||
325 | return; | ||
326 | } | ||
327 | |||
328 | int scc_addr = (addr & 0xDFFF) - 0x9800; | ||
329 | if ( msx_scc_enabled( this ) && (unsigned) scc_addr < 0xB0 ) | ||
330 | { | ||
331 | this->scc_accessed = true; | ||
332 | //if ( (unsigned) (scc_addr - 0x90) < 0x10 ) | ||
333 | // scc_addr -= 0x10; // 0x90-0x9F mirrors to 0x80-0x8F | ||
334 | if ( scc_addr < scc_reg_count ) | ||
335 | Scc_write( &this->msx.scc, Z80_time( &this->cpu ), addr, data ); | ||
336 | return; | ||
337 | } | ||
338 | } | ||
339 | } | ||
340 | |||
341 | void cpu_out( struct Kss_Emu* this, kss_time_t time, kss_addr_t addr, int data ) | ||
342 | { | ||
343 | data &= 0xFF; | ||
344 | switch ( addr & 0xFF ) | ||
345 | { | ||
346 | case 0xA0: | ||
347 | if ( msx_psg_enabled( this ) ) | ||
348 | Ay_apu_write_addr( &this->msx.psg, data ); | ||
349 | return; | ||
350 | |||
351 | case 0xA1: | ||
352 | if ( msx_psg_enabled( this ) ) | ||
353 | Ay_apu_write_data( &this->msx.psg, time, data ); | ||
354 | return; | ||
355 | |||
356 | case 0x06: | ||
357 | if ( sms_psg_enabled( this ) && (this->header.device_flags & 0x04) ) | ||
358 | { | ||
359 | Sms_apu_write_ggstereo( &this->sms.psg, time, data ); | ||
360 | return; | ||
361 | } | ||
362 | break; | ||
363 | |||
364 | case 0x7E: | ||
365 | case 0x7F: | ||
366 | if ( sms_psg_enabled( this ) ) | ||
367 | { | ||
368 | Sms_apu_write_data( &this->sms.psg, time, data ); | ||
369 | return; | ||
370 | } | ||
371 | break; | ||
372 | |||
373 | #define OPL_WRITE_HANDLER( base, name, opl )\ | ||
374 | case base : if ( name##_enabled( this ) ) { Opl_write_addr( opl, data ); return; } break;\ | ||
375 | case base+1: if ( name##_enabled( this ) ) { Opl_write_data( opl, time, data ); return; } break; | ||
376 | |||
377 | OPL_WRITE_HANDLER( 0x7C, msx_music, &this->msx.music ) | ||
378 | OPL_WRITE_HANDLER( 0xC0, msx_audio, &this->msx.audio ) | ||
379 | OPL_WRITE_HANDLER( 0xF0, sms_fm, &this->sms.fm ) | ||
380 | |||
381 | case 0xFE: | ||
382 | set_bank( this, 0, data ); | ||
383 | return; | ||
384 | |||
385 | #ifndef NDEBUG | ||
386 | case 0xA8: // PPI | ||
387 | return; | ||
388 | #endif | ||
389 | } | ||
390 | |||
391 | /* cpu_out( time, addr, data ); */ | ||
392 | } | ||
393 | |||
394 | int cpu_in( struct Kss_Emu* this, kss_time_t time, kss_addr_t addr ) | ||
395 | { | ||
396 | switch ( addr & 0xFF ) | ||
397 | { | ||
398 | case 0xC0: | ||
399 | case 0xC1: | ||
400 | if ( msx_audio_enabled( this ) ) | ||
401 | return Opl_read( &this->msx.audio, time, addr & 1 ); | ||
402 | break; | ||
403 | |||
404 | case 0xA2: | ||
405 | if ( msx_psg_enabled( this ) ) | ||
406 | return Ay_apu_read( &this->msx.psg ); | ||
407 | break; | ||
408 | |||
409 | #ifndef NDEBUG | ||
410 | case 0xA8: // PPI | ||
411 | return 0; | ||
412 | #endif | ||
413 | } | ||
414 | |||
415 | /* return cpu_in( time, addr ); */ | ||
416 | return 0xFF; | ||
417 | } | ||
418 | |||
419 | blargg_err_t run_clocks( struct Kss_Emu* this, blip_time_t* duration_ ) | ||
420 | { | ||
421 | blip_time_t duration = *duration_; | ||
422 | RETURN_ERR( end_frame( this, duration ) ); | ||
423 | |||
424 | if ( sms_psg_enabled( this ) ) Sms_apu_end_frame( &this->sms.psg, duration ); | ||
425 | if ( sms_fm_enabled( this ) ) Opl_end_frame( &this->sms.fm, duration ); | ||
426 | if ( msx_psg_enabled( this ) ) Ay_apu_end_frame( &this->msx.psg, duration ); | ||
427 | if ( msx_scc_enabled( this ) ) Scc_end_frame( &this->msx.scc, duration ); | ||
428 | if ( msx_music_enabled( this ) ) Opl_end_frame( &this->msx.music, duration ); | ||
429 | if ( msx_audio_enabled( this ) ) Opl_end_frame( &this->msx.audio, duration ); | ||
430 | |||
431 | return 0; | ||
432 | } | ||
433 | |||
434 | blargg_err_t end_frame( struct Kss_Emu* this, kss_time_t end ) | ||
435 | { | ||
436 | while ( Z80_time( &this->cpu ) < end ) | ||
437 | { | ||
438 | kss_time_t next = min( end, this->next_play ); | ||
439 | run_cpu( this, next ); | ||
440 | if ( this->cpu.r.pc == idle_addr ) | ||
441 | Z80_set_time( &this->cpu, next ); | ||
442 | |||
443 | if ( Z80_time( &this->cpu ) >= this->next_play ) | ||
444 | { | ||
445 | this->next_play += this->play_period; | ||
446 | if ( this->cpu.r.pc == idle_addr ) | ||
447 | { | ||
448 | if ( !this->gain_updated ) | ||
449 | { | ||
450 | this->gain_updated = true; | ||
451 | update_gain( this ); | ||
452 | } | ||
453 | |||
454 | jsr( this, this->header.play_addr ); | ||
455 | } | ||
456 | } | ||
457 | } | ||
458 | |||
459 | this->next_play -= end; | ||
460 | check( this->next_play >= 0 ); | ||
461 | Z80_adjust_time( &this->cpu, -end ); | ||
462 | |||
463 | return 0; | ||
464 | } | ||
465 | |||
466 | // MUSIC | ||
467 | |||
468 | |||
469 | blargg_err_t Kss_set_sample_rate( struct Kss_Emu* this, long rate ) | ||
470 | { | ||
471 | require( !this->sample_rate ); // sample rate can't be changed once set | ||
472 | RETURN_ERR( Buffer_set_sample_rate( &this->stereo_buffer, rate, 1000 / 20 ) ); | ||
473 | |||
474 | // Set bass frequency | ||
475 | Buffer_bass_freq( &this->stereo_buffer, 180 ); | ||
476 | this->sample_rate = rate; | ||
477 | return 0; | ||
478 | } | ||
479 | |||
480 | void Sound_mute_voice( struct Kss_Emu* this, int index, bool mute ) | ||
481 | { | ||
482 | require( (unsigned) index < (unsigned) this->voice_count ); | ||
483 | int bit = 1 << index; | ||
484 | int mask = this->mute_mask_ | bit; | ||
485 | if ( !mute ) | ||
486 | mask ^= bit; | ||
487 | Sound_mute_voices( this, mask ); | ||
488 | } | ||
489 | |||
490 | void Sound_mute_voices( struct Kss_Emu* this, int mask ) | ||
491 | { | ||
492 | require( this->sample_rate ); // sample rate must be set first | ||
493 | this->mute_mask_ = mask; | ||
494 | |||
495 | int i; | ||
496 | for ( i = this->voice_count; i--; ) | ||
497 | { | ||
498 | if ( mask & (1 << i) ) | ||
499 | { | ||
500 | set_voice( this, i, 0, 0, 0 ); | ||
501 | } | ||
502 | else | ||
503 | { | ||
504 | struct channel_t ch = Buffer_channel( &this->stereo_buffer ); | ||
505 | assert( (ch.center && ch.left && ch.right) || | ||
506 | (!ch.center && !ch.left && !ch.right) ); // all or nothing | ||
507 | set_voice( this, i, ch.center, ch.left, ch.right ); | ||
508 | } | ||
509 | } | ||
510 | } | ||
511 | |||
512 | void Sound_set_tempo( struct Kss_Emu* this, double t ) | ||
513 | { | ||
514 | require( this->sample_rate ); // sample rate must be set first | ||
515 | double const min = 0.02; | ||
516 | double const max = 4.00; | ||
517 | if ( t < min ) t = min; | ||
518 | if ( t > max ) t = max; | ||
519 | this->tempo = t; | ||
520 | |||
521 | blip_time_t period = | ||
522 | (this->header.device_flags & 0x40 ? clock_rate / 50 : clock_rate / 60); | ||
523 | this->play_period = (blip_time_t) (period / t); | ||
524 | } | ||
525 | |||
526 | void fill_buf( struct Kss_Emu* this ); | ||
527 | blargg_err_t Kss_start_track( struct Kss_Emu* this, int track ) | ||
528 | { | ||
529 | clear_track_vars( this ); | ||
530 | |||
531 | // Remap track if playlist available | ||
532 | if ( this->m3u.size > 0 ) { | ||
533 | struct entry_t* e = &this->m3u.entries[track]; | ||
534 | track = e->track; | ||
535 | } | ||
536 | |||
537 | this->current_track = track; | ||
538 | |||
539 | Buffer_clear( &this->stereo_buffer ); | ||
540 | |||
541 | if ( sms_psg_enabled( this ) ) Sms_apu_reset( &this->sms.psg, 0, 0 ); | ||
542 | if ( sms_fm_enabled( this ) ) Opl_reset( &this->sms.fm ); | ||
543 | if ( msx_psg_enabled( this ) ) Ay_apu_reset( &this->msx.psg ); | ||
544 | if ( msx_scc_enabled( this ) ) Scc_reset( &this->msx.scc ); | ||
545 | if ( msx_music_enabled( this ) ) Opl_reset( &this->msx.music ); | ||
546 | if ( msx_audio_enabled( this ) ) Opl_reset( &this->msx.audio ); | ||
547 | |||
548 | this->scc_accessed = false; | ||
549 | update_gain( this ); | ||
550 | |||
551 | memset( this->ram, 0xC9, 0x4000 ); | ||
552 | memset( this->ram + 0x4000, 0, sizeof this->ram - 0x4000 ); | ||
553 | |||
554 | // copy driver code to lo RAM | ||
555 | static byte const bios [] = { | ||
556 | 0xD3, 0xA0, 0xF5, 0x7B, 0xD3, 0xA1, 0xF1, 0xC9, // $0001: WRTPSG | ||
557 | 0xD3, 0xA0, 0xDB, 0xA2, 0xC9 // $0009: RDPSG | ||
558 | }; | ||
559 | static byte const vectors [] = { | ||
560 | 0xC3, 0x01, 0x00, // $0093: WRTPSG vector | ||
561 | 0xC3, 0x09, 0x00, // $0096: RDPSG vector | ||
562 | }; | ||
563 | memcpy( this->ram + 0x01, bios, sizeof bios ); | ||
564 | memcpy( this->ram + 0x93, vectors, sizeof vectors ); | ||
565 | |||
566 | // copy non-banked data into RAM | ||
567 | int load_addr = get_le16( this->header.load_addr ); | ||
568 | int orig_load_size = get_le16( this->header.load_size ); | ||
569 | int load_size = min( orig_load_size, (int) this->rom.file_size ); | ||
570 | load_size = min( load_size, (int) mem_size - load_addr ); | ||
571 | /* if ( load_size != orig_load_size ) | ||
572 | warning( "Excessive data size" ); */ | ||
573 | memcpy( this->ram + load_addr, this->rom.file_data + this->header.extra_header, load_size ); | ||
574 | |||
575 | Rom_set_addr( &this->rom, -load_size - this->header.extra_header ); | ||
576 | |||
577 | // check available bank data | ||
578 | int const bank_size = (16 * 1024L) >> (this->header.bank_mode >> 7 & 1); | ||
579 | int max_banks = (this->rom.file_size - load_size + bank_size - 1) / bank_size; | ||
580 | this->bank_count = this->header.bank_mode & 0x7F; | ||
581 | if ( this->bank_count > max_banks ) | ||
582 | { | ||
583 | this->bank_count = max_banks; | ||
584 | /* warning( "Bank data missing" ); */ | ||
585 | } | ||
586 | //dprintf( "load_size : $%X\n", load_size ); | ||
587 | //dprintf( "bank_size : $%X\n", bank_size ); | ||
588 | //dprintf( "bank_count: %d (%d claimed)\n", bank_count, this->header.bank_mode & 0x7F ); | ||
589 | |||
590 | this->ram [idle_addr] = 0xFF; | ||
591 | Z80_reset( &this->cpu, this->unmapped_write, this->unmapped_read ); | ||
592 | Z80_map_mem( &this->cpu, 0, mem_size, this->ram, this->ram ); | ||
593 | |||
594 | this->cpu.r.sp = 0xF380; | ||
595 | this->cpu.r.b.a = track; | ||
596 | this->cpu.r.b.h = 0; | ||
597 | this->next_play = this->play_period; | ||
598 | this->gain_updated = false; | ||
599 | jsr( this, this->header.init_addr ); | ||
600 | |||
601 | this->emu_track_ended_ = false; | ||
602 | this->track_ended = false; | ||
603 | |||
604 | if ( !this->ignore_silence ) | ||
605 | { | ||
606 | // play until non-silence or end of track | ||
607 | long end; | ||
608 | for ( end = this->max_initial_silence * stereo * this->sample_rate; this->emu_time < end; ) | ||
609 | { | ||
610 | fill_buf( this ); | ||
611 | if ( this->buf_remain | (int) this->emu_track_ended_ ) | ||
612 | break; | ||
613 | } | ||
614 | |||
615 | this->emu_time = this->buf_remain; | ||
616 | this->out_time = 0; | ||
617 | this->silence_time = 0; | ||
618 | this->silence_count = 0; | ||
619 | } | ||
620 | /* return track_ended() ? warning() : 0; */ | ||
621 | return 0; | ||
622 | } | ||
623 | |||
624 | // Tell/Seek | ||
625 | |||
626 | blargg_long msec_to_samples( blargg_long msec, long sample_rate ) | ||
627 | { | ||
628 | blargg_long sec = msec / 1000; | ||
629 | msec -= sec * 1000; | ||
630 | return (sec * sample_rate + msec * sample_rate / 1000) * stereo; | ||
631 | } | ||
632 | |||
633 | long Track_tell( struct Kss_Emu* this ) | ||
634 | { | ||
635 | blargg_long rate = this->sample_rate * stereo; | ||
636 | blargg_long sec = this->out_time / rate; | ||
637 | return sec * 1000 + (this->out_time - sec * rate) * 1000 / rate; | ||
638 | } | ||
639 | |||
640 | blargg_err_t Track_seek( struct Kss_Emu* this, long msec ) | ||
641 | { | ||
642 | blargg_long time = msec_to_samples( msec, this->sample_rate ); | ||
643 | if ( time < this->out_time ) | ||
644 | RETURN_ERR( Kss_start_track( this, this->current_track ) ); | ||
645 | return Track_skip( this, time - this->out_time ); | ||
646 | } | ||
647 | |||
648 | blargg_err_t play_( struct Kss_Emu* this, long count, sample_t* out ); | ||
649 | blargg_err_t skip_( struct Kss_Emu* this, long count ) | ||
650 | { | ||
651 | // for long skip, mute sound | ||
652 | const long threshold = 30000; | ||
653 | if ( count > threshold ) | ||
654 | { | ||
655 | int saved_mute = this->mute_mask_; | ||
656 | Sound_mute_voices( this, ~0 ); | ||
657 | |||
658 | while ( count > threshold / 2 && !this->emu_track_ended_ ) | ||
659 | { | ||
660 | RETURN_ERR( play_( this, buf_size, this->buf ) ); | ||
661 | count -= buf_size; | ||
662 | } | ||
663 | |||
664 | Sound_mute_voices( this, saved_mute ); | ||
665 | } | ||
666 | |||
667 | while ( count && !this->emu_track_ended_ ) | ||
668 | { | ||
669 | long n = buf_size; | ||
670 | if ( n > count ) | ||
671 | n = count; | ||
672 | count -= n; | ||
673 | RETURN_ERR( play_( this, n, this->buf ) ); | ||
674 | } | ||
675 | return 0; | ||
676 | } | ||
677 | |||
678 | blargg_err_t Track_skip( struct Kss_Emu* this, long count ) | ||
679 | { | ||
680 | require( this->current_track >= 0 ); // start_track() must have been called already | ||
681 | this->out_time += count; | ||
682 | |||
683 | // remove from silence and buf first | ||
684 | { | ||
685 | long n = min( count, this->silence_count ); | ||
686 | this->silence_count -= n; | ||
687 | count -= n; | ||
688 | |||
689 | n = min( count, this->buf_remain ); | ||
690 | this->buf_remain -= n; | ||
691 | count -= n; | ||
692 | } | ||
693 | |||
694 | if ( count && !this->emu_track_ended_ ) | ||
695 | { | ||
696 | this->emu_time += count; | ||
697 | if ( skip_( this, count ) ) | ||
698 | this->emu_track_ended_ = true; | ||
699 | } | ||
700 | |||
701 | if ( !(this->silence_count | this->buf_remain) ) // caught up to emulator, so update track ended | ||
702 | this->track_ended |= this->emu_track_ended_; | ||
703 | |||
704 | return 0; | ||
705 | } | ||
706 | |||
707 | // Fading | ||
708 | |||
709 | void Track_set_fade( struct Kss_Emu* this, long start_msec, long length_msec ) | ||
710 | { | ||
711 | this->fade_step = this->sample_rate * length_msec / (fade_block_size * fade_shift * 1000 / stereo); | ||
712 | this->fade_start = msec_to_samples( start_msec, this->sample_rate ); | ||
713 | } | ||
714 | |||
715 | // unit / pow( 2.0, (double) x / step ) | ||
716 | static int int_log( blargg_long x, int step, int unit ) | ||
717 | { | ||
718 | int shift = x / step; | ||
719 | int fraction = (x - shift * step) * unit / step; | ||
720 | return ((unit - fraction) + (fraction >> 1)) >> shift; | ||
721 | } | ||
722 | |||
723 | void handle_fade( struct Kss_Emu *this, long out_count, sample_t* out ) | ||
724 | { | ||
725 | int i; | ||
726 | for ( i = 0; i < out_count; i += fade_block_size ) | ||
727 | { | ||
728 | int const shift = 14; | ||
729 | int const unit = 1 << shift; | ||
730 | int gain = int_log( (this->out_time + i - this->fade_start) / fade_block_size, | ||
731 | this->fade_step, unit ); | ||
732 | if ( gain < (unit >> fade_shift) ) | ||
733 | this->track_ended = this->emu_track_ended_ = true; | ||
734 | |||
735 | sample_t* io = &out [i]; | ||
736 | int count; | ||
737 | for ( count = min( fade_block_size, out_count - i ); count; --count ) | ||
738 | { | ||
739 | *io = (sample_t) ((*io * gain) >> shift); | ||
740 | ++io; | ||
741 | } | ||
742 | } | ||
743 | } | ||
744 | |||
745 | // Silence detection | ||
746 | |||
747 | void emu_play( struct Kss_Emu* this, long count, sample_t* out ) | ||
748 | { | ||
749 | check( current_track_ >= 0 ); | ||
750 | this->emu_time += count; | ||
751 | if ( this->current_track >= 0 && !this->emu_track_ended_ ) { | ||
752 | if ( play_( this, count, out ) ) | ||
753 | this->emu_track_ended_ = true; | ||
754 | } | ||
755 | else | ||
756 | memset( out, 0, count * sizeof *out ); | ||
757 | } | ||
758 | |||
759 | // number of consecutive silent samples at end | ||
760 | static long count_silence( sample_t* begin, long size ) | ||
761 | { | ||
762 | sample_t first = *begin; | ||
763 | *begin = silence_threshold; // sentinel | ||
764 | sample_t* p = begin + size; | ||
765 | while ( (unsigned) (*--p + silence_threshold / 2) <= (unsigned) silence_threshold ) { } | ||
766 | *begin = first; | ||
767 | return size - (p - begin); | ||
768 | } | ||
769 | |||
770 | // fill internal buffer and check it for silence | ||
771 | void fill_buf( struct Kss_Emu* this ) | ||
772 | { | ||
773 | assert( !this->buf_remain ); | ||
774 | if ( !this->emu_track_ended_ ) | ||
775 | { | ||
776 | emu_play( this, buf_size, this->buf ); | ||
777 | long silence = count_silence( this->buf, buf_size ); | ||
778 | if ( silence < buf_size ) | ||
779 | { | ||
780 | this->silence_time = this->emu_time - silence; | ||
781 | this->buf_remain = buf_size; | ||
782 | return; | ||
783 | } | ||
784 | } | ||
785 | this->silence_count += buf_size; | ||
786 | } | ||
787 | |||
788 | blargg_err_t Kss_play( struct Kss_Emu* this, long out_count, sample_t* out ) | ||
789 | { | ||
790 | if ( this->track_ended ) | ||
791 | { | ||
792 | memset( out, 0, out_count * sizeof *out ); | ||
793 | } | ||
794 | else | ||
795 | { | ||
796 | require( this->current_track >= 0 ); | ||
797 | require( out_count % stereo == 0 ); | ||
798 | |||
799 | assert( this->emu_time >= this->out_time ); | ||
800 | |||
801 | // prints nifty graph of how far ahead we are when searching for silence | ||
802 | //debug_printf( "%*s \n", int ((emu_time - out_time) * 7 / sample_rate()), "*" ); | ||
803 | |||
804 | long pos = 0; | ||
805 | if ( this->silence_count ) | ||
806 | { | ||
807 | // during a run of silence, run emulator at >=2x speed so it gets ahead | ||
808 | long ahead_time = this->silence_lookahead * (this->out_time + out_count -this->silence_time) + this->silence_time; | ||
809 | while ( this->emu_time < ahead_time && !(this->buf_remain |this-> emu_track_ended_) ) | ||
810 | fill_buf( this ); | ||
811 | |||
812 | // fill with silence | ||
813 | pos = min( this->silence_count, out_count ); | ||
814 | memset( out, 0, pos * sizeof *out ); | ||
815 | this->silence_count -= pos; | ||
816 | |||
817 | if ( this->emu_time - this->silence_time > silence_max * stereo * this->sample_rate ) | ||
818 | { | ||
819 | this->track_ended = this->emu_track_ended_ = true; | ||
820 | this->silence_count = 0; | ||
821 | this->buf_remain = 0; | ||
822 | } | ||
823 | } | ||
824 | |||
825 | if ( this->buf_remain ) | ||
826 | { | ||
827 | // empty silence buf | ||
828 | long n = min( this->buf_remain, out_count - pos ); | ||
829 | memcpy( &out [pos], this->buf + (buf_size - this->buf_remain), n * sizeof *out ); | ||
830 | this->buf_remain -= n; | ||
831 | pos += n; | ||
832 | } | ||
833 | |||
834 | // generate remaining samples normally | ||
835 | long remain = out_count - pos; | ||
836 | if ( remain ) | ||
837 | { | ||
838 | emu_play( this, remain, out + pos ); | ||
839 | this->track_ended |= this->emu_track_ended_; | ||
840 | |||
841 | if ( !this->ignore_silence || this->out_time > this->fade_start ) | ||
842 | { | ||
843 | // check end for a new run of silence | ||
844 | long silence = count_silence( out + pos, remain ); | ||
845 | if ( silence < remain ) | ||
846 | this->silence_time = this->emu_time - silence; | ||
847 | |||
848 | if ( this->emu_time - this->silence_time >= buf_size ) | ||
849 | fill_buf( this ); // cause silence detection on next play() | ||
850 | } | ||
851 | } | ||
852 | |||
853 | if ( this->out_time > this->fade_start ) | ||
854 | handle_fade( this, out_count, out ); | ||
855 | } | ||
856 | this->out_time += out_count; | ||
857 | return 0; | ||
858 | } | ||
859 | |||
860 | blargg_err_t play_( struct Kss_Emu* this, long count, sample_t* out ) | ||
861 | { | ||
862 | long remain = count; | ||
863 | while ( remain ) | ||
864 | { | ||
865 | remain -= Buffer_read_samples( &this->stereo_buffer, &out [count - remain], remain ); | ||
866 | if ( remain ) | ||
867 | { | ||
868 | if ( this->buf_changed_count != Buffer_channels_changed_count( &this->stereo_buffer ) ) | ||
869 | { | ||
870 | this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buffer ); | ||
871 | Sound_mute_voices( this, this->mute_mask_ ); | ||
872 | } | ||
873 | int msec = Buffer_length( &this->stereo_buffer ); | ||
874 | /* blip_time_t clocks_emulated = (blargg_long) msec * clock_rate_ / 1000; */ | ||
875 | blip_time_t clocks_emulated = msec * this->clock_rate_ / 1000 - 100; | ||
876 | RETURN_ERR( run_clocks( this, &clocks_emulated ) ); | ||
877 | assert( clocks_emulated ); | ||
878 | Buffer_end_frame( &this->stereo_buffer, clocks_emulated ); | ||
879 | } | ||
880 | } | ||
881 | return 0; | ||
882 | } | ||
883 | |||