1
// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/
5
#include "blargg_endian.h"
8
/* Copyright (C) 2006 Shay Green. This module is free software; you
9
can redistribute it and/or modify it under the terms of the GNU Lesser
10
General Public License as published by the Free Software Foundation; either
11
version 2.1 of the License, or (at your option) any later version. This
12
module is distributed in the hope that it will be useful, but WITHOUT ANY
13
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
15
details. You should have received a copy of the GNU Lesser General Public
16
License along with this module; if not, write to the Free Software Foundation,
17
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
19
#include "blargg_source.h"
21
long const base_scanline_period = 114;
25
set_type( gme_sap_type );
27
static const char* const names [Sap_Apu::osc_count * 2] = {
28
"Wave 1", "Wave 2", "Wave 3", "Wave 4",
29
"Wave 5", "Wave 6", "Wave 7", "Wave 8",
31
set_voice_names( names );
33
static int const types [Sap_Apu::osc_count * 2] = {
34
wave_type | 1, wave_type | 2, wave_type | 3, wave_type | 0,
35
wave_type | 5, wave_type | 6, wave_type | 7, wave_type | 4,
37
set_voice_types( types );
38
set_silence_lookahead( 6 );
41
Sap_Emu::~Sap_Emu() { }
45
// Returns 16 or greater if not hex
46
inline int from_hex_char( int h )
49
if ( (unsigned) h > 9 )
50
h = ((h - 0x11) & 0xDF) + 10;
54
static long from_hex( byte const* in )
57
for ( int n = 4; n--; )
59
int h = from_hex_char( *in++ );
62
result = result * 0x10 + h;
67
static int from_dec( byte const* in, byte const* end )
75
int dig = *in++ - '0';
76
if ( (unsigned) dig > 9 )
83
static void parse_string( byte const* in, byte const* end, int len, char* out )
85
byte const* start = in;
89
while ( in < end && *in != '\"' )
96
len = min( len - 1, int (in - start) );
98
memcpy( out, start, len );
101
static blargg_err_t parse_info( byte const* in, long size, Sap_Emu::info_t* out )
103
out->track_count = 1;
106
out->copyright [0] = 0;
108
if ( size < 16 || memcmp( in, "SAP\x0D\x0A", 5 ) )
109
return gme_wrong_file_type;
111
byte const* file_end = in + size - 5;
113
while ( in < file_end && (in [0] != 0xFF || in [1] != 0xFF) )
115
byte const* line_end = in;
116
while ( line_end < file_end && *line_end != 0x0D )
119
char const* tag = (char const*) in;
120
while ( in < line_end && *in > ' ' )
122
int tag_len = (char const*) in - tag;
124
while ( in < line_end && *in <= ' ' ) in++;
130
else if ( !strncmp( "INIT", tag, tag_len ) )
132
out->init_addr = from_hex( in );
133
if ( (unsigned long) out->init_addr > 0xFFFF )
134
return "Invalid init address";
136
else if ( !strncmp( "PLAYER", tag, tag_len ) )
138
out->play_addr = from_hex( in );
139
if ( (unsigned long) out->play_addr > 0xFFFF )
140
return "Invalid play address";
142
else if ( !strncmp( "MUSIC", tag, tag_len ) )
144
out->music_addr = from_hex( in );
145
if ( (unsigned long) out->music_addr > 0xFFFF )
146
return "Invalid music address";
148
else if ( !strncmp( "SONGS", tag, tag_len ) )
150
out->track_count = from_dec( in, line_end );
151
if ( out->track_count <= 0 )
152
return "Invalid track count";
154
else if ( !strncmp( "TYPE", tag, tag_len ) )
156
switch ( out->type = *in )
163
return "Digimusic not supported";
166
return "Unsupported player type";
169
else if ( !strncmp( "STEREO", tag, tag_len ) )
173
else if ( !strncmp( "FASTPLAY", tag, tag_len ) )
175
out->fastplay = from_dec( in, line_end );
176
if ( out->fastplay <= 0 )
177
return "Invalid fastplay value";
179
else if ( !strncmp( "AUTHOR", tag, tag_len ) )
181
parse_string( in, line_end, sizeof out->author, out->author );
183
else if ( !strncmp( "NAME", tag, tag_len ) )
185
parse_string( in, line_end, sizeof out->name, out->name );
187
else if ( !strncmp( "DATE", tag, tag_len ) )
189
parse_string( in, line_end, sizeof out->copyright, out->copyright );
195
if ( in [0] != 0xFF || in [1] != 0xFF )
196
return "ROM data missing";
197
out->rom_data = in + 2;
202
static void copy_sap_fields( Sap_Emu::info_t const& in, track_info_t* out )
204
Gme_File::copy_field_( out->game, in.name );
205
Gme_File::copy_field_( out->author, in.author );
206
Gme_File::copy_field_( out->copyright, in.copyright );
209
blargg_err_t Sap_Emu::track_info_( track_info_t* out, int ) const
211
copy_sap_fields( info, out );
215
struct Sap_File : Gme_Info_
217
Sap_Emu::info_t info;
219
Sap_File() { set_type( gme_sap_type ); }
221
blargg_err_t load_mem_( byte const* begin, long size )
223
RETURN_ERR( parse_info( begin, size, &info ) );
224
set_track_count( info.track_count );
228
blargg_err_t track_info_( track_info_t* out, int ) const
230
copy_sap_fields( info, out );
235
static Music_Emu* new_sap_emu () { return BLARGG_NEW Sap_Emu ; }
236
static Music_Emu* new_sap_file() { return BLARGG_NEW Sap_File; }
238
gme_type_t_ const gme_sap_type [1] = { "Atari XL", 0, &new_sap_emu, &new_sap_file, "SAP", 1 };
242
blargg_err_t Sap_Emu::load_mem_( byte const* in, long size )
244
file_end = in + size;
251
info.music_addr = -1;
253
RETURN_ERR( parse_info( in, size, &info ) );
255
set_warning( info.warning );
256
set_track_count( info.track_count );
257
set_voice_count( Sap_Apu::osc_count << info.stereo );
258
apu_impl.volume( gain() );
260
return setup_buffer( 1773447 );
263
void Sap_Emu::update_eq( blip_eq_t const& eq )
265
apu_impl.synth.treble_eq( eq );
268
void Sap_Emu::set_voice( int i, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right )
270
int i2 = i - Sap_Apu::osc_count;
272
apu2.osc_output( i2, right );
274
apu.osc_output( i, (info.stereo ? left : center) );
279
void Sap_Emu::set_tempo_( double t )
281
scanline_period = sap_time_t (base_scanline_period / t);
284
inline sap_time_t Sap_Emu::play_period() const { return info.fastplay * scanline_period; }
286
void Sap_Emu::cpu_jsr( sap_addr_t addr )
288
check( r.sp >= 0xFE ); // catch anything trying to leave data on stack
290
int high_byte = (idle_addr - 1) >> 8;
291
if ( r.sp == 0xFE && mem.ram [0x1FF] == high_byte )
292
r.sp = 0xFF; // pop extra byte off
293
mem.ram [0x100 + r.sp--] = high_byte; // some routines use RTI to return
294
mem.ram [0x100 + r.sp--] = high_byte;
295
mem.ram [0x100 + r.sp--] = (idle_addr - 1) & 0xFF;
298
void Sap_Emu::run_routine( sap_addr_t addr )
301
cpu::run( 312 * base_scanline_period * 60 );
302
check( r.pc == idle_addr );
305
inline void Sap_Emu::call_init( int track )
311
run_routine( info.init_addr );
316
r.x = info.music_addr&0xFF;
317
r.y = info.music_addr >> 8;
318
run_routine( info.play_addr + 3 );
321
run_routine( info.play_addr + 3 );
326
blargg_err_t Sap_Emu::start_track_( int track )
328
RETURN_ERR( Classic_Emu::start_track_( track ) );
330
memset( &mem, 0, sizeof mem );
332
byte const* in = info.rom_data;
333
while ( file_end - in >= 5 )
335
unsigned start = get_le16( in );
336
unsigned end = get_le16( in + 2 );
337
//dprintf( "Block $%04X-$%04X\n", start, end );
341
set_warning( "Invalid file data block" );
344
long len = end - start + 1;
345
if ( len > file_end - in )
347
set_warning( "Invalid file data block" );
351
memcpy( mem.ram + start, in, len );
353
if ( file_end - in >= 2 && in [0] == 0xFF && in [1] == 0xFF )
357
apu.reset( &apu_impl );
358
apu2.reset( &apu_impl );
359
cpu::reset( mem.ram );
360
time_mask = 0; // disables sound during init
364
next_play = play_period();
371
// see sap_cpu_io.h for read/write functions
373
void Sap_Emu::cpu_write_( sap_addr_t addr, int data )
375
if ( (addr ^ Sap_Apu::start_addr) <= (Sap_Apu::end_addr - Sap_Apu::start_addr) )
377
GME_APU_HOOK( this, addr - Sap_Apu::start_addr, data );
378
apu.write_data( time() & time_mask, addr, data );
382
if ( (addr ^ (Sap_Apu::start_addr + 0x10)) <= (Sap_Apu::end_addr - Sap_Apu::start_addr) &&
385
GME_APU_HOOK( this, addr - 0x10 - Sap_Apu::start_addr + 10, data );
386
apu2.write_data( time() & time_mask, addr ^ 0x10, data );
390
if ( (addr & ~0x0010) != 0xD20F || data != 0x03 )
391
dprintf( "Unmapped write $%04X <- $%02X\n", addr, data );
394
inline void Sap_Emu::call_play()
399
cpu_jsr( info.play_addr );
403
cpu_jsr( info.play_addr + 6 );
408
blargg_err_t Sap_Emu::run_clocks( blip_time_t& duration, int )
411
while ( time() < duration )
413
if ( cpu::run( duration ) || r.pc > idle_addr )
414
return "Emulation error (illegal instruction)";
416
if ( r.pc == idle_addr )
418
if ( next_play <= duration )
420
set_time( next_play );
421
next_play += play_period();
423
GME_FRAME_HOOK( this );
427
set_time( duration );
433
next_play -= duration;
434
check( next_play >= 0 );
437
apu.end_frame( duration );
439
apu2.end_frame( duration );