summaryrefslogtreecommitdiff
path: root/lib/rbcodec/codecs/libgme/hes_emu.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rbcodec/codecs/libgme/hes_emu.c')
-rw-r--r--lib/rbcodec/codecs/libgme/hes_emu.c644
1 files changed, 644 insertions, 0 deletions
diff --git a/lib/rbcodec/codecs/libgme/hes_emu.c b/lib/rbcodec/codecs/libgme/hes_emu.c
new file mode 100644
index 0000000000..d6bafea334
--- /dev/null
+++ b/lib/rbcodec/codecs/libgme/hes_emu.c
@@ -0,0 +1,644 @@
1// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/
2
3#include "hes_emu.h"
4
5#include "blargg_endian.h"
6#include "blargg_source.h"
7
8/* Copyright (C) 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
19int const timer_mask = 0x04;
20int const vdp_mask = 0x02;
21int const i_flag_mask = 0x04;
22int const unmapped = 0xFF;
23
24int const period_60hz = 262 * 455; // scanlines * clocks per scanline
25
26const char gme_wrong_file_type [] = "Wrong file type for this emulator";
27
28static void clear_track_vars( struct Hes_Emu* this )
29{
30 this->current_track_ = -1;
31 track_stop( &this->track_filter );
32}
33
34void Hes_init( struct Hes_Emu* this )
35{
36 this->sample_rate_ = 0;
37 this->mute_mask_ = 0;
38 this->tempo_ = (int)(FP_ONE_TEMPO);
39
40 // defaults
41 this->tfilter = *track_get_setup( &this->track_filter );
42 this->tfilter.max_initial = 2;
43 this->tfilter.lookahead = 6;
44 this->track_filter.silence_ignored_ = false;
45
46 this->timer.raw_load = 0;
47 Sound_set_gain( this, (int)(FP_ONE_GAIN*1.11) );
48
49 Rom_init( &this->rom, 0x2000 );
50
51 Apu_init( &this->apu );
52 Adpcm_init( &this->adpcm );
53 Cpu_init( &this->cpu );
54
55 /* Set default track count */
56 this->track_count = 255;
57
58 // clears fields
59 this->voice_count_ = 0;
60 this->voice_types_ = 0;
61 clear_track_vars( this );
62}
63
64static blargg_err_t check_hes_header( void const* header )
65{
66 if ( memcmp( header, "HESM", 4 ) )
67 return gme_wrong_file_type;
68 return 0;
69}
70
71// Setup
72
73blargg_err_t Hes_load_mem( struct Hes_Emu* this, void* data, long size )
74{
75 // Unload
76 this->voice_count_ = 0;
77 this->track_count = 255;
78 this->m3u.size = 0;
79 clear_track_vars( this );
80
81 assert( offsetof (struct header_t,unused [4]) == header_size );
82 RETURN_ERR( Rom_load( &this->rom, data, size, header_size, &this->header, unmapped ) );
83
84 RETURN_ERR( check_hes_header( this->header.tag ) );
85
86 /* if ( header_.vers != 0 )
87 warning( "Unknown file version" );
88
89 if ( memcmp( header_.data_tag, "DATA", 4 ) )
90 warning( "Data header missing" );
91
92 if ( memcmp( header_.unused, "\0\0\0\0", 4 ) )
93 warning( "Unknown header data" ); */
94
95 // File spec supports multiple blocks, but I haven't found any, and
96 // many files have bad sizes in the only block, so it's simpler to
97 // just try to load the damn data as best as possible.
98
99 int addr = get_le32( this->header.addr );
100 /* int rom_size = get_le32( this->header.size ); */
101 int const rom_max = 0x100000;
102 if ( (unsigned) addr >= (unsigned) rom_max )
103 {
104 /* warning( "Invalid address" ); */
105 addr &= rom_max - 1;
106 }
107 /* if ( (unsigned) (addr + size) > (unsigned) rom_max )
108 warning( "Invalid size" );
109
110 if ( rom_size != rom.file_size() )
111 {
112 if ( size <= rom.file_size() - 4 && !memcmp( rom.begin() + size, "DATA", 4 ) )
113 warning( "Multiple DATA not supported" );
114 else if ( size < rom.file_size() )
115 warning( "Extra file data" );
116 else
117 warning( "Missing file data" );
118 } */
119
120 Rom_set_addr( &this->rom, addr );
121
122 this->voice_count_ = osc_count + adpcm_osc_count;
123 static int const types [osc_count + adpcm_osc_count] = {
124 wave_type+0, wave_type+1, wave_type+2, wave_type+3, mixed_type+0, mixed_type+1, mixed_type+2
125 };
126 this->voice_types_ = types;
127
128 Apu_volume( &this->apu, this->gain_ );
129 Adpcm_volume( &this->adpcm, this->gain_ );
130
131 // Setup buffer
132 this->clock_rate_ = 7159091;
133 Buffer_clock_rate( &this->stereo_buf, 7159091 );
134 RETURN_ERR( Buffer_set_channel_count( &this->stereo_buf, this->voice_count_, this->voice_types_ ) );
135 this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
136
137 Sound_set_tempo( this, this->tempo_ );
138 Sound_mute_voices( this, this->mute_mask_ );
139
140 return 0;
141}
142
143// Emulation
144
145static void recalc_timer_load( struct Hes_Emu* this )
146{
147 this->timer.load = this->timer.raw_load * this->timer_base + 1;
148}
149
150// Hardware
151
152void run_until( struct Hes_Emu* this, hes_time_t present )
153{
154 while ( this->vdp.next_vbl < present )
155 this->vdp.next_vbl += this->play_period;
156
157 hes_time_t elapsed = present - this->timer.last_time;
158 if ( elapsed > 0 )
159 {
160 if ( this->timer.enabled )
161 {
162 this->timer.count -= elapsed;
163 if ( this->timer.count <= 0 )
164 this->timer.count += this->timer.load;
165 }
166 this->timer.last_time = present;
167 }
168}
169
170void write_vdp( struct Hes_Emu* this, int addr, int data )
171{
172 switch ( addr )
173 {
174 case 0:
175 this->vdp.latch = data & 0x1F;
176 break;
177
178 case 2:
179 if ( this->vdp.latch == 5 )
180 {
181 /* if ( data & 0x04 )
182 warning( "Scanline interrupt unsupported" ); */
183 run_until( this, Cpu_time( &this->cpu ) );
184 this->vdp.control = data;
185 irq_changed( this );
186 }
187 /* else
188 {
189 dprintf( "VDP not supported: $%02X <- $%02X\n", vdp.latch, data );
190 } */
191 break;
192
193 case 3:
194 /* dprintf( "VDP MSB not supported: $%02X <- $%02X\n", vdp.latch, data ); */
195 break;
196 }
197}
198
199void write_mem_( struct Hes_Emu* this, hes_addr_t addr, int data )
200{
201 hes_time_t time = Cpu_time( &this->cpu );
202 if ( (unsigned) (addr - apu_io_addr) < apu_io_size )
203 {
204 // Avoid going way past end when a long block xfer is writing to I/O space.
205 // Not a problem for other registers below because they don't write to
206 // Blip_Buffer.
207 hes_time_t t = min( time, Cpu_end_time( &this->cpu ) + 8 );
208 Apu_write_data( &this->apu, t, addr, data );
209 return;
210 }
211 if ( (unsigned) (addr - adpcm_io_addr) < adpcm_io_size )
212 {
213 hes_time_t t = min( time, Cpu_end_time( &this->cpu ) + 6 );
214 Adpcm_write_data( &this->adpcm, t, addr, data );
215 return;
216 }
217
218 switch ( addr )
219 {
220 case 0x0000:
221 case 0x0002:
222 case 0x0003:
223 write_vdp( this, addr, data );
224 return;
225
226 case 0x0C00: {
227 run_until( this, time );
228 this->timer.raw_load = (data & 0x7F) + 1;
229 recalc_timer_load( this );
230 this->timer.count = this->timer.load;
231 break;
232 }
233
234 case 0x0C01:
235 data &= 1;
236 if ( this->timer.enabled == data )
237 return;
238 run_until( this, time );
239 this->timer.enabled = data;
240 if ( data )
241 this->timer.count = this->timer.load;
242 break;
243
244 case 0x1402:
245 run_until( this, time );
246 this->irq.disables = data;
247 /* if ( (data & 0xF8) && (data & 0xF8) != 0xF8 ) // flag questionable values
248 dprintf( "Int mask: $%02X\n", data ); */
249 break;
250
251 case 0x1403:
252 run_until( this, time );
253 if ( this->timer.enabled )
254 this->timer.count = this->timer.load;
255 this->timer.fired = false;
256 break;
257
258#ifndef NDEBUG
259 case 0x1000: // I/O port
260 case 0x0402: // palette
261 case 0x0403:
262 case 0x0404:
263 case 0x0405:
264 return;
265
266 default:
267 /* dprintf( "unmapped write $%04X <- $%02X\n", addr, data ); */
268 return;
269#endif
270 }
271
272 irq_changed( this );
273}
274
275int read_mem_( struct Hes_Emu* this, hes_addr_t addr )
276{
277 hes_time_t time = Cpu_time( &this->cpu );
278 addr &= page_size - 1;
279 switch ( addr )
280 {
281 case 0x0000:
282 if ( this->irq.vdp > time )
283 return 0;
284 this->irq.vdp = future_time;
285 run_until( this, time );
286 irq_changed( this );
287 return 0x20;
288
289 /* case 0x0002:
290 case 0x0003:
291 dprintf( "VDP read not supported: %d\n", addr );
292 return 0; */
293
294 case 0x0C01:
295 //return timer.enabled; // TODO: remove?
296 case 0x0C00:
297 run_until( this, time );
298 /* dprintf( "Timer count read\n" ); */
299 return (unsigned) (this->timer.count - 1) / this->timer_base;
300
301 case 0x1402:
302 return this->irq.disables;
303
304 case 0x1403:
305 {
306 int status = 0;
307 if ( this->irq.timer <= time ) status |= timer_mask;
308 if ( this->irq.vdp <= time ) status |= vdp_mask;
309 return status;
310 }
311
312 case 0x180A:
313 case 0x180B:
314 case 0x180C:
315 case 0x180D:
316 return Adpcm_read_data( &this->adpcm, time, addr );
317
318 #ifndef NDEBUG
319 case 0x1000: // I/O port
320 //case 0x180C: // CD-ROM
321 //case 0x180D:
322 break;
323
324 /* default:
325 dprintf( "unmapped read $%04X\n", addr ); */
326 #endif
327 }
328
329 return unmapped;
330}
331
332void irq_changed( struct Hes_Emu* this )
333{
334 hes_time_t present = Cpu_time( &this->cpu );
335
336 if ( this->irq.timer > present )
337 {
338 this->irq.timer = future_time;
339 if ( this->timer.enabled && !this->timer.fired )
340 this->irq.timer = present + this->timer.count;
341 }
342
343 if ( this->irq.vdp > present )
344 {
345 this->irq.vdp = future_time;
346 if ( this->vdp.control & 0x08 )
347 this->irq.vdp = this->vdp.next_vbl;
348 }
349
350 hes_time_t time = future_time;
351 if ( !(this->irq.disables & timer_mask) ) time = this->irq.timer;
352 if ( !(this->irq.disables & vdp_mask) ) time = min( time, this->irq.vdp );
353
354 Cpu_set_irq_time( &this->cpu, time );
355}
356
357int cpu_done( struct Hes_Emu* this )
358{
359 check( Cpu_time( &this->cpu ) >= Cpu_end_time( &this->cpu ) ||
360 (!(this->cpu.r.flags & i_flag_mask) && Cpu_time( &this->cpu ) >= Cpu_irq_time( &this->cpu )) );
361
362 if ( !(this->cpu.r.flags & i_flag_mask) )
363 {
364 hes_time_t present = Cpu_time( &this->cpu );
365
366 if ( this->irq.timer <= present && !(this->irq.disables & timer_mask) )
367 {
368 this->timer.fired = true;
369 this->irq.timer = future_time;
370 irq_changed( this ); // overkill, but not worth writing custom code
371 return 0x0A;
372 }
373
374 if ( this->irq.vdp <= present && !(this->irq.disables & vdp_mask) )
375 {
376 // work around for bugs with music not acknowledging VDP
377 //run_until( present );
378 //irq.vdp = cpu.future_time;
379 //irq_changed();
380 return 0x08;
381 }
382 }
383 return -1;
384}
385
386static void adjust_time( hes_time_t* time, hes_time_t delta )
387{
388 if ( *time < future_time )
389 {
390 *time -= delta;
391 if ( *time < 0 )
392 *time = 0;
393 }
394}
395
396static blargg_err_t end_frame( struct Hes_Emu* this, hes_time_t duration )
397{
398 /* if ( run_cpu( this, duration ) )
399 warning( "Emulation error (illegal instruction)" ); */
400 run_cpu( this, duration );
401
402 check( Cpu_time( &this->cpu ) >= duration );
403 //check( time() - duration < 20 ); // Txx instruction could cause going way over
404
405 run_until( this, duration );
406
407 // end time frame
408 this->timer.last_time -= duration;
409 this->vdp.next_vbl -= duration;
410 Cpu_end_frame( &this->cpu, duration );
411 adjust_time( &this->irq.timer, duration );
412 adjust_time( &this->irq.vdp, duration );
413 Apu_end_frame( &this->apu, duration );
414 Adpcm_end_frame( &this->adpcm, duration );
415
416 return 0;
417}
418
419static blargg_err_t run_clocks( struct Hes_Emu* this, blip_time_t* duration_ )
420{
421 return end_frame( this, *duration_ );
422}
423
424blargg_err_t play_( void *emu, int count, sample_t out [] )
425{
426 struct Hes_Emu* this = (struct Hes_Emu*) emu;
427
428 int remain = count;
429 while ( remain )
430 {
431 Buffer_disable_immediate_removal( &this->stereo_buf );
432 remain -= Buffer_read_samples( &this->stereo_buf, &out [count - remain], remain );
433 if ( remain )
434 {
435 if ( this->buf_changed_count != Buffer_channels_changed_count( &this->stereo_buf ) )
436 {
437 this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
438
439 // Remute voices
440 Sound_mute_voices( this, this->mute_mask_ );
441 }
442 int msec = Buffer_length( &this->stereo_buf );
443 blip_time_t clocks_emulated = msec * this->clock_rate_ / 1000 - 100;
444 RETURN_ERR( run_clocks( this, &clocks_emulated ) );
445 assert( clocks_emulated );
446 Buffer_end_frame( &this->stereo_buf, clocks_emulated );
447 }
448 }
449 return 0;
450}
451
452// Music emu
453
454blargg_err_t Hes_set_sample_rate( struct Hes_Emu* this, int rate )
455{
456 require( !this->sample_rate_ ); // sample rate can't be changed once set
457 Buffer_init( &this->stereo_buf );
458 RETURN_ERR( Buffer_set_sample_rate( &this->stereo_buf, rate, 1000 / 20 ) );
459
460 // Set bass frequency
461 Buffer_bass_freq( &this->stereo_buf, 60 );
462
463 this->sample_rate_ = rate;
464 RETURN_ERR( track_init( &this->track_filter, this ) );
465 this->tfilter.max_silence = 6 * stereo * this->sample_rate_;
466 return 0;
467}
468
469void Sound_mute_voice( struct Hes_Emu* this, int index, bool mute )
470{
471 require( (unsigned) index < (unsigned) this->voice_count_ );
472 int bit = 1 << index;
473 int mask = this->mute_mask_ | bit;
474 if ( !mute )
475 mask ^= bit;
476 Sound_mute_voices( this, mask );
477}
478
479void Sound_mute_voices( struct Hes_Emu* this, int mask )
480{
481 require( this->sample_rate_ ); // sample rate must be set first
482 this->mute_mask_ = mask;
483
484 // Set adpcm voice
485 struct channel_t ch = Buffer_channel( &this->stereo_buf, this->voice_count_ );
486 if ( mask & (1 << this->voice_count_ ) )
487 Adpcm_set_output( &this->adpcm, 0, 0, 0, 0 );
488 else
489 Adpcm_set_output( &this->adpcm, 0, ch.center, ch.left, ch.right );
490
491 // Set apu voices
492 int i = this->voice_count_ - 1;
493 for ( ; i--; )
494 {
495 if ( mask & (1 << i) )
496 {
497 Apu_osc_output( &this->apu, i, 0, 0, 0 );
498 }
499 else
500 {
501 struct channel_t ch = Buffer_channel( &this->stereo_buf, i );
502 assert( (ch.center && ch.left && ch.right) ||
503 (!ch.center && !ch.left && !ch.right) ); // all or nothing
504 Apu_osc_output( &this->apu, i, ch.center, ch.left, ch.right );
505 }
506 }
507}
508
509void Sound_set_tempo( struct Hes_Emu* this, int t )
510{
511 require( this->sample_rate_ ); // sample rate must be set first
512 int const min = (int)(FP_ONE_TEMPO*0.02);
513 int const max = (int)(FP_ONE_TEMPO*4.00);
514 if ( t < min ) t = min;
515 if ( t > max ) t = max;
516 this->play_period = (hes_time_t) ((period_60hz*FP_ONE_TEMPO) / t);
517 this->timer_base = (int) ((1024*FP_ONE_TEMPO) / t);
518 recalc_timer_load( this );
519 this->tempo_ = t;
520}
521
522blargg_err_t Hes_start_track( struct Hes_Emu* this, int track )
523{
524 clear_track_vars( this );
525
526 // Remap track if playlist available
527 if ( this->m3u.size > 0 ) {
528 struct entry_t* e = &this->m3u.entries[track];
529 track = e->track;
530 }
531
532 this->current_track_ = track;
533
534 Buffer_clear( &this->stereo_buf );
535
536 memset( this->ram, 0, sizeof this->ram ); // some HES music relies on zero fill
537 memset( this->sgx, 0, sizeof this->sgx );
538
539 Apu_reset( &this->apu );
540 Adpcm_reset( &this->adpcm );
541 Cpu_reset( &this->cpu );
542
543 unsigned i;
544 for ( i = 0; i < sizeof this->header.banks; i++ )
545 set_mmr( this, i, this->header.banks [i] );
546 set_mmr( this, page_count, 0xFF ); // unmapped beyond end of address space
547
548 this->irq.disables = timer_mask | vdp_mask;
549 this->irq.timer = future_time;
550 this->irq.vdp = future_time;
551
552 this->timer.enabled = false;
553 this->timer.raw_load= 0x80;
554 this->timer.count = this->timer.load;
555 this->timer.fired = false;
556 this->timer.last_time = 0;
557
558 this->vdp.latch = 0;
559 this->vdp.control = 0;
560 this->vdp.next_vbl = 0;
561
562 this->ram [0x1FF] = (idle_addr - 1) >> 8;
563 this->ram [0x1FE] = (idle_addr - 1) & 0xFF;
564 this->cpu.r.sp = 0xFD;
565 this->cpu.r.pc = get_le16( this->header.init_addr );
566 this->cpu.r.a = track;
567
568 recalc_timer_load( this );
569
570 // convert filter times to samples
571 struct setup_t s = this->tfilter;
572 s.max_initial *= this->sample_rate_ * stereo;
573 #ifdef GME_DISABLE_SILENCE_LOOKAHEAD
574 s.lookahead = 1;
575 #endif
576 track_setup( &this->track_filter, &s );
577
578 return track_start( &this->track_filter );
579}
580
581// Tell/Seek
582
583static int msec_to_samples( int msec, int sample_rate )
584{
585 int sec = msec / 1000;
586 msec -= sec * 1000;
587 return (sec * sample_rate + msec * sample_rate / 1000) * stereo;
588}
589
590int Track_tell( struct Hes_Emu* this )
591{
592 int rate = this->sample_rate_ * stereo;
593 int sec = track_sample_count( &this->track_filter ) / rate;
594 return sec * 1000 + (track_sample_count( &this->track_filter ) - sec * rate) * 1000 / rate;
595}
596
597blargg_err_t Track_seek( struct Hes_Emu* this, int msec )
598{
599 int time = msec_to_samples( msec, this->sample_rate_ );
600 if ( time < track_sample_count( &this->track_filter ) )
601 RETURN_ERR( Hes_start_track( this, this->current_track_ ) );
602 return Track_skip( this, time - track_sample_count( &this->track_filter ) );
603}
604
605blargg_err_t skip_( void* emu, int count )
606{
607 struct Hes_Emu* this = (struct Hes_Emu*) emu;
608
609 // for long skip, mute sound
610 const int threshold = 32768;
611 if ( count > threshold )
612 {
613 int saved_mute = this->mute_mask_;
614 Sound_mute_voices( this, ~0 );
615
616 int n = count - threshold/2;
617 n &= ~(2048-1); // round to multiple of 2048
618 count -= n;
619 RETURN_ERR( skippy_( &this->track_filter, n ) );
620
621 Sound_mute_voices( this, saved_mute );
622 }
623
624 return skippy_( &this->track_filter, count );
625}
626
627blargg_err_t Track_skip( struct Hes_Emu* this, int count )
628{
629 require( this->current_track_ >= 0 ); // start_track() must have been called already
630 return track_skip( &this->track_filter, count );
631}
632
633void Track_set_fade( struct Hes_Emu* this, int start_msec, int length_msec )
634{
635 track_set_fade( &this->track_filter, msec_to_samples( start_msec, this->sample_rate_ ),
636 length_msec * this->sample_rate_ / (1000 / stereo) );
637}
638
639blargg_err_t Hes_play( struct Hes_Emu* this, int out_count, sample_t* out )
640{
641 require( this->current_track_ >= 0 );
642 require( out_count % stereo == 0 );
643 return track_play( &this->track_filter, out_count, out );
644}