"Fossies" - the Fresh Open Source Software Archive

Member "audacious-plugins-3.10.1/src/console/Sap_Emu.cc" (26 Dec 2018, 11659 Bytes) of package /linux/misc/audacious-plugins-3.10.1.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "Sap_Emu.cc" see the Fossies "Dox" file reference documentation.

    1 // Game_Music_Emu 0.5.5. http://www.slack.net/~ant/
    2 
    3 #include "Sap_Emu.h"
    4 
    5 #include "blargg_endian.h"
    6 #include <string.h>
    7 
    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 */
   18 
   19 #include "blargg_source.h"
   20 
   21 long const base_scanline_period = 114;
   22 
   23 Sap_Emu::Sap_Emu()
   24 {
   25     set_type( gme_sap_type );
   26 
   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",
   30     };
   31     set_voice_names( names );
   32 
   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,
   36     };
   37     set_voice_types( types );
   38     set_silence_lookahead( 6 );
   39 }
   40 
   41 Sap_Emu::~Sap_Emu() { }
   42 
   43 // Track info
   44 
   45 // Returns 16 or greater if not hex
   46 inline int from_hex_char( int h )
   47 {
   48     h -= 0x30;
   49     if ( (unsigned) h > 9 )
   50         h = ((h - 0x11) & 0xDF) + 10;
   51     return h;
   52 }
   53 
   54 static long from_hex( byte const* in )
   55 {
   56     unsigned result = 0;
   57     for ( int n = 4; n--; )
   58     {
   59         int h = from_hex_char( *in++ );
   60         if ( h > 15 )
   61             return -1;
   62         result = result * 0x10 + h;
   63     }
   64     return result;
   65 }
   66 
   67 static int from_dec( byte const* in, byte const* end )
   68 {
   69     if ( in >= end )
   70         return -1;
   71 
   72     int n = 0;
   73     while ( in < end )
   74     {
   75         int dig = *in++ - '0';
   76         if ( (unsigned) dig > 9 )
   77             return -1;
   78         n = n * 10 + dig;
   79     }
   80     return n;
   81 }
   82 
   83 static void parse_string( byte const* in, byte const* end, int len, char* out )
   84 {
   85     byte const* start = in;
   86     if ( *in++ == '\"' )
   87     {
   88         start++;
   89         while ( in < end && *in != '\"' )
   90             in++;
   91     }
   92     else
   93     {
   94         in = end;
   95     }
   96     len = min( len - 1, int (in - start) );
   97     out [len] = 0;
   98     memcpy( out, start, len );
   99 }
  100 
  101 static byte const* parse_int_( byte const* in, int* out )
  102 {
  103     int n = 0;
  104     while ( 1 )
  105     {
  106         unsigned d = from_dec( in, in + 1 );
  107         if ( d > 9 )
  108             break;
  109         in++;
  110         n = n * 10 + d;
  111         *out = n;
  112     }
  113     return in;
  114 }
  115 
  116 static byte const* parse_time( byte const* in, int* out )
  117 {
  118     *out = -1;
  119     int n = -1;
  120     in = parse_int_( in, &n );
  121     if ( n >= 0 )
  122     {
  123         *out = n;
  124         if ( *in == ':' )
  125         {
  126             n = -1;
  127             in = parse_int_( in + 1, &n );
  128             if ( n >= 0 )
  129                 *out = *out * 60 + n;
  130         }
  131     }
  132     *out = *out * 1000;
  133     return in;
  134 }
  135 
  136 static blargg_err_t parse_info( byte const* in, long size, Sap_Emu::info_t* out )
  137 {
  138     out->track_count   = 1;
  139     out->author    [0] = 0;
  140     out->name      [0] = 0;
  141     out->copyright [0] = 0;
  142 
  143     if ( size < 16 || memcmp( in, "SAP\x0D\x0A", 5 ) )
  144         return gme_wrong_file_type;
  145 
  146     byte const* file_end = in + size - 5;
  147     in += 5;
  148     while ( in < file_end && (in [0] != 0xFF || in [1] != 0xFF) )
  149     {
  150         byte const* line_end = in;
  151         while ( line_end < file_end && *line_end != 0x0D )
  152             line_end++;
  153 
  154         char const* tag = (char const*) in;
  155         while ( in < line_end && *in > ' ' )
  156             in++;
  157         int tag_len = (char const*) in - tag;
  158 
  159         while ( in < line_end && *in <= ' ' ) in++;
  160 
  161         if ( tag_len <= 0 )
  162         {
  163             // skip line
  164         }
  165         else if ( !strncmp( "INIT", tag, tag_len ) )
  166         {
  167             out->init_addr = from_hex( in );
  168             if ( (unsigned long) out->init_addr > 0xFFFF )
  169                 return "Invalid init address";
  170         }
  171         else if ( !strncmp( "PLAYER", tag, tag_len ) )
  172         {
  173             out->play_addr = from_hex( in );
  174             if ( (unsigned long) out->play_addr > 0xFFFF )
  175                 return "Invalid play address";
  176         }
  177         else if ( !strncmp( "MUSIC", tag, tag_len ) )
  178         {
  179             out->music_addr = from_hex( in );
  180             if ( (unsigned long) out->music_addr > 0xFFFF )
  181                 return "Invalid music address";
  182         }
  183         else if ( !strncmp( "SONGS", tag, tag_len ) )
  184         {
  185             out->track_count = from_dec( in, line_end );
  186             if ( out->track_count <= 0 )
  187                 return "Invalid track count";
  188         }
  189         else if ( !strncmp( "TYPE", tag, tag_len ) )
  190         {
  191             switch ( out->type = *in )
  192             {
  193             case 'D':
  194             case 'C':
  195             case 'B':
  196                 break;
  197 
  198             default:
  199                 return "Unsupported player type";
  200             }
  201         }
  202         else if ( !strncmp( "STEREO", tag, tag_len ) )
  203         {
  204             out->stereo = true;
  205         }
  206         else if ( !strncmp( "NTSC", tag, tag_len ) )
  207         {
  208             out->ntsc = true;
  209         }
  210         else if ( !strncmp( "FASTPLAY", tag, tag_len ) )
  211         {
  212             out->fastplay = from_dec( in, line_end );
  213             if ( out->fastplay <= 0 )
  214                 return "Invalid fastplay value";
  215         }
  216         else if ( !strncmp( "AUTHOR", tag, tag_len ) )
  217         {
  218             parse_string( in, line_end, sizeof out->author, out->author );
  219         }
  220         else if ( !strncmp( "NAME", tag, tag_len ) )
  221         {
  222             parse_string( in, line_end, sizeof out->name, out->name );
  223         }
  224         else if ( !strncmp( "DATE", tag, tag_len ) )
  225         {
  226             parse_string( in, line_end, sizeof out->copyright, out->copyright );
  227         }
  228         else if ( !strncmp( "TIME", tag, tag_len ) )
  229         {
  230             parse_time( in, &out->length );
  231         }
  232 
  233         in = line_end + 2;
  234     }
  235 
  236     if ( in [0] != 0xFF || in [1] != 0xFF )
  237         return "ROM data missing";
  238     out->rom_data = in + 2;
  239 
  240     return 0;
  241 }
  242 
  243 static void copy_sap_fields( Sap_Emu::info_t const& in, track_info_t* out )
  244 {
  245     Gme_File::copy_field_( out->song,      in.name );
  246     Gme_File::copy_field_( out->author,    in.author );
  247     Gme_File::copy_field_( out->copyright, in.copyright );
  248     out->length = in.length;
  249 }
  250 
  251 blargg_err_t Sap_Emu::track_info_( track_info_t* out, int ) const
  252 {
  253     copy_sap_fields( info, out );
  254     return 0;
  255 }
  256 
  257 struct Sap_File : Gme_Info_
  258 {
  259     Sap_Emu::info_t info;
  260 
  261     Sap_File() { set_type( gme_sap_type ); }
  262 
  263     blargg_err_t load_mem_( byte const* begin, long size )
  264     {
  265         RETURN_ERR( parse_info( begin, size, &info ) );
  266         set_track_count( info.track_count );
  267         return 0;
  268     }
  269 
  270     blargg_err_t track_info_( track_info_t* out, int ) const
  271     {
  272         copy_sap_fields( info, out );
  273         return 0;
  274     }
  275 };
  276 
  277 static Music_Emu* new_sap_emu () { return BLARGG_NEW Sap_Emu ; }
  278 static Music_Emu* new_sap_file() { return BLARGG_NEW Sap_File; }
  279 
  280 static gme_type_t_ const gme_sap_type_ = { "Atari XL", 0, &new_sap_emu, &new_sap_file, "SAP", 1 };
  281 gme_type_t const gme_sap_type = &gme_sap_type_;
  282 
  283 
  284 // Setup
  285 
  286 blargg_err_t Sap_Emu::load_mem_( byte const* in, long size )
  287 {
  288     file_end = in + size;
  289 
  290     info.warning    = 0;
  291     info.type       = 'B';
  292     info.stereo     = false;
  293     info.init_addr  = -1;
  294     info.play_addr  = -1;
  295     info.music_addr = -1;
  296     info.fastplay   = 312;
  297     RETURN_ERR( parse_info( in, size, &info ) );
  298 
  299     set_warning( info.warning );
  300     set_track_count( info.track_count );
  301     set_voice_count( Sap_Apu::osc_count << info.stereo );
  302     apu_impl.volume( gain() );
  303 
  304     return setup_buffer( 1773447 );
  305 }
  306 
  307 void Sap_Emu::update_eq( blip_eq_t const& eq )
  308 {
  309     apu_impl.synth.treble_eq( eq );
  310 }
  311 
  312 void Sap_Emu::set_voice( int i, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right )
  313 {
  314     int i2 = i - Sap_Apu::osc_count;
  315     if ( i2 >= 0 )
  316         apu2.osc_output( i2, right );
  317     else
  318         apu.osc_output( i, (info.stereo ? left : center) );
  319 }
  320 
  321 // Emulation
  322 
  323 void Sap_Emu::set_tempo_( double t )
  324 {
  325     scanline_period = sap_time_t (base_scanline_period / t);
  326 }
  327 
  328 inline sap_time_t Sap_Emu::play_period() const { return info.fastplay * scanline_period; }
  329 
  330 void Sap_Emu::cpu_jsr( sap_addr_t addr )
  331 {
  332     check( r.sp >= 0xFE ); // catch anything trying to leave data on stack
  333     r.pc = addr;
  334     int high_byte = (idle_addr - 1) >> 8;
  335     if ( r.sp == 0xFE && mem.ram [0x1FF] == high_byte )
  336         r.sp = 0xFF; // pop extra byte off
  337     mem.ram [0x100 + r.sp--] = high_byte; // some routines use RTI to return
  338     mem.ram [0x100 + r.sp--] = high_byte;
  339     mem.ram [0x100 + r.sp--] = (idle_addr - 1) & 0xFF;
  340 }
  341 
  342 void Sap_Emu::run_routine( sap_addr_t addr )
  343 {
  344     cpu_jsr( addr );
  345     cpu::run( ( info.ntsc ? 262 : 312 ) * base_scanline_period * 60 );
  346     check( r.pc == idle_addr );
  347 }
  348 
  349 inline void Sap_Emu::call_init( int track )
  350 {
  351     uint8_t sp;
  352     uint16_t code_base;
  353 
  354     switch ( info.type )
  355     {
  356     case 'B':
  357         r.a = track;
  358         run_routine( info.init_addr );
  359         break;
  360 
  361     case 'C':
  362         r.a = 0x70;
  363         r.x = info.music_addr&0xFF;
  364         r.y = info.music_addr >> 8;
  365         run_routine( info.play_addr + 3 );
  366         r.a = 0;
  367         r.x = track;
  368         run_routine( info.play_addr + 3 );
  369         break;
  370 
  371     case 'D':
  372         r.a = track;
  373         r.x = 0;
  374         r.y = 0;
  375         r.sp = 0xFF;
  376         run_routine( info.init_addr );
  377 
  378         sp = r.sp;
  379 
  380         mem.ram [0x100 + sp] = r.pc >> 8;
  381         sp = (sp - 1) & 0xFF;
  382         mem.ram [0x100 + sp] = ( r.pc & 0xFF );
  383         r.sp = (sp - 1) & 0xFF;
  384 
  385         code_base = 0xd200;
  386         mem.ram [code_base] = 0x08;
  387         mem.ram [code_base + 1] = 0x48;
  388         mem.ram [code_base + 2] = 0x8a;
  389         mem.ram [code_base + 3] = 0x48;
  390         mem.ram [code_base + 4] = 0x98;
  391         mem.ram [code_base + 5] = 0x48;
  392         mem.ram [code_base + 6] = 0x20;
  393         mem.ram [code_base + 7] = code_base & 0xFF;
  394         mem.ram [code_base + 8] = (code_base >> 8);
  395         mem.ram [code_base + 9] = 0x68;
  396         mem.ram [code_base + 10] = 0xa8;
  397         mem.ram [code_base + 11] = 0x68;
  398         mem.ram [code_base + 12] = 0xaa;
  399         mem.ram [code_base + 13] = 0x68;
  400         mem.ram [code_base + 14] = 0x40;
  401         info.play_addr = code_base;
  402         break;
  403     }
  404 }
  405 
  406 blargg_err_t Sap_Emu::start_track_( int track )
  407 {
  408     RETURN_ERR( Classic_Emu::start_track_( track ) );
  409 
  410     memset( &mem, 0, sizeof mem );
  411 
  412     byte const* in = info.rom_data;
  413     while ( file_end - in >= 5 )
  414     {
  415         unsigned start = GET_LE16( in );
  416         unsigned end   = GET_LE16( in + 2 );
  417         //debug_printf( "Block $%04X-$%04X\n", start, end );
  418         in += 4;
  419         if ( end < start )
  420         {
  421             set_warning( "Invalid file data block" );
  422             break;
  423         }
  424         long len = end - start + 1;
  425         if ( len > file_end - in )
  426         {
  427             set_warning( "Invalid file data block" );
  428             break;
  429         }
  430 
  431         memcpy( mem.ram + start, in, len );
  432         in += len;
  433         if ( file_end - in >= 2 && in [0] == 0xFF && in [1] == 0xFF )
  434             in += 2;
  435     }
  436 
  437     apu.reset( &apu_impl );
  438     apu2.reset( &apu_impl );
  439     cpu::reset( mem.ram );
  440     time_mask = 0; // disables sound during init
  441     call_init( track );
  442     time_mask = -1;
  443 
  444     next_play = play_period();
  445 
  446     return 0;
  447 }
  448 
  449 // Emulation
  450 
  451 // see sap_cpu_io.h for read/write functions
  452 
  453 void Sap_Emu::cpu_write_( sap_addr_t addr, int data )
  454 {
  455     if ( (addr ^ Sap_Apu::start_addr) <= (Sap_Apu::end_addr - Sap_Apu::start_addr) )
  456     {
  457         GME_APU_HOOK( this, addr - Sap_Apu::start_addr, data );
  458         apu.write_data( time() & time_mask, addr, data );
  459         return;
  460     }
  461 
  462     if ( (addr ^ (Sap_Apu::start_addr + 0x10)) <= (Sap_Apu::end_addr - Sap_Apu::start_addr) &&
  463             info.stereo )
  464     {
  465         GME_APU_HOOK( this, addr - 0x10 - Sap_Apu::start_addr + 10, data );
  466         apu2.write_data( time() & time_mask, addr ^ 0x10, data );
  467         return;
  468     }
  469 
  470     if ( (addr & ~0x0010) != 0xD20F || data != 0x03 )
  471         debug_printf( "Unmapped write $%04X <- $%02X\n", addr, data );
  472 }
  473 
  474 inline void Sap_Emu::call_play()
  475 {
  476     switch ( info.type )
  477     {
  478     case 'D':
  479         cpu_jsr( info.play_addr );
  480         break;
  481     case 'B':
  482         cpu_jsr( info.play_addr );
  483         break;
  484 
  485     case 'C':
  486         cpu_jsr( info.play_addr + 6 );
  487         break;
  488     }
  489 }
  490 
  491 blargg_err_t Sap_Emu::run_clocks( blip_time_t& duration, int )
  492 {
  493     set_time( 0 );
  494     while ( time() < duration )
  495     {
  496         if ( cpu::run( duration ) || r.pc > idle_addr )
  497             return "Emulation error (illegal instruction)";
  498 
  499         if ( r.pc == idle_addr )
  500         {
  501             if ( next_play <= duration )
  502             {
  503                 set_time( next_play );
  504                 next_play += play_period();
  505                 call_play();
  506                 GME_FRAME_HOOK( this );
  507             }
  508             else
  509             {
  510                 set_time( duration );
  511             }
  512         }
  513     }
  514 
  515     duration = time();
  516     next_play -= duration;
  517     check( next_play >= 0 );
  518     if ( next_play < 0 )
  519         next_play = 0;
  520     apu.end_frame( duration );
  521     if ( info.stereo )
  522         apu2.end_frame( duration );
  523 
  524     return 0;
  525 }