Fix makefile conditions
[maemo-rb.git] / apps / codecs / libgme / ay_emu.c
blob92faba492973f220b86a1a98453bd04a443f252c
1 // Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
3 #include "ay_emu.h"
5 #include "blargg_endian.h"
7 /* Copyright (C) 2006-2009 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 */
18 #include "blargg_source.h"
20 const char* const gme_wrong_file_type = "Wrong file type for this emulator";
22 // TODO: probably don't need detailed errors as to why file is corrupt
24 int const spectrum_clock = 3546900; // 128K Spectrum
25 int const spectrum_period = 70908;
27 //int const spectrum_clock = 3500000; // 48K Spectrum
28 //int const spectrum_period = 69888;
30 int const cpc_clock = 2000000;
32 static void clear_track_vars( struct Ay_Emu *this )
34 this->current_track = -1;
35 track_stop( &this->track_filter );
38 void Ay_init( struct Ay_Emu *this )
40 this->sample_rate = 0;
41 this->mute_mask_ = 0;
42 this->tempo = (int)FP_ONE_TEMPO;
43 this->gain = (int)FP_ONE_GAIN;
44 this->track_count = 0;
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;
52 this->beeper_output = NULL;
53 disable_beeper( this );
55 Ay_apu_init( &this->apu );
56 Z80_init( &this->cpu );
58 // clears fields
59 this->voice_count = 0;
60 this->voice_types = 0;
61 clear_track_vars( this );
64 // Track info
66 // Given pointer to 2-byte offset of data, returns pointer to data, or NULL if
67 // offset is 0 or there is less than min_size bytes of data available.
68 static byte const* get_data( struct file_t const* file, byte const ptr [], int min_size )
70 int offset = (int16_t) get_be16( ptr );
71 int pos = ptr - (byte const*) file->header;
72 int size = file->end - (byte const*) file->header;
73 assert( (unsigned) pos <= (unsigned) size - 2 );
74 int limit = size - min_size;
75 if ( limit < 0 || !offset || (unsigned) (pos + offset) > (unsigned) limit )
76 return NULL;
77 return ptr + offset;
80 static blargg_err_t parse_header( byte const in [], int size, struct file_t* out )
82 if ( size < header_size )
83 return gme_wrong_file_type;
85 out->header = (struct header_t const*) in;
86 out->end = in + size;
87 struct header_t const* h = (struct header_t const*) in;
88 if ( memcmp( h->tag, "ZXAYEMUL", 8 ) )
89 return gme_wrong_file_type;
91 out->tracks = get_data( out, h->track_info, (h->max_track + 1) * 4 );
92 if ( !out->tracks )
93 return "missing track data";
95 return 0;
98 // Setup
100 static void change_clock_rate( struct Ay_Emu *this, int rate )
102 this->clock_rate_ = rate;
103 Buffer_clock_rate( &this->stereo_buf, rate );
106 blargg_err_t Ay_load_mem( struct Ay_Emu *this, byte const in [], long size )
108 // Unload
109 this->voice_count = 0;
110 this->track_count = 0;
111 this->m3u.size = 0;
112 clear_track_vars( this );
114 assert( offsetof (struct header_t,track_info [2]) == header_size );
116 RETURN_ERR( parse_header( in, size, &this->file ) );
118 /* if ( file.header->vers > 2 )
119 warning( "Unknown file version" ); */
121 this->voice_count = ay_osc_count + 1; // +1 for beeper
122 static int const types [ay_osc_count + 1] = {
123 wave_type+0, wave_type+1, wave_type+2, mixed_type+1
125 this->voice_types = types;
127 Ay_apu_volume( &this->apu, this->gain);
129 // Setup buffer
130 change_clock_rate( this, spectrum_clock );
131 RETURN_ERR( Buffer_set_channel_count( &this->stereo_buf, this->voice_count, this->voice_types ) );
132 this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
134 Sound_set_tempo( this, this->tempo );
135 Sound_mute_voices( this, this->mute_mask_ );
137 this->track_count = this->file.header->max_track + 1;
138 return 0;
141 static void set_beeper_output( struct Ay_Emu *this, struct Blip_Buffer* b )
143 this->beeper_output = b;
144 if ( b && !this->cpc_mode )
145 this->beeper_mask = 0x10;
146 else
147 disable_beeper( this );
150 static void set_voice( struct Ay_Emu *this, int i, struct Blip_Buffer* center )
152 if ( i >= ay_osc_count )
153 set_beeper_output( this, center );
154 else
155 Ay_apu_set_output( &this->apu, i, center );
158 static blargg_err_t run_clocks( struct Ay_Emu *this, blip_time_t* duration, int msec )
160 #if defined(ROCKBOX)
161 (void) msec;
162 #endif
164 cpu_time_t *end = duration;
165 struct Z80_Cpu* cpu = &this->cpu;
166 Z80_set_time( cpu, 0 );
168 // Since detection of CPC mode will halve clock rate during the frame
169 // and thus generate up to twice as much sound, we must generate half
170 // as much until mode is known.
171 if ( !(this->spectrum_mode | this->cpc_mode) )
172 *end /= 2;
174 while ( Z80_time( cpu ) < *end )
176 run_cpu( this, min( *end, this->next_play ) );
178 if ( Z80_time( cpu ) >= this->next_play )
180 // next frame
181 this->next_play += this->play_period;
183 if ( cpu->r.iff1 )
185 // interrupt enabled
187 if ( this->mem.ram [cpu->r.pc] == 0x76 )
188 cpu->r.pc++; // advance past HALT instruction
190 cpu->r.iff1 = 0;
191 cpu->r.iff2 = 0;
193 this->mem.ram [--cpu->r.sp] = (byte) (cpu->r.pc >> 8);
194 this->mem.ram [--cpu->r.sp] = (byte) (cpu->r.pc);
196 // fixed interrupt
197 cpu->r.pc = 0x38;
198 Z80_adjust_time( cpu, 12 );
200 if ( cpu->r.im == 2 )
202 // vectored interrupt
203 addr_t addr = cpu->r.i * 0x100 + 0xFF;
204 cpu->r.pc = this->mem.ram [(addr + 1) & 0xFFFF] * 0x100 + this->mem.ram [addr];
205 Z80_adjust_time( cpu, 6 );
211 // End time frame
212 *end = Z80_time( cpu );
213 this->next_play -= *end;
214 check( this->next_play >= 0 );
215 Z80_adjust_time( cpu, -*end );
216 Ay_apu_end_frame( &this->apu, *end );
217 return 0;
220 // Emulation
222 void cpu_out_( struct Ay_Emu *this, cpu_time_t time, addr_t addr, int data )
224 // Spectrum
225 if ( !this->cpc_mode )
227 switch ( addr & 0xFEFF )
229 case 0xFEFD:
230 this->spectrum_mode = true;
231 Ay_apu_write_addr( &this->apu, data );
232 return;
234 case 0xBEFD:
235 this->spectrum_mode = true;
236 Ay_apu_write_data( &this->apu, time, data );
237 return;
241 // CPC
242 if ( !this->spectrum_mode )
244 switch ( addr >> 8 )
246 case 0xF6:
247 switch ( data & 0xC0 )
249 case 0xC0:
250 Ay_apu_write_addr( &this->apu, this->cpc_latch );
251 goto enable_cpc;
253 case 0x80:
254 Ay_apu_write_data( &this->apu, time, this->cpc_latch );
255 goto enable_cpc;
257 break;
259 case 0xF4:
260 this->cpc_latch = data;
261 goto enable_cpc;
265 /* dprintf( "Unmapped OUT: $%04X <- $%02X\n", addr, data ); */
266 return;
268 enable_cpc:
269 if ( !this->cpc_mode )
271 this->cpc_mode = true;
272 disable_beeper( this );
274 change_clock_rate( this, cpc_clock );
275 Sound_set_tempo( this, this->tempo );
279 blargg_err_t Ay_set_sample_rate( struct Ay_Emu *this, int rate )
281 require( !this->sample_rate ); // sample rate can't be changed once set
282 Buffer_init( &this->stereo_buf );
283 RETURN_ERR( Buffer_set_sample_rate( &this->stereo_buf, rate, 1000 / 20 ) );
285 // Set buffer bass
286 Buffer_bass_freq( &this->stereo_buf, 160 );
288 this->sample_rate = rate;
289 RETURN_ERR( track_init( &this->track_filter, this ) );
290 this->tfilter.max_silence = 6 * stereo * this->sample_rate;
291 return 0;
294 void Sound_mute_voice( struct Ay_Emu *this, int index, bool mute )
296 require( (unsigned) index < (unsigned) this->voice_count );
297 int bit = 1 << index;
298 int mask = this->mute_mask_ | bit;
299 if ( !mute )
300 mask ^= bit;
301 Sound_mute_voices( this, mask );
304 void Sound_mute_voices( struct Ay_Emu *this, int mask )
306 require( this->sample_rate ); // sample rate must be set first
307 this->mute_mask_ = mask;
309 int i;
310 for ( i = this->voice_count; i--; )
312 if ( mask & (1 << i) )
314 set_voice( this, i, 0 );
316 else
318 struct channel_t ch = Buffer_channel( &this->stereo_buf, i );
319 assert( (ch.center && ch.left && ch.right) ||
320 (!ch.center && !ch.left && !ch.right) ); // all or nothing
321 set_voice( this, i, ch.center );
326 void Sound_set_tempo( struct Ay_Emu *this, int t )
328 require( this->sample_rate ); // sample rate must be set first
329 int const min = (int)(FP_ONE_TEMPO*0.02);
330 int const max = (int)(FP_ONE_TEMPO*4.00);
331 if ( t < min ) t = min;
332 if ( t > max ) t = max;
333 this->tempo = t;
335 int p = spectrum_period;
336 if ( this->clock_rate_ != spectrum_clock )
337 p = this->clock_rate_ / 50;
339 this->play_period = (blip_time_t) ((p * FP_ONE_TEMPO) / t);
342 blargg_err_t Ay_start_track( struct Ay_Emu *this, int track )
344 clear_track_vars( this );
346 // Remap track if playlist available
347 if ( this->m3u.size > 0 ) {
348 struct entry_t* e = &this->m3u.entries[track];
349 track = e->track;
352 this->current_track = track;
353 Buffer_clear( &this->stereo_buf );
355 byte* const mem = this->mem.ram;
357 memset( mem + 0x0000, 0xC9, 0x100 ); // fill RST vectors with RET
358 memset( mem + 0x0100, 0xFF, 0x4000 - 0x100 );
359 memset( mem + ram_addr, 0x00, mem_size - ram_addr );
361 // locate data blocks
362 byte const* const data = get_data( &this->file, this->file.tracks + track * 4 + 2, 14 );
363 if ( !data )
364 return "file data missing";
366 byte const* const more_data = get_data( &this->file, data + 10, 6 );
367 if ( !more_data )
368 return "file data missing";
370 byte const* blocks = get_data( &this->file, data + 12, 8 );
371 if ( !blocks )
372 return "file data missing";
374 // initial addresses
375 unsigned addr = get_be16( blocks );
376 if ( !addr )
377 return "file data missing";
379 unsigned init = get_be16( more_data + 2 );
380 if ( !init )
381 init = addr;
383 // copy blocks into memory
386 blocks += 2;
387 unsigned len = get_be16( blocks ); blocks += 2;
388 if ( addr + len > mem_size )
390 /* warning( "Bad data block size" ); */
391 len = mem_size - addr;
393 check( len );
394 byte const* in = get_data( &this->file, blocks, 0 ); blocks += 2;
395 if ( len > (unsigned) (this->file.end - in) )
397 /* warning( "File data missing" ); */
398 len = this->file.end - in;
401 memcpy( mem + addr, in, len );
403 if ( this->file.end - blocks < 8 )
405 /* warning( "File data missing" ); */
406 break;
409 while ( (addr = get_be16( blocks )) != 0 );
411 // copy and configure driver
412 static byte const passive [] = {
413 0xF3, // DI
414 0xCD, 0, 0, // CALL init
415 0xED, 0x5E, // LOOP: IM 2
416 0xFB, // EI
417 0x76, // HALT
418 0x18, 0xFA // JR LOOP
420 static byte const active [] = {
421 0xF3, // DI
422 0xCD, 0, 0, // CALL init
423 0xED, 0x56, // LOOP: IM 1
424 0xFB, // EI
425 0x76, // HALT
426 0xCD, 0, 0, // CALL play
427 0x18, 0xF7 // JR LOOP
429 memcpy( mem, passive, sizeof passive );
430 int const play_addr = get_be16( more_data + 4 );
431 if ( play_addr )
433 memcpy( mem, active, sizeof active );
434 mem [ 9] = play_addr;
435 mem [10] = play_addr >> 8;
437 mem [2] = init;
438 mem [3] = init >> 8;
440 mem [0x38] = 0xFB; // Put EI at interrupt vector (followed by RET)
442 // start at spectrum speed
443 change_clock_rate( this, spectrum_clock );
444 Sound_set_tempo( this, this->tempo );
446 struct registers_t r;
447 memset( &r, 0, sizeof(struct registers_t) );
449 r.sp = get_be16( more_data );
450 r.b.a = r.b.b = r.b.d = r.b.h = data [8];
451 r.b.flags = r.b.c = r.b.e = r.b.l = data [9];
452 r.alt.w = r.w;
453 r.ix = r.iy = r.w.hl;
455 memset( this->mem.padding1, 0xFF, sizeof this->mem.padding1 );
457 int const mirrored = 0x80; // this much is mirrored after end of memory
458 memset( this->mem.ram + mem_size + mirrored, 0xFF, sizeof this->mem.ram - mem_size - mirrored );
459 memcpy( this->mem.ram + mem_size, this->mem.ram, mirrored ); // some code wraps around (ugh)
461 Z80_reset( &this->cpu, this->mem.padding1, this->mem.padding1 );
462 Z80_map_mem( &this->cpu, 0, mem_size, this->mem.ram, this->mem.ram );
463 this->cpu.r = r;
465 this->beeper_delta = (int) ((ay_amp_range*4)/5);
466 this->last_beeper = 0;
467 this->next_play = this->play_period;
468 this->spectrum_mode = false;
469 this->cpc_mode = false;
470 this->cpc_latch = 0;
471 set_beeper_output( this, this->beeper_output );
472 Ay_apu_reset( &this->apu );
474 // a few tunes rely on channels having tone enabled at the beginning
475 Ay_apu_write_addr( &this->apu, 7 );
476 Ay_apu_write_data( &this->apu, 0, 0x38 );
478 // convert filter times to samples
479 struct setup_t s = this->tfilter;
480 s.max_initial *= this->sample_rate * stereo;
481 #ifdef GME_DISABLE_SILENCE_LOOKAHEAD
482 s.lookahead = 1;
483 #endif
484 track_setup( &this->track_filter, &s );
486 return track_start( &this->track_filter );
489 // Tell/Seek
491 static int msec_to_samples( int msec, int sample_rate )
493 int sec = msec / 1000;
494 msec -= sec * 1000;
495 return (sec * sample_rate + msec * sample_rate / 1000) * stereo;
498 int Track_tell( struct Ay_Emu *this )
500 int rate = this->sample_rate * stereo;
501 int sec = track_sample_count( &this->track_filter ) / rate;
502 return sec * 1000 + (track_sample_count( &this->track_filter ) - sec * rate) * 1000 / rate;
505 blargg_err_t Track_seek( struct Ay_Emu *this, int msec )
507 int time = msec_to_samples( msec, this->sample_rate );
508 if ( time < track_sample_count( &this->track_filter ) )
509 RETURN_ERR( Ay_start_track( this, this->current_track ) );
510 return Track_skip( this, time - track_sample_count( &this->track_filter ) );
513 blargg_err_t skip_( void *emu, int count )
515 struct Ay_Emu* this = (struct Ay_Emu*) emu;
517 // for long skip, mute sound
518 const int threshold = 32768;
519 if ( count > threshold )
521 int saved_mute = this->mute_mask_;
522 Sound_mute_voices( this, ~0 );
524 int n = count - threshold/2;
525 n &= ~(2048-1); // round to multiple of 2048
526 count -= n;
527 RETURN_ERR( skippy_( &this->track_filter, n ) );
529 Sound_mute_voices( this, saved_mute );
532 return skippy_( &this->track_filter, count );
535 blargg_err_t Track_skip( struct Ay_Emu *this, int count )
537 require( this->current_track >= 0 ); // start_track() must have been called already
538 return track_skip( &this->track_filter, count );
541 int Track_get_length( struct Ay_Emu* this, int n )
543 int length = 0;
545 byte const* track_info = get_data( &this->file, this->file.tracks + n * 4 + 2, 6 );
546 if ( track_info )
547 length = get_be16( track_info + 4 ) * (1000 / 50); // frames to msec
549 if ( (this->m3u.size > 0) && (n < this->m3u.size) ) {
550 struct entry_t* entry = &this->m3u.entries [n];
551 length = entry->length;
554 if ( length <= 0 )
555 length = 120 * 1000; /* 2 minutes */
557 return length;
560 void Track_set_fade( struct Ay_Emu *this, int start_msec, int length_msec )
562 track_set_fade( &this->track_filter, msec_to_samples( start_msec, this->sample_rate ),
563 length_msec * this->sample_rate / (1000 / stereo) );
566 blargg_err_t Ay_play( struct Ay_Emu *this, int out_count, sample_t* out )
568 require( this->current_track >= 0 );
569 require( out_count % stereo == 0 );
570 return track_play( &this->track_filter, out_count, out );
573 blargg_err_t play_( void *emu, int count, sample_t* out )
575 struct Ay_Emu* this = (struct Ay_Emu*) emu;
577 int remain = count;
578 while ( remain )
580 Buffer_disable_immediate_removal( &this->stereo_buf );
581 remain -= Buffer_read_samples( &this->stereo_buf, &out [count - remain], remain );
582 if ( remain )
584 if ( this->buf_changed_count != Buffer_channels_changed_count( &this->stereo_buf ) )
586 this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
588 // Remute voices
589 Sound_mute_voices( this, this->mute_mask_ );
591 int msec = Buffer_length( &this->stereo_buf );
592 blip_time_t clocks_emulated = msec * this->clock_rate_ / 1000 - 100;
593 RETURN_ERR( run_clocks( this, &clocks_emulated, msec ) );
594 assert( clocks_emulated );
595 Buffer_end_frame( &this->stereo_buf, clocks_emulated );
598 return 0;