Coverage Report

Created: 2026-08-14 07:16

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.18k
{
28
1.18k
  set_type( gme_gbs_type );
29
30
1.18k
  static const char* const names [Gb_Apu::osc_count] = {
31
1.18k
    "Square 1", "Square 2", "Wave", "Noise"
32
1.18k
  };
33
1.18k
  set_voice_names( names );
34
35
1.18k
  static int const types [Gb_Apu::osc_count] = {
36
1.18k
    wave_type | 1, wave_type | 2, wave_type | 0, mixed_type | 0
37
1.18k
  };
38
1.18k
  set_voice_types( types );
39
40
1.18k
  set_silence_lookahead( 6 );
41
1.18k
  set_max_initial_silence( 21 );
42
1.18k
  set_gain( 1.2 );
43
44
1.18k
  set_equalizer( make_equalizer( -1.0, 120 ) );
45
1.18k
}
46
47
1.18k
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.36k
{
59
1.36k
  GME_COPY_FIELD( h, out, game );
60
1.36k
  GME_COPY_FIELD( h, out, author );
61
1.36k
  GME_COPY_FIELD( h, out, copyright );
62
1.36k
}
63
64
blargg_err_t Gbs_Emu::track_info_( track_info_t* out, int ) const
65
1.36k
{
66
1.36k
  copy_gbs_fields( header_, out );
67
1.36k
  return 0;
68
1.36k
}
69
70
static blargg_err_t check_gbs_header( void const* header )
71
1.18k
{
72
1.18k
  if ( memcmp( header, "GBS", 3 ) )
73
0
    return gme_wrong_file_type;
74
1.18k
  return 0;
75
1.18k
}
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.18k
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.18k
{
110
1.18k
  blaarg_static_assert( offsetof (header_t,copyright [32]) == header_size, "GBS Header layout incorrect!" );
111
1.18k
  RETURN_ERR( rom.load( in, header_size, &header_, 0 ) );
112
113
1.18k
  set_track_count( header_.track_count );
114
1.18k
  RETURN_ERR( check_gbs_header( &header_ ) );
115
116
1.18k
  if ( header_.vers != 1 )
117
0
    set_warning( "Unknown file version" );
118
119
1.18k
  if ( header_.timer_mode & 0x78 )
120
330
    set_warning( "Invalid timer mode" );
121
122
1.18k
  unsigned load_addr = get_le16( header_.load_addr );
123
1.18k
  if ( (header_.load_addr [1] | header_.init_addr [1] | header_.play_addr [1]) > 0x7F ||
124
1.10k
      load_addr < 0x400 )
125
95
    set_warning( "Invalid load/init/play address" );
126
127
1.18k
  set_voice_count( Gb_Apu::osc_count );
128
129
1.18k
  apu.volume( gain() );
130
131
1.18k
  return setup_buffer( 4194304 );
132
1.18k
}
133
134
void Gbs_Emu::update_eq( blip_eq_t const& eq )
135
2.37k
{
136
2.37k
  apu.treble_eq( eq );
137
2.37k
}
138
139
void Gbs_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r )
140
4.74k
{
141
4.74k
  apu.osc_output( i, c, l, r );
142
4.74k
}
143
144
// Emulation
145
146
// see gb_cpu_io.h for read/write functions
147
148
void Gbs_Emu::set_bank( int n )
149
94.9M
{
150
  // Only valid for MBC1 cartridges, but hopefully shouldn't hurt
151
94.9M
  n &= 0x1f;
152
94.9M
  if (n == 0)
153
5.45M
  {
154
5.45M
    n = 1;
155
5.45M
  }
156
157
94.9M
  int32_t addr = n * (int32_t) bank_size;
158
94.9M
  if (addr > rom.size())
159
85.0M
  {
160
85.0M
    return;
161
85.0M
  }
162
9.88M
  cpu::map_code( bank_size, bank_size, rom.at_addr( rom.mask_addr( addr ) ) );
163
9.88M
}
164
165
void Gbs_Emu::update_timer()
166
29.2k
{
167
29.2k
  if ( header_.timer_mode & 0x04 )
168
3.19k
  {
169
3.19k
    static byte const rates [4] = { 10, 4, 6, 8 };
170
3.19k
    int shift = rates [ram [hi_page + 7] & 3] - (header_.timer_mode >> 7);
171
3.19k
    play_period = (256L - ram [hi_page + 6]) << shift;
172
3.19k
  }
173
26.0k
  else
174
26.0k
  {
175
26.0k
    play_period = 70224; // 59.73 Hz
176
26.0k
  }
177
29.2k
  if ( tempo() != 1.0 )
178
0
    play_period = blip_time_t (play_period / tempo());
179
29.2k
}
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
37.9k
{
194
37.9k
  check( cpu::r.sp == get_le16( header_.stack_ptr ) );
195
37.9k
  cpu::r.pc = addr;
196
37.9k
  cpu_write( --cpu::r.sp, idle_addr >> 8 );
197
37.9k
  cpu_write( --cpu::r.sp, idle_addr&0xFF );
198
37.9k
}
199
200
void Gbs_Emu::set_tempo_( double t )
201
1.18k
{
202
1.18k
  apu.set_tempo( t );
203
1.18k
  update_timer();
204
1.18k
}
205
206
blargg_err_t Gbs_Emu::start_track_( int track )
207
1.36k
{
208
1.36k
  RETURN_ERR( Classic_Emu::start_track_( track ) );
209
210
1.36k
  memset( ram, 0, 0x4000 );
211
1.36k
  memset( ram + 0x4000, 0xFF, 0x1F00 );
212
1.36k
  memset( ram + 0x5F00, 0, sizeof ram - 0x5F00 );
213
1.36k
  ram [hi_page] = 0; // joypad reads back as 0
214
215
1.36k
  apu.reset();
216
66.7k
  for ( int i = 0; i < (int) sizeof sound_data; i++ )
217
65.3k
    apu.write_register( 0, i + apu.start_addr, sound_data [i] );
218
219
1.36k
  unsigned load_addr = get_le16( header_.load_addr );
220
1.36k
  rom.set_addr( load_addr );
221
1.36k
  cpu::rst_base = load_addr;
222
223
1.36k
  cpu::reset( rom.unmapped() );
224
225
1.36k
  cpu::map_code( ram_addr, 0x10000 - ram_addr, ram );
226
1.36k
  cpu::map_code( 0, bank_size, rom.at_addr( 0 ) );
227
1.36k
  set_bank( rom.size() > bank_size );
228
229
1.36k
  ram [hi_page + 6] = header_.timer_modulo;
230
1.36k
  ram [hi_page + 7] = header_.timer_mode;
231
1.36k
  update_timer();
232
1.36k
  next_play = play_period;
233
234
1.36k
  cpu::r.a  = track;
235
1.36k
  cpu::r.pc = idle_addr;
236
1.36k
  cpu::r.sp = get_le16( header_.stack_ptr );
237
1.36k
  cpu_time  = 0;
238
1.36k
  cpu_jsr( get_le16( header_.init_addr ) );
239
240
1.36k
  return 0;
241
1.36k
}
242
243
blargg_err_t Gbs_Emu::run_clocks( blip_time_t& duration, int )
244
384k
{
245
384k
  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
384k
  duration = cpu_time;
287
384k
  next_play -= cpu_time;
288
384k
  if ( next_play < 0 ) // could go negative if routine is taking too long to return
289
372k
    next_play = 0;
290
384k
  apu.end_frame( cpu_time );
291
292
384k
  return 0;
293
384k
}