summaryrefslogtreecommitdiff
path: root/lib/rbcodec/codecs/libgme/ay_emu.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rbcodec/codecs/libgme/ay_emu.c')
-rw-r--r--lib/rbcodec/codecs/libgme/ay_emu.c599
1 files changed, 599 insertions, 0 deletions
diff --git a/lib/rbcodec/codecs/libgme/ay_emu.c b/lib/rbcodec/codecs/libgme/ay_emu.c
new file mode 100644
index 0000000000..92faba4929
--- /dev/null
+++ b/lib/rbcodec/codecs/libgme/ay_emu.c
@@ -0,0 +1,599 @@
1// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
2
3#include "ay_emu.h"
4
5#include "blargg_endian.h"
6
7/* Copyright (C) 2006-2009 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* const gme_wrong_file_type = "Wrong file type for this emulator";
21
22// TODO: probably don't need detailed errors as to why file is corrupt
23
24int const spectrum_clock = 3546900; // 128K Spectrum
25int const spectrum_period = 70908;
26
27//int const spectrum_clock = 3500000; // 48K Spectrum
28//int const spectrum_period = 69888;
29
30int const cpc_clock = 2000000;
31
32static void clear_track_vars( struct Ay_Emu *this )
33{
34 this->current_track = -1;
35 track_stop( &this->track_filter );
36}
37
38void Ay_init( struct Ay_Emu *this )
39{
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;
45
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;
51
52 this->beeper_output = NULL;
53 disable_beeper( this );
54
55 Ay_apu_init( &this->apu );
56 Z80_init( &this->cpu );
57
58 // clears fields
59 this->voice_count = 0;
60 this->voice_types = 0;
61 clear_track_vars( this );
62}
63
64// Track info
65
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.
68static byte const* get_data( struct file_t const* file, byte const ptr [], int min_size )
69{
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;
78}
79
80static blargg_err_t parse_header( byte const in [], int size, struct file_t* out )
81{
82 if ( size < header_size )
83 return gme_wrong_file_type;
84
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;
90
91 out->tracks = get_data( out, h->track_info, (h->max_track + 1) * 4 );
92 if ( !out->tracks )
93 return "missing track data";
94
95 return 0;
96}
97
98// Setup
99
100static void change_clock_rate( struct Ay_Emu *this, int rate )
101{
102 this->clock_rate_ = rate;
103 Buffer_clock_rate( &this->stereo_buf, rate );
104}
105
106blargg_err_t Ay_load_mem( struct Ay_Emu *this, byte const in [], long size )
107{
108 // Unload
109 this->voice_count = 0;
110 this->track_count = 0;
111 this->m3u.size = 0;
112 clear_track_vars( this );
113
114 assert( offsetof (struct header_t,track_info [2]) == header_size );
115
116 RETURN_ERR( parse_header( in, size, &this->file ) );
117
118 /* if ( file.header->vers > 2 )
119 warning( "Unknown file version" ); */
120
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
124 };
125 this->voice_types = types;
126
127 Ay_apu_volume( &this->apu, this->gain);
128
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 );
133
134 Sound_set_tempo( this, this->tempo );
135 Sound_mute_voices( this, this->mute_mask_ );
136
137 this->track_count = this->file.header->max_track + 1;
138 return 0;
139}
140
141static void set_beeper_output( struct Ay_Emu *this, struct Blip_Buffer* b )
142{
143 this->beeper_output = b;
144 if ( b && !this->cpc_mode )
145 this->beeper_mask = 0x10;
146 else
147 disable_beeper( this );
148}
149
150static void set_voice( struct Ay_Emu *this, int i, struct Blip_Buffer* center )
151{
152 if ( i >= ay_osc_count )
153 set_beeper_output( this, center );
154 else
155 Ay_apu_set_output( &this->apu, i, center );
156}
157
158static blargg_err_t run_clocks( struct Ay_Emu *this, blip_time_t* duration, int msec )
159{
160#if defined(ROCKBOX)
161 (void) msec;
162#endif
163
164 cpu_time_t *end = duration;
165 struct Z80_Cpu* cpu = &this->cpu;
166 Z80_set_time( cpu, 0 );
167
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;
173
174 while ( Z80_time( cpu ) < *end )
175 {
176 run_cpu( this, min( *end, this->next_play ) );
177
178 if ( Z80_time( cpu ) >= this->next_play )
179 {
180 // next frame
181 this->next_play += this->play_period;
182
183 if ( cpu->r.iff1 )
184 {
185 // interrupt enabled
186
187 if ( this->mem.ram [cpu->r.pc] == 0x76 )
188 cpu->r.pc++; // advance past HALT instruction
189
190 cpu->r.iff1 = 0;
191 cpu->r.iff2 = 0;
192
193 this->mem.ram [--cpu->r.sp] = (byte) (cpu->r.pc >> 8);
194 this->mem.ram [--cpu->r.sp] = (byte) (cpu->r.pc);
195
196 // fixed interrupt
197 cpu->r.pc = 0x38;
198 Z80_adjust_time( cpu, 12 );
199
200 if ( cpu->r.im == 2 )
201 {
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 );
206 }
207 }
208 }
209 }
210
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;
218}
219
220// Emulation
221
222void cpu_out_( struct Ay_Emu *this, cpu_time_t time, addr_t addr, int data )
223{
224 // Spectrum
225 if ( !this->cpc_mode )
226 {
227 switch ( addr & 0xFEFF )
228 {
229 case 0xFEFD:
230 this->spectrum_mode = true;
231 Ay_apu_write_addr( &this->apu, data );
232 return;
233
234 case 0xBEFD:
235 this->spectrum_mode = true;
236 Ay_apu_write_data( &this->apu, time, data );
237 return;
238 }
239 }
240
241 // CPC
242 if ( !this->spectrum_mode )
243 {
244 switch ( addr >> 8 )
245 {
246 case 0xF6:
247 switch ( data & 0xC0 )
248 {
249 case 0xC0:
250 Ay_apu_write_addr( &this->apu, this->cpc_latch );
251 goto enable_cpc;
252
253 case 0x80:
254 Ay_apu_write_data( &this->apu, time, this->cpc_latch );
255 goto enable_cpc;
256 }
257 break;
258
259 case 0xF4:
260 this->cpc_latch = data;
261 goto enable_cpc;
262 }
263 }
264
265 /* dprintf( "Unmapped OUT: $%04X <- $%02X\n", addr, data ); */
266 return;
267
268enable_cpc:
269 if ( !this->cpc_mode )
270 {
271 this->cpc_mode = true;
272 disable_beeper( this );
273
274 change_clock_rate( this, cpc_clock );
275 Sound_set_tempo( this, this->tempo );
276 }
277}
278
279blargg_err_t Ay_set_sample_rate( struct Ay_Emu *this, int rate )
280{
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 ) );
284
285 // Set buffer bass
286 Buffer_bass_freq( &this->stereo_buf, 160 );
287
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;
292}
293
294void Sound_mute_voice( struct Ay_Emu *this, int index, bool mute )
295{
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 );
302}
303
304void Sound_mute_voices( struct Ay_Emu *this, int mask )
305{
306 require( this->sample_rate ); // sample rate must be set first
307 this->mute_mask_ = mask;
308
309 int i;
310 for ( i = this->voice_count; i--; )
311 {
312 if ( mask & (1 << i) )
313 {
314 set_voice( this, i, 0 );
315 }
316 else
317 {
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 );
322 }
323 }
324}
325
326void Sound_set_tempo( struct Ay_Emu *this, int t )
327{
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;
334
335 int p = spectrum_period;
336 if ( this->clock_rate_ != spectrum_clock )
337 p = this->clock_rate_ / 50;
338
339 this->play_period = (blip_time_t) ((p * FP_ONE_TEMPO) / t);
340}
341
342blargg_err_t Ay_start_track( struct Ay_Emu *this, int track )
343{
344 clear_track_vars( this );
345
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;
350 }
351
352 this->current_track = track;
353 Buffer_clear( &this->stereo_buf );
354
355 byte* const mem = this->mem.ram;
356
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 );
360
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";
365
366 byte const* const more_data = get_data( &this->file, data + 10, 6 );
367 if ( !more_data )
368 return "file data missing";
369
370 byte const* blocks = get_data( &this->file, data + 12, 8 );
371 if ( !blocks )
372 return "file data missing";
373
374 // initial addresses
375 unsigned addr = get_be16( blocks );
376 if ( !addr )
377 return "file data missing";
378
379 unsigned init = get_be16( more_data + 2 );
380 if ( !init )
381 init = addr;
382
383 // copy blocks into memory
384 do
385 {
386 blocks += 2;
387 unsigned len = get_be16( blocks ); blocks += 2;
388 if ( addr + len > mem_size )
389 {
390 /* warning( "Bad data block size" ); */
391 len = mem_size - addr;
392 }
393 check( len );
394 byte const* in = get_data( &this->file, blocks, 0 ); blocks += 2;
395 if ( len > (unsigned) (this->file.end - in) )
396 {
397 /* warning( "File data missing" ); */
398 len = this->file.end - in;
399 }
400
401 memcpy( mem + addr, in, len );
402
403 if ( this->file.end - blocks < 8 )
404 {
405 /* warning( "File data missing" ); */
406 break;
407 }
408 }
409 while ( (addr = get_be16( blocks )) != 0 );
410
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
419 };
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
428 };
429 memcpy( mem, passive, sizeof passive );
430 int const play_addr = get_be16( more_data + 4 );
431 if ( play_addr )
432 {
433 memcpy( mem, active, sizeof active );
434 mem [ 9] = play_addr;
435 mem [10] = play_addr >> 8;
436 }
437 mem [2] = init;
438 mem [3] = init >> 8;
439
440 mem [0x38] = 0xFB; // Put EI at interrupt vector (followed by RET)
441
442 // start at spectrum speed
443 change_clock_rate( this, spectrum_clock );
444 Sound_set_tempo( this, this->tempo );
445
446 struct registers_t r;
447 memset( &r, 0, sizeof(struct registers_t) );
448
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;
454
455 memset( this->mem.padding1, 0xFF, sizeof this->mem.padding1 );
456
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)
460
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;
464
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 );
473
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 );
477
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 );
485
486 return track_start( &this->track_filter );
487}
488
489// Tell/Seek
490
491static int msec_to_samples( int msec, int sample_rate )
492{
493 int sec = msec / 1000;
494 msec -= sec * 1000;
495 return (sec * sample_rate + msec * sample_rate / 1000) * stereo;
496}
497
498int Track_tell( struct Ay_Emu *this )
499{
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;
503}
504
505blargg_err_t Track_seek( struct Ay_Emu *this, int msec )
506{
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 ) );
511}
512
513blargg_err_t skip_( void *emu, int count )
514{
515 struct Ay_Emu* this = (struct Ay_Emu*) emu;
516
517 // for long skip, mute sound
518 const int threshold = 32768;
519 if ( count > threshold )
520 {
521 int saved_mute = this->mute_mask_;
522 Sound_mute_voices( this, ~0 );
523
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 ) );
528
529 Sound_mute_voices( this, saved_mute );
530 }
531
532 return skippy_( &this->track_filter, count );
533}
534
535blargg_err_t Track_skip( struct Ay_Emu *this, int count )
536{
537 require( this->current_track >= 0 ); // start_track() must have been called already
538 return track_skip( &this->track_filter, count );
539}
540
541int Track_get_length( struct Ay_Emu* this, int n )
542{
543 int length = 0;
544
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
548
549 if ( (this->m3u.size > 0) && (n < this->m3u.size) ) {
550 struct entry_t* entry = &this->m3u.entries [n];
551 length = entry->length;
552 }
553
554 if ( length <= 0 )
555 length = 120 * 1000; /* 2 minutes */
556
557 return length;
558}
559
560void Track_set_fade( struct Ay_Emu *this, int start_msec, int length_msec )
561{
562 track_set_fade( &this->track_filter, msec_to_samples( start_msec, this->sample_rate ),
563 length_msec * this->sample_rate / (1000 / stereo) );
564}
565
566blargg_err_t Ay_play( struct Ay_Emu *this, int out_count, sample_t* out )
567{
568 require( this->current_track >= 0 );
569 require( out_count % stereo == 0 );
570 return track_play( &this->track_filter, out_count, out );
571}
572
573blargg_err_t play_( void *emu, int count, sample_t* out )
574{
575 struct Ay_Emu* this = (struct Ay_Emu*) emu;
576
577 int remain = count;
578 while ( remain )
579 {
580 Buffer_disable_immediate_removal( &this->stereo_buf );
581 remain -= Buffer_read_samples( &this->stereo_buf, &out [count - remain], remain );
582 if ( remain )
583 {
584 if ( this->buf_changed_count != Buffer_channels_changed_count( &this->stereo_buf ) )
585 {
586 this->buf_changed_count = Buffer_channels_changed_count( &this->stereo_buf );
587
588 // Remute voices
589 Sound_mute_voices( this, this->mute_mask_ );
590 }
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 );
596 }
597 }
598 return 0;
599}