summaryrefslogtreecommitdiff
path: root/lib/rbcodec/codecs/libgme/gbs_emu.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rbcodec/codecs/libgme/gbs_emu.c')
-rw-r--r--lib/rbcodec/codecs/libgme/gbs_emu.c452
1 files changed, 452 insertions, 0 deletions
diff --git a/lib/rbcodec/codecs/libgme/gbs_emu.c b/lib/rbcodec/codecs/libgme/gbs_emu.c
new file mode 100644
index 0000000000..7a6d484673
--- /dev/null
+++ b/lib/rbcodec/codecs/libgme/gbs_emu.c
@@ -0,0 +1,452 @@
1// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/
2
3#include "gbs_emu.h"
4
5#include "blargg_endian.h"
6
7/* Copyright (C) 2003-2006 Shay Green. this module is free software; you
8can redistribute it and/or modify it under the terms of the GNU Lesser
9General Public License as published by the Free Software Foundation; either
10version 2.1 of the License, or (at your option) any later version. this
11module is distributed in the hope that it will be useful, but WITHOUT ANY
12WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
14details. You should have received a copy of the GNU Lesser General Public
15License along with this module; if not, write to the Free Software Foundation,
16Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
17
18#include "blargg_source.h"
19
20const char gme_wrong_file_type [] = "Wrong file type for this emulator";
21
22int const idle_addr = 0xF00D;
23int const tempo_unit = 16;
24
25static void clear_track_vars( struct Gbs_Emu* this )
26{
27 this->current_track_ = -1;
28 track_stop( &this->track_filter );
29}
30
31void Gbs_init( struct Gbs_Emu* this )
32{
33 this->sample_rate_ = 0;
34 this->mute_mask_ = 0;
35 this->tempo_ = (int)(FP_ONE_TEMPO);
36
37 // Unload
38 this->header.timer_mode = 0;
39
40 // defaults
41 this->tfilter = *track_get_setup( &this->track_filter );
42 this->tfilter.max_initial = 21;
43 this->tfilter.lookahead = 6;
44 this->track_filter.silence_ignored_ = false;
45
46 Sound_set_gain( this, (int)(FP_ONE_GAIN*1.2) );
47
48 Rom_init( &this->rom, 0x4000 );
49
50 Apu_init( &this->apu );
51 Cpu_init( &this->cpu );
52
53 this->tempo = tempo_unit;
54 this->sound_hardware = sound_gbs;
55
56 // Reduce apu sound clicks?
57 Apu_reduce_clicks( &this->apu, true );
58
59 // clears fields
60 this->voice_count_ = 0;
61 this->voice_types_ = 0;
62 clear_track_vars( this );
63}
64
65static blargg_err_t check_gbs_header( void const* header )
66{
67 if ( memcmp( header, "GBS", 3 ) )
68 return gme_wrong_file_type;
69 return 0;
70}
71
72// Setup
73
74blargg_err_t Gbs_load_mem( struct Gbs_Emu* this, void* data, long size )
75{
76 // Unload
77 this->header.timer_mode = 0;
78 this->voice_count_ = 0;
79 this->track_count = 0;
80 this->m3u.size = 0;
81 clear_track_vars( this );
82
83 assert( offsetof (struct header_t,copyright [32]) == header_size );
84 RETURN_ERR( Rom_load( &this->rom, data, size, header_size, &this->header, 0 ) );
85
86 RETURN_ERR( check_gbs_header( &this->header ) );
87
88 /* Ignore warnings? */
89 /*if ( header_.vers != 1 )
90 warning( "Unknown file version" );
91
92 if ( header_.timer_mode & 0x78 )
93 warning( "Invalid timer mode" ); */
94
95 /* unsigned load_addr = get_le16( this->header.load_addr ); */
96 /* if ( (header_.load_addr [1] | header_.init_addr [1] | header_.play_addr [1]) > 0x7F ||
97 load_addr < 0x400 )
98 warning( "Invalid load/init/play address" ); */
99
100 unsigned load_addr = get_le16( this->header.load_addr );
101 /* if ( (this->header.load_addr [1] | this->header.init_addr [1] | this->header.play_addr [1]) > 0x7F ||
102 load_addr < 0x400 )
103 warning( "Invalid load/init/play address" ); */
104
105 this->cpu.rst_base = load_addr;
106 Rom_set_addr( &this->rom, load_addr );
107
108 this->voice_count_ = osc_count;
109 static int const types [osc_count] = {
110 wave_type+1, wave_type+2, wave_type+3, mixed_type+1
111 };
112 this->voice_types_ = types;
113
114 Apu_volume( &this->apu, this->gain_ );
115
116 // Change clock rate & setup buffer
117 this->clock_rate_ = 4194304;
118 Buffer_clock_rate( &this->stereo_buf, 4194304 );
119 RETURN_ERR( Buffer_set_channel_count( &this->stereo_buf, this->voice_count_, this->voice_types_ ) );
120 this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
121
122 // Post load
123 Sound_set_tempo( this, this->tempo_ );
124 Sound_mute_voices( this, this->mute_mask_ );
125
126 // Set track count
127 this->track_count = this->header.track_count;
128 return 0;
129}
130
131// Emulation
132
133// see gb_cpu_io.h for read/write functions
134
135void set_bank( struct Gbs_Emu* this, int n )
136{
137 addr_t addr = mask_addr( n * this->rom.bank_size, this->rom.mask );
138 if ( addr == 0 && this->rom.size > this->rom.bank_size )
139 addr = this->rom.bank_size; // MBC1&2 behavior, bank 0 acts like bank 1
140 Cpu_map_code( &this->cpu, this->rom.bank_size, this->rom.bank_size, Rom_at_addr( &this->rom, addr ) );
141}
142
143void update_timer( struct Gbs_Emu* this )
144{
145 this->play_period = 70224 / tempo_unit; /// 59.73 Hz
146
147 if ( this->header.timer_mode & 0x04 )
148 {
149 // Using custom rate
150 static byte const rates [4] = { 6, 0, 2, 4 };
151 // TODO: emulate double speed CPU mode rather than halving timer rate
152 int double_speed = this->header.timer_mode >> 7;
153 int shift = rates [this->ram [hi_page + 7] & 3] - double_speed;
154 this->play_period = (256 - this->ram [hi_page + 6]) << shift;
155 }
156
157 this->play_period *= this->tempo;
158}
159
160// Jumps to routine, given pointer to address in file header. Pushes idle_addr
161// as return address, NOT old PC.
162void jsr_then_stop( struct Gbs_Emu* this, byte const addr [] )
163{
164 check( this->cpu.r.sp == get_le16( this->header.stack_ptr ) );
165 this->cpu.r.pc = get_le16( addr );
166 write_mem( this, --this->cpu.r.sp, idle_addr >> 8 );
167 write_mem( this, --this->cpu.r.sp, idle_addr );
168}
169
170static blargg_err_t run_until( struct Gbs_Emu* this, int end )
171{
172 this->end_time = end;
173 Cpu_set_time( &this->cpu, Cpu_time( &this->cpu ) - end );
174 while ( true )
175 {
176 run_cpu( this );
177 if ( Cpu_time( &this->cpu ) >= 0 )
178 break;
179
180 if ( this->cpu.r.pc == idle_addr )
181 {
182 if ( this->next_play > this->end_time )
183 {
184 Cpu_set_time( &this->cpu, 0 );
185 break;
186 }
187
188 if ( Cpu_time( &this->cpu ) < this->next_play - this->end_time )
189 Cpu_set_time( &this->cpu, this->next_play - this->end_time );
190 this->next_play += this->play_period;
191 jsr_then_stop( this, this->header.play_addr );
192 }
193 else if ( this->cpu.r.pc > 0xFFFF )
194 {
195 /* warning( "PC wrapped around\n" ); */
196 this->cpu.r.pc &= 0xFFFF;
197 }
198 else
199 {
200 /* warning( "Emulation error (illegal/unsupported instruction)" ); */
201 this->cpu.r.pc = (this->cpu.r.pc + 1) & 0xFFFF;
202 Cpu_set_time( &this->cpu, Cpu_time( &this->cpu ) + 6 );
203 }
204 }
205
206 return 0;
207}
208
209static blargg_err_t end_frame( struct Gbs_Emu* this, int end )
210{
211 RETURN_ERR( run_until( this, end ) );
212
213 this->next_play -= end;
214 if ( this->next_play < 0 ) // happens when play routine takes too long
215 {
216 #if !defined(GBS_IGNORE_STARVED_PLAY)
217 check( false );
218 #endif
219 this->next_play = 0;
220 }
221
222 Apu_end_frame( &this->apu, end );
223
224 return 0;
225}
226
227blargg_err_t run_clocks( struct Gbs_Emu* this, blip_time_t duration )
228{
229 return end_frame( this, duration );
230}
231
232blargg_err_t play_( void* emu, int count, sample_t* out )
233{
234 struct Gbs_Emu* this = (struct Gbs_Emu*) emu;
235
236 int remain = count;
237 while ( remain )
238 {
239 Buffer_disable_immediate_removal( &this->stereo_buf );
240 remain -= Buffer_read_samples( &this->stereo_buf, &out [count - remain], remain );
241 if ( remain )
242 {
243 if ( this->buf_changed_count != Buffer_channels_changed_count( &this->stereo_buf ) )
244 {
245 this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
246
247 // Remute voices
248 Sound_mute_voices( this, this->mute_mask_ );
249 }
250 int msec = Buffer_length( &this->stereo_buf );
251 blip_time_t clocks_emulated = msec * this->clock_rate_ / 1000 - 100;
252 RETURN_ERR( run_clocks( this, clocks_emulated ) );
253 assert( clocks_emulated );
254 Buffer_end_frame( &this->stereo_buf, clocks_emulated );
255 }
256 }
257 return 0;
258}
259
260blargg_err_t Gbs_set_sample_rate( struct Gbs_Emu* this, int rate )
261{
262 require( !this->sample_rate_ ); // sample rate can't be changed once set
263 Buffer_init( &this->stereo_buf );
264 RETURN_ERR( Buffer_set_sample_rate( &this->stereo_buf, rate, 1000 / 20 ) );
265
266 // Set bass frequency
267 Buffer_bass_freq( &this->stereo_buf, 300 );
268
269 this->sample_rate_ = rate;
270 RETURN_ERR( track_init( &this->track_filter, this ) );
271 this->tfilter.max_silence = 6 * stereo * this->sample_rate_;
272 return 0;
273}
274
275
276// Sound
277
278void Sound_mute_voice( struct Gbs_Emu* this, int index, bool mute )
279{
280 require( (unsigned) index < (unsigned) this->voice_count_ );
281 int bit = 1 << index;
282 int mask = this->mute_mask_ | bit;
283 if ( !mute )
284 mask ^= bit;
285 Sound_mute_voices( this, mask );
286}
287
288void Sound_mute_voices( struct Gbs_Emu* this, int mask )
289{
290 require( this->sample_rate_ ); // sample rate must be set first
291 this->mute_mask_ = mask;
292
293 int i;
294 for ( i = this->voice_count_; i--; )
295 {
296 if ( mask & (1 << i) )
297 {
298 Apu_set_output( &this->apu, i, 0, 0, 0 );
299 }
300 else
301 {
302 struct channel_t ch = Buffer_channel( &this->stereo_buf, i );
303 assert( (ch.center && ch.left && ch.right) ||
304 (!ch.center && !ch.left && !ch.right) ); // all or nothing
305 Apu_set_output( &this->apu, i, ch.center, ch.left, ch.right );
306 }
307 }
308}
309
310void Sound_set_tempo( struct Gbs_Emu* this, int t )
311{
312 require( this->sample_rate_ ); // sample rate must be set first
313 int const min = (int)(FP_ONE_TEMPO*0.02);
314 int const max = (int)(FP_ONE_TEMPO*4.00);
315 if ( t < min ) t = min;
316 if ( t > max ) t = max;
317 this->tempo_ = t;
318
319 this->tempo = (int) ((tempo_unit * FP_ONE_TEMPO) / t);
320 Apu_set_tempo( &this->apu, t );
321 update_timer( this );
322}
323
324blargg_err_t Gbs_start_track( struct Gbs_Emu* this, int track )
325{
326 clear_track_vars( this );
327
328 // Remap track if playlist available
329 if ( this->m3u.size > 0 ) {
330 struct entry_t* e = &this->m3u.entries[track];
331 track = e->track;
332 }
333
334 this->current_track_ = track;
335 Buffer_clear( &this->stereo_buf );
336
337 // Reset APU to state expected by most rips
338 static byte const sound_data [] = {
339 0x80, 0xBF, 0x00, 0x00, 0xB8, // square 1 DAC disabled
340 0x00, 0x3F, 0x00, 0x00, 0xB8, // square 2 DAC disabled
341 0x7F, 0xFF, 0x9F, 0x00, 0xB8, // wave DAC disabled
342 0x00, 0xFF, 0x00, 0x00, 0xB8, // noise DAC disabled
343 0x77, 0xFF, 0x80, // max volume, all chans in center, power on
344 };
345
346 enum sound_t mode = this->sound_hardware;
347 if ( mode == sound_gbs )
348 mode = (this->header.timer_mode & 0x80) ? sound_cgb : sound_dmg;
349
350 Apu_reset( &this->apu, (enum gb_mode_t) mode, false );
351 Apu_write_register( &this->apu, 0, 0xFF26, 0x80 ); // power on
352 int i;
353 for ( i = 0; i < (int) sizeof sound_data; i++ )
354 Apu_write_register( &this->apu, 0, i + io_addr, sound_data [i] );
355 Apu_end_frame( &this->apu, 1 ); // necessary to get click out of the way */
356
357 memset( this->ram, 0, 0x4000 );
358 memset( this->ram + 0x4000, 0xFF, 0x1F80 );
359 memset( this->ram + 0x5F80, 0, sizeof this->ram - 0x5F80 );
360 this->ram [hi_page] = 0; // joypad reads back as 0
361 this->ram [idle_addr - ram_addr] = 0xED; // illegal instruction
362 this->ram [hi_page + 6] = this->header.timer_modulo;
363 this->ram [hi_page + 7] = this->header.timer_mode;
364
365 Cpu_reset( &this->cpu, this->rom.unmapped );
366 Cpu_map_code( &this->cpu, ram_addr, 0x10000 - ram_addr, this->ram );
367 Cpu_map_code( &this->cpu, 0, this->rom.bank_size, Rom_at_addr( &this->rom, 0 ) );
368 set_bank( this, this->rom.size > this->rom.bank_size );
369
370 update_timer( this );
371 this->next_play = this->play_period;
372 this->cpu.r.rp.fa = track;
373 this->cpu.r.sp = get_le16( this->header.stack_ptr );
374 this->cpu_time = 0;
375 jsr_then_stop( this, this->header.init_addr );
376
377 // convert filter times to samples
378 struct setup_t s = this->tfilter;
379 s.max_initial *= this->sample_rate_ * stereo;
380 #ifdef GME_DISABLE_SILENCE_LOOKAHEAD
381 s.lookahead = 1;
382 #endif
383 track_setup( &this->track_filter, &s );
384
385 return track_start( &this->track_filter );
386}
387
388
389// Track
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 Gbs_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 Gbs_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( Gbs_start_track( this, this->current_track_ ) );
410 return Track_skip( this, time - track_sample_count( &this->track_filter ) );
411}
412
413blargg_err_t skip_( void* emu, int count )
414{
415 struct Gbs_Emu* this = (struct Gbs_Emu*) emu;
416
417 // for long skip, mute sound
418 const int threshold = 32768;
419 if ( count > threshold )
420 {
421 int saved_mute = this->mute_mask_;
422 Sound_mute_voices( this, ~0 );
423
424 int n = count - threshold/2;
425 n &= ~(2048-1); // round to multiple of 2048
426 count -= n;
427 RETURN_ERR( skippy_( &this->track_filter, n ) );
428
429 Sound_mute_voices( this, saved_mute );
430 }
431
432 return skippy_( &this->track_filter, count );
433}
434
435blargg_err_t Track_skip( struct Gbs_Emu* this, int count )
436{
437 require( this->current_track_ >= 0 ); // start_track() must have been called already
438 return track_skip( &this->track_filter, count );
439}
440
441void Track_set_fade( struct Gbs_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 Gbs_play( struct Gbs_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}