Coverage Report

Created: 2026-07-12 08:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/vlc/contrib/contrib-build/game-music-emu/gme/Gbs_Emu.cpp
Line
Count
Source
1
// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/
2
3
#include "Gbs_Emu.h"
4
5
#include "blargg_endian.h"
6
#include <string.h>
7
8
/* Copyright (C) 2003-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
Gbs_Emu::equalizer_t const Gbs_Emu::handheld_eq   =
22
  Music_Emu::make_equalizer( -47.0, 2000 );
23
Gbs_Emu::equalizer_t const Gbs_Emu::headphones_eq =
24
  Music_Emu::make_equalizer( 0.0, 300 );
25
26
Gbs_Emu::Gbs_Emu()
27
1.19k
{
28
1.19k
  set_type( gme_gbs_type );
29
30
1.19k
  static const char* const names [Gb_Apu::osc_count] = {
31
1.19k
    "Square 1", "Square 2", "Wave", "Noise"
32
1.19k
  };
33
1.19k
  set_voice_names( names );
34
35
1.19k
  static int const types [Gb_Apu::osc_count] = {
36
1.19k
    wave_type | 1, wave_type | 2, wave_type | 0, mixed_type | 0
37
1.19k
  };
38
1.19k
  set_voice_types( types );
39
40
1.19k
  set_silence_lookahead( 6 );
41
1.19k
  set_max_initial_silence( 21 );
42
1.19k
  set_gain( 1.2 );
43
44
1.19k
  set_equalizer( make_equalizer( -1.0, 120 ) );
45
1.19k
}
46
47
1.19k
Gbs_Emu::~Gbs_Emu() { }
48
49
void Gbs_Emu::unload()
50
1.19k
{
51
1.19k
  rom.clear();
52
1.19k
  Music_Emu::unload();
53
1.19k
}
54
55
// Track info
56
57
static void copy_gbs_fields( Gbs_Emu::header_t const& h, track_info_t* out )
58
1.46k
{
59
1.46k
  GME_COPY_FIELD( h, out, game );
60
1.46k
  GME_COPY_FIELD( h, out, author );
61
1.46k
  GME_COPY_FIELD( h, out, copyright );
62
1.46k
}
63
64
blargg_err_t Gbs_Emu::track_info_( track_info_t* out, int ) const
65
1.46k
{
66
1.46k
  copy_gbs_fields( header_, out );
67
1.46k
  return 0;
68
1.46k
}
69
70
static blargg_err_t check_gbs_header( void const* header )
71
1.19k
{
72
1.19k
  if ( memcmp( header, "GBS", 3 ) )
73
0
    return gme_wrong_file_type;
74
1.19k
  return 0;
75
1.19k
}
76
77
struct Gbs_File : Gme_Info_
78
{
79
  Gbs_Emu::header_t h;
80
81
0
  Gbs_File() { set_type( gme_gbs_type ); }
82
83
  blargg_err_t load_( Data_Reader& in )
84
0
  {
85
0
    blargg_err_t err = in.read( &h, Gbs_Emu::header_size );
86
0
    if ( err )
87
0
      return (err == in.eof_error ? gme_wrong_file_type : err);
88
89
0
    set_track_count( h.track_count );
90
0
    return check_gbs_header( &h );
91
0
  }
92
93
  blargg_err_t track_info_( track_info_t* out, int ) const
94
0
  {
95
0
    copy_gbs_fields( h, out );
96
0
    return 0;
97
0
  }
98
};
99
100
1.19k
static Music_Emu* new_gbs_emu () { return BLARGG_NEW Gbs_Emu ; }
101
0
static Music_Emu* new_gbs_file() { return BLARGG_NEW Gbs_File; }
102
103
static gme_type_t_ const gme_gbs_type_ = { "Game Boy", 0, &new_gbs_emu, &new_gbs_file, "GBS", 1 };
104
extern gme_type_t const gme_gbs_type = &gme_gbs_type_;
105
106
// Setup
107
108
blargg_err_t Gbs_Emu::load_( Data_Reader& in )
109
1.19k
{
110
1.19k
  blaarg_static_assert( offsetof (header_t,copyright [32]) == header_size, "GBS Header layout incorrect!" );
111
1.19k
  RETURN_ERR( rom.load( in, header_size, &header_, 0 ) );
112
113
1.19k
  set_track_count( header_.track_count );
114
1.19k
  RETURN_ERR( check_gbs_header( &header_ ) );
115
116
1.19k
  if ( header_.vers != 1 )
117
0
    set_warning( "Unknown file version" );
118
119
1.19k
  if ( header_.timer_mode & 0x78 )
120
331
    set_warning( "Invalid timer mode" );
121
122
1.19k
  unsigned load_addr = get_le16( header_.load_addr );
123
1.19k
  if ( (header_.load_addr [1] | header_.init_addr [1] | header_.play_addr [1]) > 0x7F ||
124
1.11k
      load_addr < 0x400 )
125
97
    set_warning( "Invalid load/init/play address" );
126
127
1.19k
  set_voice_count( Gb_Apu::osc_count );
128
129
1.19k
  apu.volume( gain() );
130
131
1.19k
  return setup_buffer( 4194304 );
132
1.19k
}
133
134
void Gbs_Emu::update_eq( blip_eq_t const& eq )
135
2.38k
{
136
2.38k
  apu.treble_eq( eq );
137
2.38k
}
138
139
void Gbs_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r )
140
4.77k
{
141
4.77k
  apu.osc_output( i, c, l, r );
142
4.77k
}
143
144
// Emulation
145
146
// see gb_cpu_io.h for read/write functions
147
148
void Gbs_Emu::set_bank( int n )
149
95.2M
{
150
  // Only valid for MBC1 cartridges, but hopefully shouldn't hurt
151
95.2M
  n &= 0x1f;
152
95.2M
  if (n == 0)
153
5.59M
  {
154
5.59M
    n = 1;
155
5.59M
  }
156
157
95.2M
  blargg_long addr = n * (blargg_long) bank_size;
158
95.2M
  if (addr > rom.size())
159
85.2M
  {
160
85.2M
    return;
161
85.2M
  }
162
10.0M
  cpu::map_code( bank_size, bank_size, rom.at_addr( rom.mask_addr( addr ) ) );
163
10.0M
}
164
165
void Gbs_Emu::update_timer()
166
29.6k
{
167
29.6k
  if ( header_.timer_mode & 0x04 )
168
3.26k
  {
169
3.26k
    static byte const rates [4] = { 10, 4, 6, 8 };
170
3.26k
    int shift = rates [ram [hi_page + 7] & 3] - (header_.timer_mode >> 7);
171
3.26k
    play_period = (256L - ram [hi_page + 6]) << shift;
172
3.26k
  }
173
26.3k
  else
174
26.3k
  {
175
26.3k
    play_period = 70224; // 59.73 Hz
176
26.3k
  }
177
29.6k
  if ( tempo() != 1.0 )
178
0
    play_period = blip_time_t (play_period / tempo());
179
29.6k
}
180
181
static uint8_t const sound_data [Gb_Apu::register_count] = {
182
  0x80, 0xBF, 0x00, 0x00, 0xBF, // square 1
183
  0x00, 0x3F, 0x00, 0x00, 0xBF, // square 2
184
  0x7F, 0xFF, 0x9F, 0x00, 0xBF, // wave
185
  0x00, 0xFF, 0x00, 0x00, 0xBF, // noise
186
  0x77, 0xF3, 0xF1, // vin/volume, status, power mode
187
  0, 0, 0, 0, 0, 0, 0, 0, 0, // unused
188
  0xAC, 0xDD, 0xDA, 0x48, 0x36, 0x02, 0xCF, 0x16, // waveform data
189
  0x2C, 0x04, 0xE5, 0x2C, 0xAC, 0xDD, 0xDA, 0x48
190
};
191
192
void Gbs_Emu::cpu_jsr( gb_addr_t addr )
193
38.0k
{
194
38.0k
  check( cpu::r.sp == get_le16( header_.stack_ptr ) );
195
38.0k
  cpu::r.pc = addr;
196
38.0k
  cpu_write( --cpu::r.sp, idle_addr >> 8 );
197
38.0k
  cpu_write( --cpu::r.sp, idle_addr&0xFF );
198
38.0k
}
199
200
void Gbs_Emu::set_tempo_( double t )
201
1.19k
{
202
1.19k
  apu.set_tempo( t );
203
1.19k
  update_timer();
204
1.19k
}
205
206
blargg_err_t Gbs_Emu::start_track_( int track )
207
1.46k
{
208
1.46k
  RETURN_ERR( Classic_Emu::start_track_( track ) );
209
210
1.46k
  memset( ram, 0, 0x4000 );
211
1.46k
  memset( ram + 0x4000, 0xFF, 0x1F00 );
212
1.46k
  memset( ram + 0x5F00, 0, sizeof ram - 0x5F00 );
213
1.46k
  ram [hi_page] = 0; // joypad reads back as 0
214
215
1.46k
  apu.reset();
216
71.6k
  for ( int i = 0; i < (int) sizeof sound_data; i++ )
217
70.1k
    apu.write_register( 0, i + apu.start_addr, sound_data [i] );
218
219
1.46k
  unsigned load_addr = get_le16( header_.load_addr );
220
1.46k
  rom.set_addr( load_addr );
221
1.46k
  cpu::rst_base = load_addr;
222
223
1.46k
  cpu::reset( rom.unmapped() );
224
225
1.46k
  cpu::map_code( ram_addr, 0x10000 - ram_addr, ram );
226
1.46k
  cpu::map_code( 0, bank_size, rom.at_addr( 0 ) );
227
1.46k
  set_bank( rom.size() > bank_size );
228
229
1.46k
  ram [hi_page + 6] = header_.timer_modulo;
230
1.46k
  ram [hi_page + 7] = header_.timer_mode;
231
1.46k
  update_timer();
232
1.46k
  next_play = play_period;
233
234
1.46k
  cpu::r.a  = track;
235
1.46k
  cpu::r.pc = idle_addr;
236
1.46k
  cpu::r.sp = get_le16( header_.stack_ptr );
237
1.46k
  cpu_time  = 0;
238
1.46k
  cpu_jsr( get_le16( header_.init_addr ) );
239
240
1.46k
  return 0;
241
1.46k
}
242
243
blargg_err_t Gbs_Emu::run_clocks( blip_time_t& duration, int )
244
402k
{
245
402k
  cpu_time = 0;
246
47.4M
  while ( cpu_time < duration )
247
47.0M
  {
248
47.0M
    long count = duration - cpu_time;
249
47.0M
    cpu_time = duration;
250
47.0M
    bool result = cpu::run( count );
251
47.0M
    cpu_time -= cpu::remain();
252
253
47.0M
    if ( result )
254
46.7M
    {
255
46.7M
      if ( cpu::r.pc == idle_addr )
256
46.1k
      {
257
46.1k
        if ( next_play > duration )
258
9.53k
        {
259
9.53k
          cpu_time = duration;
260
9.53k
          break;
261
9.53k
        }
262
263
36.6k
        if ( cpu_time < next_play )
264
36.3k
          cpu_time = next_play;
265
36.6k
        next_play += play_period;
266
36.6k
        cpu_jsr( get_le16( header_.play_addr ) );
267
36.6k
        GME_FRAME_HOOK( this );
268
        // TODO: handle timer rates different than 60 Hz
269
36.6k
      }
270
46.6M
      else if ( cpu::r.pc > 0xFFFF )
271
0
      {
272
0
        debug_printf( "PC wrapped around\n" );
273
0
        cpu::r.pc &= 0xFFFF;
274
0
      }
275
46.6M
      else
276
46.6M
      {
277
46.6M
        set_warning( "Emulation error (illegal/unsupported instruction)" );
278
46.6M
        debug_printf( "Bad opcode $%.2x at $%.4x\n",
279
0
            (int) *cpu::get_code( cpu::r.pc ), (int) cpu::r.pc );
280
46.6M
        cpu::r.pc = (cpu::r.pc + 1) & 0xFFFF;
281
46.6M
        cpu_time += 6;
282
46.6M
      }
283
46.7M
    }
284
47.0M
  }
285
286
402k
  duration = cpu_time;
287
402k
  next_play -= cpu_time;
288
402k
  if ( next_play < 0 ) // could go negative if routine is taking too long to return
289
390k
    next_play = 0;
290
402k
  apu.end_frame( cpu_time );
291
292
402k
  return 0;
293
402k
}