summaryrefslogtreecommitdiff
path: root/lib/rbcodec/codecs/libgme/sgc_emu.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rbcodec/codecs/libgme/sgc_emu.c')
-rw-r--r--lib/rbcodec/codecs/libgme/sgc_emu.c480
1 files changed, 480 insertions, 0 deletions
diff --git a/lib/rbcodec/codecs/libgme/sgc_emu.c b/lib/rbcodec/codecs/libgme/sgc_emu.c
new file mode 100644
index 0000000000..267f2c9271
--- /dev/null
+++ b/lib/rbcodec/codecs/libgme/sgc_emu.c
@@ -0,0 +1,480 @@
1// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
2
3#include "sgc_emu.h"
4
5/* Copyright (C) 2009 Shay Green. This module is free software; you
6can redistribute it and/or modify it under the terms of the GNU Lesser
7General Public License as published by the Free Software Foundation; either
8version 2.1 of the License, or (at your option) any later version. This
9module is distributed in the hope that it will be useful, but WITHOUT ANY
10WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12details. You should have received a copy of the GNU Lesser General Public
13License aint with this module; if not, write to the Free Software Foundation,
14Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
15
16#include "blargg_source.h"
17
18int const osc_count = sms_osc_count + fm_apu_osc_count;
19
20const char gme_wrong_file_type [] = "Wrong file type for this emulator";
21
22static void clear_track_vars( struct Sgc_Emu* this )
23{
24 this->current_track = -1;
25 track_stop( &this->track_filter );
26}
27
28void Sgc_init( struct Sgc_Emu* this )
29{
30 assert( offsetof (struct header_t,copyright [32]) == header_size );
31
32 this->sample_rate = 0;
33 this->mute_mask_ = 0;
34 this->tempo = (int)FP_ONE_TEMPO;
35 this->gain = (int)FP_ONE_GAIN;
36
37 // defaults
38 this->tfilter = *track_get_setup( &this->track_filter );
39 this->tfilter.max_initial = 2;
40 this->tfilter.lookahead = 6;
41 this->track_filter.silence_ignored_ = false;
42
43 Sms_apu_init( &this->apu );
44 Fm_apu_create( &this->fm_apu );
45
46 Rom_init( &this->rom, 0x4000 );
47 Z80_init( &this->cpu );
48
49 Sound_set_gain( this, (int)(FP_ONE_GAIN*1.2) );
50
51 // Unload
52 this->voice_count = 0;
53 this->voice_types = 0;
54 clear_track_vars( this );
55}
56
57// Setup
58
59blargg_err_t Sgc_load_mem( struct Sgc_Emu* this, const void* data, long size )
60{
61 RETURN_ERR( Rom_load( &this->rom, data, size, header_size, &this->header, 0 ) );
62
63 if ( !valid_tag( &this->header ) )
64 return gme_wrong_file_type;
65
66 /* if ( header.vers != 1 )
67 warning( "Unknown file version" ); */
68
69 /* if ( header.system > 2 )
70 warning( "Unknown system" ); */
71
72 addr_t load_addr = get_le16( this->header.load_addr );
73 /* if ( load_addr < 0x400 )
74 set_warning( "Invalid load address" ); */
75
76 Rom_set_addr( &this->rom, load_addr );
77 this->play_period = clock_rate( this ) / 60;
78
79 if ( sega_mapping( this ) && Fm_apu_supported() )
80 RETURN_ERR( Fm_apu_init( &this->fm_apu, clock_rate( this ), clock_rate( this ) / 72 ) );
81
82 this->m3u.size = 0;
83 this->track_count = this->header.song_count;
84 this->voice_count = sega_mapping( this ) ? osc_count : sms_osc_count;
85 static int const types [sms_osc_count + fm_apu_osc_count] = {
86 wave_type+1, wave_type+2, wave_type+3, mixed_type+1, mixed_type+2
87 };
88 this->voice_types = types;
89
90 Sms_apu_volume( &this->apu, this->gain );
91 Fm_apu_volume( &this->fm_apu, this->gain );
92
93 // Setup buffer
94 this->clock_rate_ = clock_rate( this );
95 Buffer_clock_rate( &this->stereo_buf, clock_rate( this ) );
96 RETURN_ERR( Buffer_set_channel_count( &this->stereo_buf, this->voice_count, this->voice_types ) );
97 this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
98
99 Sound_set_tempo( this, this->tempo );
100 Sound_mute_voices( this, this->mute_mask_ );
101 return 0;
102}
103
104static void Sound_set_voice( struct Sgc_Emu* this, int i, struct Blip_Buffer* c, struct Blip_Buffer* l, struct Blip_Buffer* r )
105{
106 if ( i < sms_osc_count )
107 Sms_apu_set_output( &this->apu, i, c, l, r );
108 else
109 Fm_apu_set_output( &this->fm_apu, c );
110}
111
112static blargg_err_t run_clocks( struct Sgc_Emu* this, blip_time_t* duration, int msec )
113{
114#if defined(ROCKBOX)
115 (void) msec;
116#endif
117
118 cpu_time_t t = *duration;
119 while ( Z80_time( &this->cpu ) < t )
120 {
121 cpu_time_t next = min( t, this->next_play );
122 if ( run_cpu( this, next ) )
123 {
124 /* warning( "Unsupported CPU instruction" ); */
125 Z80_set_time( &this->cpu, next );
126 }
127
128 if ( this->cpu.r.pc == this->idle_addr )
129 Z80_set_time( &this->cpu, next );
130
131 if ( Z80_time( &this->cpu ) >= this->next_play )
132 {
133 this->next_play += this->play_period;
134 if ( this->cpu.r.pc == this->idle_addr )
135 jsr( this, this->header.play_addr );
136 }
137 }
138
139 this->next_play -= t;
140 check( this->next_play >= 0 );
141 Z80_adjust_time( &this->cpu, -t );
142
143 Sms_apu_end_frame( &this->apu, t );
144 if ( sega_mapping( this ) && this->fm_accessed )
145 {
146 if ( Fm_apu_supported() )
147 Fm_apu_end_frame( &this->fm_apu, t );
148 /* else
149 warning( "FM sound not supported" ); */
150 }
151
152 return 0;
153}
154
155// Emulation
156
157void cpu_out( struct Sgc_Emu* this, cpu_time_t time, addr_t addr, int data )
158{
159 int port = addr & 0xFF;
160
161 if ( sega_mapping( this ) )
162 {
163 switch ( port )
164 {
165 case 0x06:
166 Sms_apu_write_ggstereo( &this->apu, time, data );
167 return;
168
169 case 0x7E:
170 case 0x7F:
171 Sms_apu_write_data( &this->apu, time, data ); /* dprintf( "$7E<-%02X\n", data ); */
172 return;
173
174 case 0xF0:
175 this->fm_accessed = true;
176 if ( Fm_apu_supported() )
177 Fm_apu_write_addr( &this->fm_apu, data );//, dprintf( "$F0<-%02X\n", data );
178 return;
179
180 case 0xF1:
181 this->fm_accessed = true;
182 if ( Fm_apu_supported() )
183 Fm_apu_write_data( &this->fm_apu, time, data );//, dprintf( "$F1<-%02X\n", data );
184 return;
185 }
186 }
187 else if ( port >= 0xE0 )
188 {
189 Sms_apu_write_data( &this->apu, time, data );
190 return;
191 }
192}
193
194void jsr( struct Sgc_Emu* this, byte addr [2] )
195{
196 *Z80_write( &this->cpu, --this->cpu.r.sp ) = this->idle_addr >> 8;
197 *Z80_write( &this->cpu, --this->cpu.r.sp ) = this->idle_addr & 0xFF;
198 this->cpu.r.pc = get_le16( addr );
199}
200
201static void set_bank( struct Sgc_Emu* this, int bank, void const* data )
202{
203 //dprintf( "map bank %d to %p\n", bank, (byte*) data - rom.at_addr( 0 ) );
204 Z80_map_mem( &this->cpu, bank * this->rom.bank_size, this->rom.bank_size, this->unmapped_write, data );
205}
206
207void cpu_write( struct Sgc_Emu* this, addr_t addr, int data )
208{
209 if ( (addr ^ 0xFFFC) > 3 || !sega_mapping( this ) )
210 {
211 *Z80_write( &this->cpu, addr ) = data;
212 return;
213 }
214
215 switch ( addr )
216 {
217 case 0xFFFC:
218 Z80_map_mem_rw( &this->cpu, 2 * this->rom.bank_size, this->rom.bank_size, this->ram2 );
219 if ( data & 0x08 )
220 break;
221
222 this->bank2 = this->ram2;
223 // FALL THROUGH
224
225 case 0xFFFF: {
226 bool rom_mapped = (Z80_read( &this->cpu, 2 * this->rom.bank_size ) == this->bank2);
227 this->bank2 = Rom_at_addr( &this->rom, data * this->rom.bank_size );
228 if ( rom_mapped )
229 set_bank( this, 2, this->bank2 );
230 break;
231 }
232
233 case 0xFFFD:
234 set_bank( this, 0, Rom_at_addr( &this->rom, data * this->rom.bank_size ) );
235 break;
236
237 case 0xFFFE:
238 set_bank( this, 1, Rom_at_addr( &this->rom, data * this->rom.bank_size ) );
239 break;
240 }
241}
242
243blargg_err_t Sgc_set_sample_rate( struct Sgc_Emu* this, int rate )
244{
245 require( !this->sample_rate ); // sample rate can't be changed once set
246 Buffer_init( &this->stereo_buf );
247 Buffer_set_sample_rate( &this->stereo_buf, rate, 1000 / 20 );
248
249 // Set buffer bass
250 Buffer_bass_freq( &this->stereo_buf, 80 );
251
252 this->sample_rate = rate;
253 RETURN_ERR( track_init( &this->track_filter, this ) );
254 this->tfilter.max_silence = 6 * stereo * this->sample_rate;
255 return 0;
256}
257
258void Sound_mute_voice( struct Sgc_Emu* this, int index, bool mute )
259{
260 require( (unsigned) index < (unsigned) this->voice_count );
261 int bit = 1 << index;
262 int mask = this->mute_mask_ | bit;
263 if ( !mute )
264 mask ^= bit;
265 Sound_mute_voices( this, mask );
266}
267
268void Sound_mute_voices( struct Sgc_Emu* this, int mask )
269{
270 require( this->sample_rate ); // sample rate must be set first
271 this->mute_mask_ = mask;
272
273 int i;
274 for ( i = this->voice_count; i--; )
275 {
276 if ( mask & (1 << i) )
277 {
278 Sound_set_voice( this, i, 0, 0, 0 );
279 }
280 else
281 {
282 struct channel_t ch = Buffer_channel( &this->stereo_buf, i );
283 assert( (ch.center && ch.left && ch.right) ||
284 (!ch.center && !ch.left && !ch.right) ); // all or nothing
285 Sound_set_voice( this, i, ch.center, ch.left, ch.right );
286 }
287 }
288}
289
290void Sound_set_tempo( struct Sgc_Emu* this, int t )
291{
292 require( this->sample_rate ); // sample rate must be set first
293 int const min = (int)(FP_ONE_TEMPO*0.02);
294 int const max = (int)(FP_ONE_TEMPO*4.00);
295 if ( t < min ) t = min;
296 if ( t > max ) t = max;
297 this->tempo = t;
298
299 this->play_period = (int) ((clock_rate( this ) * FP_ONE_TEMPO) / (this->header.rate ? 50 : 60) / t);
300}
301
302blargg_err_t Sgc_start_track( struct Sgc_Emu* this, int track )
303{
304 clear_track_vars( this );
305
306 // Remap track if playlist available
307 if ( this->m3u.size > 0 ) {
308 struct entry_t* e = &this->m3u.entries[track];
309 track = e->track;
310 }
311
312 this->current_track = track;
313
314 if ( sega_mapping( this ) )
315 {
316 Sms_apu_reset( &this->apu, 0, 0 );
317 Fm_apu_reset( &this->fm_apu );
318 this->fm_accessed = false;
319 }
320 else
321 {
322 Sms_apu_reset( &this->apu, 0x0003, 15 );
323 }
324
325 memset( this->ram , 0, sizeof this->ram );
326 memset( this->ram2, 0, sizeof this->ram2 );
327 memset( this->vectors, 0xFF, sizeof this->vectors );
328 Z80_reset( &this->cpu, this->unmapped_write, this->rom.unmapped );
329
330 if ( sega_mapping( this ) )
331 {
332 this->vectors_addr = 0x10000 - page_size;
333 this->idle_addr = this->vectors_addr;
334 int i;
335 for ( i = 1; i < 8; ++i )
336 {
337 this->vectors [i*8 + 0] = 0xC3; // JP addr
338 this->vectors [i*8 + 1] = this->header.rst_addrs [i - 1] & 0xff;
339 this->vectors [i*8 + 2] = this->header.rst_addrs [i - 1] >> 8;
340 }
341
342 Z80_map_mem_rw( &this->cpu, 0xC000, 0x2000, this->ram );
343 Z80_map_mem( &this->cpu, this->vectors_addr, page_size, this->unmapped_write, this->vectors );
344
345 this->bank2 = NULL;
346 for ( i = 0; i < 4; ++i )
347 cpu_write( this, 0xFFFC + i, this->header.mapping [i] );
348 }
349 else
350 {
351 if ( !this->coleco_bios )
352 return "Coleco BIOS not set"; /* BLARGG_ERR( BLARGG_ERR_CALLER, "Coleco BIOS not set" ); */
353
354 this->vectors_addr = 0;
355 Z80_map_mem( &this->cpu, 0, 0x2000, this->unmapped_write, this->coleco_bios );
356 int i;
357 for ( i = 0; i < 8; ++i )
358 Z80_map_mem_rw( &this->cpu, 0x6000 + i*0x400, 0x400, this->ram );
359
360 this->idle_addr = 0x2000;
361 Z80_map_mem( &this->cpu, 0x2000, page_size, this->unmapped_write, this->vectors );
362
363 for ( i = 0; i < 0x8000 / this->rom.bank_size; ++i )
364 {
365 int addr = 0x8000 + i*this->rom.bank_size;
366 Z80_map_mem( &this->cpu, addr, this->rom.bank_size, this->unmapped_write, Rom_at_addr( &this->rom, addr ) );
367 }
368 }
369
370 this->cpu.r.sp = get_le16( this->header.stack_ptr );
371 this->cpu.r.b.a = track;
372 this->next_play = this->play_period;
373
374 jsr( this, this->header.init_addr );
375
376 Buffer_clear( &this->stereo_buf );
377
378 // convert filter times to samples
379 struct setup_t s = this->tfilter;
380 s.max_initial *= this->sample_rate * stereo;
381 #ifdef GME_DISABLE_SILENCE_LOOKAHEAD
382 s.lookahead = 1;
383 #endif
384 track_setup( &this->track_filter, &s );
385
386 return track_start( &this->track_filter );
387}
388
389// Tell/Seek
390
391static int msec_to_samples( int msec, int sample_rate )
392{
393 int sec = msec / 1000;
394 msec -= sec * 1000;
395 return (sec * sample_rate + msec * sample_rate / 1000) * stereo;
396}
397
398int Track_tell( struct Sgc_Emu* this )
399{
400 int rate = this->sample_rate * stereo;
401 int sec = track_sample_count( &this->track_filter ) / rate;
402 return sec * 1000 + (track_sample_count( &this->track_filter ) - sec * rate) * 1000 / rate;
403}
404
405blargg_err_t Track_seek( struct Sgc_Emu* this, int msec )
406{
407 int time = msec_to_samples( msec, this->sample_rate );
408 if ( time < track_sample_count( &this->track_filter ) )
409 RETURN_ERR( Sgc_start_track( this, this->current_track ) );
410 return Track_skip( this, time - track_sample_count( &this->track_filter ) );
411}
412
413blargg_err_t Track_skip( struct Sgc_Emu* this, int count )
414{
415 require( this->current_track >= 0 ); // start_track() must have been called already
416 return track_skip( &this->track_filter, count );
417}
418
419blargg_err_t skip_( void* emu, int count )
420{
421 struct Sgc_Emu* this = (struct Sgc_Emu*) emu;
422
423 // for long skip, mute sound
424 const int threshold = 32768;
425 if ( count > threshold )
426 {
427 int saved_mute = this->mute_mask_;
428 Sound_mute_voices( this, ~0 );
429
430 int n = count - threshold/2;
431 n &= ~(2048-1); // round to multiple of 2048
432 count -= n;
433 RETURN_ERR( skippy_( &this->track_filter, n ) );
434
435 Sound_mute_voices( this, saved_mute );
436 }
437
438 return skippy_( &this->track_filter, count );
439}
440
441void Track_set_fade( struct Sgc_Emu* this, int start_msec, int length_msec )
442{
443 track_set_fade( &this->track_filter, msec_to_samples( start_msec, this->sample_rate ),
444 length_msec * this->sample_rate / (1000 / stereo) );
445}
446
447blargg_err_t Sgc_play( struct Sgc_Emu* this, int out_count, sample_t* out )
448{
449 require( this->current_track >= 0 );
450 require( out_count % stereo == 0 );
451 return track_play( &this->track_filter, out_count, out );
452}
453
454blargg_err_t play_( void* emu, int count, sample_t out [] )
455{
456 struct Sgc_Emu* this = (struct Sgc_Emu*) emu;
457
458 int remain = count;
459 while ( remain )
460 {
461 Buffer_disable_immediate_removal( &this->stereo_buf );
462 remain -= Buffer_read_samples( &this->stereo_buf, &out [count - remain], remain );
463 if ( remain )
464 {
465 if ( this->buf_changed_count != Buffer_channels_changed_count( &this->stereo_buf ) )
466 {
467 this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
468
469 // Remute voices
470 Sound_mute_voices( this, this->mute_mask_ );
471 }
472 int msec = Buffer_length( &this->stereo_buf );
473 blip_time_t clocks_emulated = msec * this->clock_rate_ / 1000 - 100;
474 RETURN_ERR( run_clocks( this, &clocks_emulated, msec ) );
475 assert( clocks_emulated );
476 Buffer_end_frame( &this->stereo_buf, clocks_emulated );
477 }
478 }
479 return 0;
480}