~ubuntu-branches/ubuntu/wily/xmms2/wily

« back to all changes in this revision

Viewing changes to src/plugins/gme/gme/Sap_Emu.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Benjamin Drung
  • Date: 2008-07-04 16:23:34 UTC
  • mfrom: (1.1.5 upstream) (6.1.1 lenny)
  • Revision ID: james.westby@ubuntu.com-20080704162334-b3esbkcapt8wbrk4
Tags: 0.5DrLecter-2ubuntu1
* Merge from debian unstable (LP: #241098), remaining changes:
  + debian/control:
    + Update Maintainer field
    + add lpia to xmms2-plugin-alsa supported architectures
    + Added liba52-0.7.4-dev to build depends
  + debian/rules: Added patch, patch-stamp and unpatch
  + changed 01_gcc4.3.patch:
    + src/include/xmmsclient/xmmsclient++/helpers.h: Added #include <climits>
* New upstream relase fixes LP: #212566, #222341

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Game_Music_Emu 0.5.2. 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 blargg_err_t parse_info( byte const* in, long size, Sap_Emu::info_t* out )
 
102
{
 
103
        out->track_count   = 1;
 
104
        out->author    [0] = 0;
 
105
        out->name      [0] = 0;
 
106
        out->copyright [0] = 0;
 
107
        
 
108
        if ( size < 16 || memcmp( in, "SAP\x0D\x0A", 5 ) )
 
109
                return gme_wrong_file_type;
 
110
        
 
111
        byte const* file_end = in + size - 5;
 
112
        in += 5;
 
113
        while ( in < file_end && (in [0] != 0xFF || in [1] != 0xFF) )
 
114
        {
 
115
                byte const* line_end = in;
 
116
                while ( line_end < file_end && *line_end != 0x0D )
 
117
                        line_end++;
 
118
                
 
119
                char const* tag = (char const*) in;
 
120
                while ( in < line_end && *in > ' ' )
 
121
                        in++;
 
122
                int tag_len = (char const*) in - tag;
 
123
                
 
124
                while ( in < line_end && *in <= ' ' ) in++;
 
125
                
 
126
                if ( tag_len <= 0 )
 
127
                {
 
128
                        // skip line
 
129
                }
 
130
                else if ( !strncmp( "INIT", tag, tag_len ) )
 
131
                {
 
132
                        out->init_addr = from_hex( in );
 
133
                        if ( (unsigned long) out->init_addr > 0xFFFF )
 
134
                                return "Invalid init address";
 
135
                }
 
136
                else if ( !strncmp( "PLAYER", tag, tag_len ) )
 
137
                {
 
138
                        out->play_addr = from_hex( in );
 
139
                        if ( (unsigned long) out->play_addr > 0xFFFF )
 
140
                                return "Invalid play address";
 
141
                }
 
142
                else if ( !strncmp( "MUSIC", tag, tag_len ) )
 
143
                {
 
144
                        out->music_addr = from_hex( in );
 
145
                        if ( (unsigned long) out->music_addr > 0xFFFF )
 
146
                                return "Invalid music address";
 
147
                }
 
148
                else if ( !strncmp( "SONGS", tag, tag_len ) )
 
149
                {
 
150
                        out->track_count = from_dec( in, line_end );
 
151
                        if ( out->track_count <= 0 )
 
152
                                return "Invalid track count";
 
153
                }
 
154
                else if ( !strncmp( "TYPE", tag, tag_len ) )
 
155
                {
 
156
                        switch ( out->type = *in )
 
157
                        {
 
158
                        case 'C':
 
159
                        case 'B':
 
160
                                break;
 
161
                        
 
162
                        case 'D':
 
163
                                return "Digimusic not supported";
 
164
                        
 
165
                        default:
 
166
                                return "Unsupported player type";
 
167
                        }
 
168
                }
 
169
                else if ( !strncmp( "STEREO", tag, tag_len ) )
 
170
                {
 
171
                        out->stereo = true;
 
172
                }
 
173
                else if ( !strncmp( "FASTPLAY", tag, tag_len ) )
 
174
                {
 
175
                        out->fastplay = from_dec( in, line_end );
 
176
                        if ( out->fastplay <= 0 )
 
177
                                return "Invalid fastplay value";
 
178
                }
 
179
                else if ( !strncmp( "AUTHOR", tag, tag_len ) )
 
180
                {
 
181
                        parse_string( in, line_end, sizeof out->author, out->author );
 
182
                }
 
183
                else if ( !strncmp( "NAME", tag, tag_len ) )
 
184
                {
 
185
                        parse_string( in, line_end, sizeof out->name, out->name );
 
186
                }
 
187
                else if ( !strncmp( "DATE", tag, tag_len ) )
 
188
                {
 
189
                        parse_string( in, line_end, sizeof out->copyright, out->copyright );
 
190
                }
 
191
                
 
192
                in = line_end + 2;
 
193
        }
 
194
        
 
195
        if ( in [0] != 0xFF || in [1] != 0xFF )
 
196
                return "ROM data missing";
 
197
        out->rom_data = in + 2;
 
198
        
 
199
        return 0;
 
200
}
 
201
 
 
202
static void copy_sap_fields( Sap_Emu::info_t const& in, track_info_t* out )
 
203
{
 
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 );
 
207
}
 
208
 
 
209
blargg_err_t Sap_Emu::track_info_( track_info_t* out, int ) const
 
210
{
 
211
        copy_sap_fields( info, out );
 
212
        return 0;
 
213
}
 
214
 
 
215
struct Sap_File : Gme_Info_
 
216
{
 
217
        Sap_Emu::info_t info;
 
218
        
 
219
        Sap_File() { set_type( gme_sap_type ); }
 
220
        
 
221
        blargg_err_t load_mem_( byte const* begin, long size )
 
222
        {
 
223
                RETURN_ERR( parse_info( begin, size, &info ) );
 
224
                set_track_count( info.track_count );
 
225
                return 0;
 
226
        }
 
227
        
 
228
        blargg_err_t track_info_( track_info_t* out, int ) const
 
229
        {
 
230
                copy_sap_fields( info, out );
 
231
                return 0;
 
232
        }
 
233
};
 
234
 
 
235
static Music_Emu* new_sap_emu () { return BLARGG_NEW Sap_Emu ; }
 
236
static Music_Emu* new_sap_file() { return BLARGG_NEW Sap_File; }
 
237
 
 
238
gme_type_t_ const gme_sap_type [1] = { "Atari XL", 0, &new_sap_emu, &new_sap_file, "SAP", 1 };
 
239
 
 
240
// Setup
 
241
 
 
242
blargg_err_t Sap_Emu::load_mem_( byte const* in, long size )
 
243
{
 
244
        file_end = in + size;
 
245
        
 
246
        info.warning    = 0;
 
247
        info.type       = 'B';
 
248
        info.stereo     = false;
 
249
        info.init_addr  = -1;
 
250
        info.play_addr  = -1;
 
251
        info.music_addr = -1;
 
252
        info.fastplay   = 312;
 
253
        RETURN_ERR( parse_info( in, size, &info ) );
 
254
        
 
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() );
 
259
        
 
260
        return setup_buffer( 1773447 );
 
261
}
 
262
 
 
263
void Sap_Emu::update_eq( blip_eq_t const& eq )
 
264
{
 
265
        apu_impl.synth.treble_eq( eq );
 
266
}
 
267
 
 
268
void Sap_Emu::set_voice( int i, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right )
 
269
{
 
270
        int i2 = i - Sap_Apu::osc_count;
 
271
        if ( i2 >= 0 )
 
272
                apu2.osc_output( i2, right );
 
273
        else
 
274
                apu.osc_output( i, (info.stereo ? left : center) );
 
275
}
 
276
 
 
277
// Emulation
 
278
 
 
279
void Sap_Emu::set_tempo_( double t )
 
280
{
 
281
        scanline_period = sap_time_t (base_scanline_period / t);
 
282
}
 
283
 
 
284
inline sap_time_t Sap_Emu::play_period() const { return info.fastplay * scanline_period; }
 
285
 
 
286
void Sap_Emu::cpu_jsr( sap_addr_t addr )
 
287
{
 
288
        check( r.sp >= 0xFE ); // catch anything trying to leave data on stack
 
289
        r.pc = addr;
 
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;
 
296
}
 
297
 
 
298
void Sap_Emu::run_routine( sap_addr_t addr )
 
299
{
 
300
        cpu_jsr( addr );
 
301
        cpu::run( 312 * base_scanline_period * 60 );
 
302
        check( r.pc == idle_addr );
 
303
}
 
304
 
 
305
inline void Sap_Emu::call_init( int track )
 
306
{
 
307
        switch ( info.type )
 
308
        {
 
309
        case 'B':
 
310
                r.a = track;
 
311
                run_routine( info.init_addr );
 
312
                break;
 
313
        
 
314
        case 'C':
 
315
                r.a = 0x70;
 
316
                r.x = info.music_addr&0xFF;
 
317
                r.y = info.music_addr >> 8;
 
318
                run_routine( info.play_addr + 3 );
 
319
                r.a = 0;
 
320
                r.x = track;
 
321
                run_routine( info.play_addr + 3 );
 
322
                break;
 
323
        }
 
324
}
 
325
 
 
326
blargg_err_t Sap_Emu::start_track_( int track )
 
327
{
 
328
        RETURN_ERR( Classic_Emu::start_track_( track ) );
 
329
        
 
330
        memset( &mem, 0, sizeof mem );
 
331
 
 
332
        byte const* in = info.rom_data;
 
333
        while ( file_end - in >= 5 )
 
334
        {
 
335
                unsigned start = get_le16( in );
 
336
                unsigned end   = get_le16( in + 2 );
 
337
                //dprintf( "Block $%04X-$%04X\n", start, end );
 
338
                in += 4;
 
339
                if ( end < start )
 
340
                {
 
341
                        set_warning( "Invalid file data block" );
 
342
                        break;
 
343
                }
 
344
                long len = end - start + 1;
 
345
                if ( len > file_end - in )
 
346
                {
 
347
                        set_warning( "Invalid file data block" );
 
348
                        break;
 
349
                }
 
350
                
 
351
                memcpy( mem.ram + start, in, len );
 
352
                in += len;
 
353
                if ( file_end - in >= 2 && in [0] == 0xFF && in [1] == 0xFF )
 
354
                        in += 2;
 
355
        }
 
356
        
 
357
        apu.reset( &apu_impl );
 
358
        apu2.reset( &apu_impl );
 
359
        cpu::reset( mem.ram );
 
360
        time_mask = 0; // disables sound during init
 
361
        call_init( track );
 
362
        time_mask = -1;
 
363
        
 
364
        next_play = play_period();
 
365
        
 
366
        return 0;
 
367
}
 
368
 
 
369
// Emulation
 
370
 
 
371
// see sap_cpu_io.h for read/write functions
 
372
 
 
373
void Sap_Emu::cpu_write_( sap_addr_t addr, int data )
 
374
{
 
375
        if ( (addr ^ Sap_Apu::start_addr) <= (Sap_Apu::end_addr - Sap_Apu::start_addr) )
 
376
        {
 
377
                GME_APU_HOOK( this, addr - Sap_Apu::start_addr, data );
 
378
                apu.write_data( time() & time_mask, addr, data );
 
379
                return;
 
380
        }
 
381
        
 
382
        if ( (addr ^ (Sap_Apu::start_addr + 0x10)) <= (Sap_Apu::end_addr - Sap_Apu::start_addr) &&
 
383
                        info.stereo )
 
384
        {
 
385
                GME_APU_HOOK( this, addr - 0x10 - Sap_Apu::start_addr + 10, data );
 
386
                apu2.write_data( time() & time_mask, addr ^ 0x10, data );
 
387
                return;
 
388
        }
 
389
 
 
390
        if ( (addr & ~0x0010) != 0xD20F || data != 0x03 )
 
391
                dprintf( "Unmapped write $%04X <- $%02X\n", addr, data );
 
392
}
 
393
 
 
394
inline void Sap_Emu::call_play()
 
395
{
 
396
        switch ( info.type )
 
397
        {
 
398
        case 'B':
 
399
                cpu_jsr( info.play_addr );
 
400
                break;
 
401
        
 
402
        case 'C':
 
403
                cpu_jsr( info.play_addr + 6 );
 
404
                break;
 
405
        }
 
406
}
 
407
 
 
408
blargg_err_t Sap_Emu::run_clocks( blip_time_t& duration, int )
 
409
{
 
410
        set_time( 0 );
 
411
        while ( time() < duration )
 
412
        {
 
413
                if ( cpu::run( duration ) || r.pc > idle_addr )
 
414
                        return "Emulation error (illegal instruction)";
 
415
                
 
416
                if ( r.pc == idle_addr )
 
417
                {
 
418
                        if ( next_play <= duration )
 
419
                        {
 
420
                                set_time( next_play );
 
421
                                next_play += play_period();
 
422
                                call_play();
 
423
                                GME_FRAME_HOOK( this );
 
424
                        }
 
425
                        else
 
426
                        {
 
427
                                set_time( duration );
 
428
                        }
 
429
                }
 
430
        }
 
431
        
 
432
        duration = time();
 
433
        next_play -= duration;
 
434
        check( next_play >= 0 );
 
435
        if ( next_play < 0 )
 
436
                next_play = 0;
 
437
        apu.end_frame( duration );
 
438
        if ( info.stereo )
 
439
                apu2.end_frame( duration );
 
440
        
 
441
        return 0;
 
442
}