Coverage Report

Created: 2026-08-31 08:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/vlc/contrib/contrib-build/game-music-emu/gme/Nes_Apu.cpp
Line
Count
Source
1
// Nes_Snd_Emu 0.1.8. http://www.slack.net/~ant/
2
3
#include "Nes_Apu.h"
4
5
/* Copyright (C) 2003-2006 Shay Green. This module is free software; you
6
can redistribute it and/or modify it under the terms of the GNU Lesser
7
General Public License as published by the Free Software Foundation; either
8
version 2.1 of the License, or (at your option) any later version. This
9
module is distributed in the hope that it will be useful, but WITHOUT ANY
10
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12
details. You should have received a copy of the GNU Lesser General Public
13
License along with this module; if not, write to the Free Software Foundation,
14
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
15
16
#include "blargg_source.h"
17
18
static int const amp_range = 15;
19
20
Nes_Apu::Nes_Apu() :
21
1.38k
  square1( &square_synth ),
22
1.38k
  square2( &square_synth )
23
1.38k
{
24
1.38k
  tempo_ = 1.0;
25
1.38k
  dmc.apu = this;
26
1.38k
  dmc.prg_reader = NULL;
27
1.38k
  irq_notifier_ = NULL;
28
29
1.38k
  oscs [0] = &square1;
30
1.38k
  oscs [1] = &square2;
31
1.38k
  oscs [2] = ▵
32
1.38k
  oscs [3] = &noise;
33
1.38k
  oscs [4] = &dmc;
34
35
1.38k
  output( NULL );
36
1.38k
  volume( 1.0 );
37
1.38k
  reset( false );
38
1.38k
}
39
40
void Nes_Apu::treble_eq( const blip_eq_t& eq )
41
2.22k
{
42
2.22k
  square_synth.treble_eq( eq );
43
2.22k
  triangle.synth.treble_eq( eq );
44
2.22k
  noise.synth.treble_eq( eq );
45
2.22k
  dmc.synth.treble_eq( eq );
46
2.22k
}
47
48
void Nes_Apu::enable_nonlinear( double v )
49
0
{
50
0
  dmc.nonlinear = true;
51
0
  square_synth.volume( 1.3 * 0.25751258 / 0.742467605 * 0.25 / amp_range * v );
52
53
0
  const double tnd = 0.48 / 202 * nonlinear_tnd_gain();
54
0
  triangle.synth.volume( 3.0 * tnd );
55
0
  noise.synth.volume( 2.0 * tnd );
56
0
  dmc.synth.volume( tnd );
57
58
0
  square1 .last_amp = 0;
59
0
  square2 .last_amp = 0;
60
0
  triangle.last_amp = 0;
61
0
  noise   .last_amp = 0;
62
0
  dmc     .last_amp = 0;
63
0
}
64
65
void Nes_Apu::volume( double v )
66
2.75k
{
67
2.75k
  dmc.nonlinear = false;
68
2.75k
  square_synth.volume(   0.1128  / amp_range * v );
69
2.75k
  triangle.synth.volume( 0.12765 / amp_range * v );
70
2.75k
  noise.synth.volume(    0.0741  / amp_range * v );
71
2.75k
  dmc.synth.volume(      0.42545 / 127 * v );
72
2.75k
}
73
74
void Nes_Apu::output( Blip_Buffer* buffer )
75
1.38k
{
76
8.29k
  for ( int i = 0; i < osc_count; i++ )
77
6.91k
    osc_output( i, buffer );
78
1.38k
}
79
80
void Nes_Apu::set_tempo( double t )
81
11.8k
{
82
11.8k
  tempo_ = t;
83
11.8k
  frame_period = (dmc.pal_mode ? 8314 : 7458);
84
11.8k
  if ( t != 1.0 )
85
0
    frame_period = (int) (frame_period / t) & ~1; // must be even
86
11.8k
}
87
88
void Nes_Apu::reset( bool pal_mode, int initial_dmc_dac )
89
10.1k
{
90
10.1k
  dmc.pal_mode = pal_mode;
91
10.1k
  set_tempo( tempo_ );
92
93
10.1k
  square1.reset();
94
10.1k
  square2.reset();
95
10.1k
  triangle.reset();
96
10.1k
  noise.reset();
97
10.1k
  dmc.reset();
98
99
10.1k
  last_time = 0;
100
10.1k
  last_dmc_time = 0;
101
10.1k
  osc_enables = 0;
102
10.1k
  irq_flag = false;
103
10.1k
  earliest_irq_ = no_irq;
104
10.1k
  frame_delay = 1;
105
10.1k
  write_register( 0, 0x4017, 0x00 );
106
10.1k
  write_register( 0, 0x4015, 0x00 );
107
108
214k
  for ( nes_addr_t addr = start_addr; addr <= 0x4013; addr++ )
109
203k
    write_register( 0, addr, (addr & 3) ? 0x00 : 0x10 );
110
111
10.1k
  dmc.dac = initial_dmc_dac;
112
10.1k
  if ( !dmc.nonlinear )
113
10.1k
    triangle.last_amp = 15;
114
10.1k
  if ( !dmc.nonlinear ) // TODO: remove?
115
10.1k
    dmc.last_amp = initial_dmc_dac; // prevent output transition
116
10.1k
}
117
118
void Nes_Apu::irq_changed()
119
31.1k
{
120
31.1k
  nes_time_t new_irq = dmc.next_irq;
121
31.1k
  if ( dmc.irq_flag | irq_flag ) {
122
0
    new_irq = 0;
123
0
  }
124
31.1k
  else if ( new_irq > next_irq ) {
125
31.1k
    new_irq = next_irq;
126
31.1k
  }
127
128
31.1k
  if ( new_irq != earliest_irq_ ) {
129
10.1k
    earliest_irq_ = new_irq;
130
10.1k
    if ( irq_notifier_ )
131
0
      irq_notifier_( irq_data );
132
10.1k
  }
133
31.1k
}
134
135
// frames
136
137
void Nes_Apu::run_until( nes_time_t end_time )
138
0
{
139
0
  require( end_time >= last_dmc_time );
140
0
  if ( end_time > next_dmc_read_time() )
141
0
  {
142
0
    nes_time_t start = last_dmc_time;
143
0
    last_dmc_time = end_time;
144
0
    dmc.run( start, end_time );
145
0
  }
146
0
}
147
148
void Nes_Apu::run_until_( nes_time_t end_time )
149
1.65M
{
150
1.65M
  require( end_time >= last_time );
151
152
1.65M
  if ( end_time == last_time )
153
235k
    return;
154
155
1.41M
  if ( last_dmc_time < end_time )
156
1.41M
  {
157
1.41M
    nes_time_t start = last_dmc_time;
158
1.41M
    last_dmc_time = end_time;
159
1.41M
    dmc.run( start, end_time );
160
1.41M
  }
161
162
16.8M
  while ( true )
163
16.8M
  {
164
    // earlier of next frame time or end time
165
16.8M
    nes_time_t time = last_time + frame_delay;
166
16.8M
    if ( time > end_time )
167
1.41M
      time = end_time;
168
16.8M
    frame_delay -= time - last_time;
169
170
    // run oscs to present
171
16.8M
    square1.run( last_time, time );
172
16.8M
    square2.run( last_time, time );
173
16.8M
    triangle.run( last_time, time );
174
16.8M
    noise.run( last_time, time );
175
16.8M
    last_time = time;
176
177
16.8M
    if ( time == end_time )
178
1.41M
      break; // no more frames to run
179
180
    // take frame-specific actions
181
15.3M
    frame_delay = frame_period;
182
15.3M
    switch ( frame++ )
183
15.3M
    {
184
3.84M
      case 0:
185
3.84M
        if ( !(frame_mode & 0xC0) ) {
186
3.84M
          next_irq = time + frame_period * 4 + 2;
187
3.84M
          irq_flag = true;
188
3.84M
        }
189
        // fall through
190
7.68M
      case 2:
191
        // clock length and sweep on frames 0 and 2
192
7.68M
        square1.clock_length( 0x20 );
193
7.68M
        square2.clock_length( 0x20 );
194
7.68M
        noise.clock_length( 0x20 );
195
7.68M
        triangle.clock_length( 0x80 ); // different bit for halt flag on triangle
196
197
7.68M
        square1.clock_sweep( -1 );
198
7.68M
        square2.clock_sweep( 0 );
199
200
        // frame 2 is slightly shorter in mode 1
201
7.68M
        if ( dmc.pal_mode && frame == 3 )
202
1.46M
          frame_delay -= 2;
203
7.68M
        break;
204
205
3.85M
      case 1:
206
        // frame 1 is slightly shorter in mode 0
207
3.85M
        if ( !dmc.pal_mode )
208
2.37M
          frame_delay -= 2;
209
3.85M
        break;
210
211
3.84M
      case 3:
212
3.84M
        frame = 0;
213
214
        // frame 3 is almost twice as long in mode 1
215
3.84M
        if ( frame_mode & 0x80 )
216
0
          frame_delay += frame_period - (dmc.pal_mode ? 2 : 6);
217
3.84M
        break;
218
15.3M
    }
219
220
    // clock envelopes and linear counter every frame
221
15.3M
    triangle.clock_linear_counter();
222
15.3M
    square1.clock_envelope();
223
15.3M
    square2.clock_envelope();
224
15.3M
    noise.clock_envelope();
225
15.3M
  }
226
1.41M
}
227
228
template<class T>
229
inline void zero_apu_osc( T* osc, nes_time_t time )
230
0
{
231
0
  Blip_Buffer* output = osc->output;
232
0
  int last_amp = osc->last_amp;
233
0
  osc->last_amp = 0;
234
0
  if ( output && last_amp )
235
0
    osc->synth.offset( time, -last_amp, output );
236
0
}
Unexecuted instantiation: void zero_apu_osc<Nes_Square>(Nes_Square*, int)
Unexecuted instantiation: void zero_apu_osc<Nes_Triangle>(Nes_Triangle*, int)
Unexecuted instantiation: void zero_apu_osc<Nes_Noise>(Nes_Noise*, int)
Unexecuted instantiation: void zero_apu_osc<Nes_Dmc>(Nes_Dmc*, int)
237
238
void Nes_Apu::end_frame( nes_time_t end_time )
239
1.41M
{
240
1.41M
  if ( end_time > last_time )
241
1.41M
    run_until_( end_time );
242
243
1.41M
  if ( dmc.nonlinear )
244
0
  {
245
0
    zero_apu_osc( &square1,  last_time );
246
0
    zero_apu_osc( &square2,  last_time );
247
0
    zero_apu_osc( &triangle, last_time );
248
0
    zero_apu_osc( &noise,    last_time );
249
0
    zero_apu_osc( &dmc,      last_time );
250
0
  }
251
252
  // make times relative to new frame
253
1.41M
  last_time -= end_time;
254
1.41M
  require( last_time >= 0 );
255
256
1.41M
  last_dmc_time -= end_time;
257
1.41M
  require( last_dmc_time >= 0 );
258
259
1.41M
  if ( next_irq != no_irq ) {
260
1.41M
    next_irq -= end_time;
261
1.41M
    check( next_irq >= 0 );
262
1.41M
  }
263
1.41M
  if ( dmc.next_irq != no_irq ) {
264
0
    dmc.next_irq -= end_time;
265
0
    check( dmc.next_irq >= 0 );
266
0
  }
267
1.41M
  if ( earliest_irq_ != no_irq ) {
268
1.41M
    earliest_irq_ -= end_time;
269
1.41M
    if ( earliest_irq_ < 0 )
270
1.41M
      earliest_irq_ = 0;
271
1.41M
  }
272
1.41M
}
273
274
// registers
275
276
static const unsigned char length_table [0x20] = {
277
  0x0A, 0xFE, 0x14, 0x02, 0x28, 0x04, 0x50, 0x06,
278
  0xA0, 0x08, 0x3C, 0x0A, 0x0E, 0x0C, 0x1A, 0x0E,
279
  0x0C, 0x10, 0x18, 0x12, 0x30, 0x14, 0x60, 0x16,
280
  0xC0, 0x18, 0x48, 0x1A, 0x10, 0x1C, 0x20, 0x1E
281
};
282
283
void Nes_Apu::write_register( nes_time_t time, nes_addr_t addr, int data )
284
235k
{
285
235k
  require( addr > 0x20 ); // addr must be actual address (i.e. 0x40xx)
286
235k
  require( (unsigned) data <= 0xFF );
287
288
  // Ignore addresses outside range
289
235k
  if ( unsigned (addr - start_addr) > end_addr - start_addr )
290
0
    return;
291
292
235k
  run_until_( time );
293
294
235k
  if ( addr < 0x4014 )
295
204k
  {
296
    // Write to channel
297
204k
    int osc_index = (addr - start_addr) >> 2;
298
204k
    Nes_Osc* osc = oscs [osc_index];
299
300
204k
    int reg = addr & 3;
301
204k
    osc->regs [reg] = data;
302
204k
    osc->reg_written [reg] = true;
303
304
204k
    if ( osc_index == 4 )
305
40.7k
    {
306
      // handle DMC specially
307
40.7k
      dmc.write_register( reg, data );
308
40.7k
    }
309
163k
    else if ( reg == 3 )
310
40.7k
    {
311
      // load length counter
312
40.7k
      if ( (osc_enables >> osc_index) & 1 )
313
0
        osc->length_counter = length_table [(data >> 3) & 0x1F];
314
315
      // reset square phase
316
40.7k
      if ( osc_index < 2 )
317
20.3k
        ((Nes_Square*) osc)->phase = Nes_Square::phase_range - 1;
318
40.7k
    }
319
204k
  }
320
31.1k
  else if ( addr == 0x4015 )
321
15.5k
  {
322
    // Channel enables
323
93.5k
    for ( int i = osc_count; i--; )
324
77.9k
      if ( !((data >> i) & 1) )
325
56.3k
        oscs [i]->length_counter = 0;
326
327
15.5k
    bool recalc_irq = dmc.irq_flag;
328
15.5k
    dmc.irq_flag = false;
329
330
15.5k
    int old_enables = osc_enables;
331
15.5k
    osc_enables = data;
332
15.5k
    if ( !(data & 0x10) ) {
333
15.5k
      dmc.next_irq = no_irq;
334
15.5k
      recalc_irq = true;
335
15.5k
    }
336
0
    else if ( !(old_enables & 0x10) ) {
337
0
      dmc.start(); // dmc just enabled
338
0
    }
339
340
15.5k
    if ( recalc_irq )
341
15.5k
      irq_changed();
342
15.5k
  }
343
15.5k
  else if ( addr == 0x4017 )
344
15.5k
  {
345
    // Frame mode
346
15.5k
    frame_mode = data;
347
348
15.5k
    bool irq_enabled = !(data & 0x40);
349
15.5k
    irq_flag &= irq_enabled;
350
15.5k
    next_irq = no_irq;
351
352
    // mode 1
353
15.5k
    frame_delay = (frame_delay & 1);
354
15.5k
    frame = 0;
355
356
15.5k
    if ( !(data & 0x80) )
357
15.5k
    {
358
      // mode 0
359
15.5k
      frame = 1;
360
15.5k
      frame_delay += frame_period;
361
15.5k
      if ( irq_enabled )
362
15.5k
        next_irq = time + frame_delay + frame_period * 3 + 1;
363
15.5k
    }
364
365
15.5k
    irq_changed();
366
15.5k
  }
367
235k
}
368
369
int Nes_Apu::read_status( nes_time_t time )
370
0
{
371
0
  run_until_( time - 1 );
372
373
0
  int result = (dmc.irq_flag << 7) | (irq_flag << 6);
374
375
0
  for ( int i = 0; i < osc_count; i++ )
376
0
    if ( oscs [i]->length_counter )
377
0
      result |= 1 << i;
378
379
0
  run_until_( time );
380
381
0
  if ( irq_flag )
382
0
  {
383
0
    result |= 0x40;
384
0
    irq_flag = false;
385
0
    irq_changed();
386
0
  }
387
388
  //debug_printf( "%6d/%d Read $4015->$%02X\n", frame_delay, frame, result );
389
390
0
  return result;
391
0
}