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