1 // Nes_Snd_Emu 0.1.8. http://www.slack.net/~ant/
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 */
16 #include "blargg_source.h"
18 int const amp_range
= 15;
20 void Apu_init( struct Nes_Apu
* this )
24 this->dmc
.prg_reader
= NULL
;
25 this->irq_notifier_
= NULL
;
27 Synth_init( &this->square_synth
);
28 Synth_init( &this->triangle
.synth
);
29 Synth_init( &this->noise
.synth
);
30 Synth_init( &this->dmc
.synth
);
32 Square_set_synth( &this->square1
, &this->square_synth
);
33 Square_set_synth( &this->square2
, &this->square_synth
);
35 this->oscs
[0] = &this->square1
.osc
;
36 this->oscs
[1] = &this->square2
.osc
;
37 this->oscs
[2] = &this->triangle
.osc
;
38 this->oscs
[3] = &this->noise
.osc
;
39 this->oscs
[4] = &this->dmc
.osc
;
41 Apu_output( this, NULL
);
42 Apu_volume( this, 1.0 );
43 Apu_reset( this, false, 0 );
46 static double nonlinear_tnd_gain( void ) { return 0.75; }
47 void Apu_enable_nonlinear( struct Nes_Apu
* this, double v
)
49 this->dmc
.nonlinear
= true;
50 Synth_volume( &this->square_synth
, 1.3 * 0.25751258 / 0.742467605 * 0.25 / amp_range
* v
);
52 const double tnd
= 0.48 / 202 * nonlinear_tnd_gain();
53 Synth_volume( &this->triangle
.synth
, 3.0 * tnd
);
54 Synth_volume( &this->noise
.synth
, 2.0 * tnd
);
55 Synth_volume( &this->dmc
.synth
, tnd
);
57 this->square1
.osc
.last_amp
= 0;
58 this->square2
.osc
.last_amp
= 0;
59 this->triangle
.osc
.last_amp
= 0;
60 this->noise
.osc
.last_amp
= 0;
61 this->dmc
.osc
.last_amp
= 0;
64 void Apu_volume( struct Nes_Apu
* this, double v
)
66 this->dmc
.nonlinear
= false;
67 Synth_volume( &this->square_synth
, 0.1128 / amp_range
* v
);
68 Synth_volume( &this->triangle
.synth
,0.12765 / amp_range
* v
);
69 Synth_volume( &this->noise
.synth
, 0.0741 / amp_range
* v
);
70 Synth_volume( &this->dmc
.synth
, 0.42545 / 127 * v
);
73 void Apu_output( struct Nes_Apu
* this, struct Blip_Buffer
* buffer
)
76 for ( i
= 0; i
< apu_osc_count
; i
++ )
77 Apu_osc_output( this, i
, buffer
);
80 void Apu_set_tempo( struct Nes_Apu
* this, double t
)
83 this->frame_period
= (this->dmc
.pal_mode
? 8314 : 7458);
85 this->frame_period
= (int) (this->frame_period
/ t
) & ~1; // must be even
88 void Apu_reset( struct Nes_Apu
* this, bool pal_mode
, int initial_dmc_dac
)
90 this->dmc
.pal_mode
= pal_mode
;
91 Apu_set_tempo( this, this->tempo_
);
93 Square_reset( &this->square1
);
94 Square_reset( &this->square2
);
95 Triangle_reset( &this->triangle
);
96 Noise_reset( &this->noise
);
97 Dmc_reset( &this->dmc
);
100 this->last_dmc_time
= 0;
101 this->osc_enables
= 0;
102 this->irq_flag
= false;
103 this->earliest_irq_
= apu_no_irq
;
104 this->frame_delay
= 1;
105 Apu_write_register( this, 0, 0x4017, 0x00 );
106 Apu_write_register( this, 0, 0x4015, 0x00 );
109 for ( addr
= apu_io_addr
; addr
<= 0x4013; addr
++ )
110 Apu_write_register( this, 0, addr
, (addr
& 3) ? 0x00 : 0x10 );
112 this->dmc
.dac
= initial_dmc_dac
;
113 if ( !this->dmc
.nonlinear
)
114 this->triangle
.osc
.last_amp
= 15;
115 if ( !this->dmc
.nonlinear
) // TODO: remove?
116 this->dmc
.osc
.last_amp
= initial_dmc_dac
; // prevent output transition
119 void Apu_irq_changed( struct Nes_Apu
* this )
121 nes_time_t new_irq
= this->dmc
.next_irq
;
122 if ( this->dmc
.irq_flag
| this->irq_flag
) {
125 else if ( new_irq
> this->next_irq
) {
126 new_irq
= this->next_irq
;
129 if ( new_irq
!= this->earliest_irq_
) {
130 this->earliest_irq_
= new_irq
;
131 if ( this->irq_notifier_
)
132 this->irq_notifier_( this->irq_data
);
138 void Apu_run_until( struct Nes_Apu
* this, nes_time_t end_time
)
140 require( end_time
>= this->last_dmc_time
);
141 if ( end_time
> Apu_next_dmc_read_time( this ) )
143 nes_time_t start
= this->last_dmc_time
;
144 this->last_dmc_time
= end_time
;
145 Dmc_run( &this->dmc
, start
, end_time
);
149 void run_until_( struct Nes_Apu
* this, nes_time_t end_time
)
151 require( end_time
>= this->last_time
);
153 if ( end_time
== this->last_time
)
156 if ( this->last_dmc_time
< end_time
)
158 nes_time_t start
= this->last_dmc_time
;
159 this->last_dmc_time
= end_time
;
160 Dmc_run( &this->dmc
, start
, end_time
);
165 // earlier of next frame time or end time
166 nes_time_t time
= this->last_time
+ this->frame_delay
;
167 if ( time
> end_time
)
169 this->frame_delay
-= time
- this->last_time
;
171 // run oscs to present
172 Square_run( &this->square1
, this->last_time
, time
);
173 Square_run( &this->square2
, this->last_time
, time
);
174 Triangle_run( &this->triangle
, this->last_time
, time
);
175 Noise_run( &this->noise
, this->last_time
, time
);
176 this->last_time
= time
;
178 if ( time
== end_time
)
179 break; // no more frames to run
181 // take frame-specific actions
182 this->frame_delay
= this->frame_period
;
183 switch ( this->frame
++ )
186 if ( !(this->frame_mode
& 0xC0) ) {
187 this->next_irq
= time
+ this->frame_period
* 4 + 2;
188 this->irq_flag
= true;
192 // clock length and sweep on frames 0 and 2
193 Osc_clock_length( &this->square1
.osc
, 0x20 );
194 Osc_clock_length( &this->square2
.osc
, 0x20 );
195 Osc_clock_length( &this->noise
.osc
, 0x20 );
196 Osc_clock_length( &this->triangle
.osc
, 0x80 ); // different bit for halt flag on triangle
198 Square_clock_sweep( &this->square1
, -1 );
199 Square_clock_sweep( &this->square2
, 0 );
201 // frame 2 is slightly shorter in mode 1
202 if ( this->dmc
.pal_mode
&& this->frame
== 3 )
203 this->frame_delay
-= 2;
207 // frame 1 is slightly shorter in mode 0
208 if ( !this->dmc
.pal_mode
)
209 this->frame_delay
-= 2;
215 // frame 3 is almost twice as long in mode 1
216 if ( this->frame_mode
& 0x80 )
217 this->frame_delay
+= this->frame_period
- (this->dmc
.pal_mode
? 2 : 6);
221 // clock envelopes and linear counter every frame
222 Triangle_clock_linear_counter( &this->triangle
);
223 Square_clock_envelope( &this->square1
);
224 Square_clock_envelope( &this->square2
);
225 Noise_clock_envelope( &this->noise
);
229 static inline void zero_apu_osc( struct Nes_Osc
* osc
, struct Blip_Synth
* synth
, nes_time_t time
)
231 struct Blip_Buffer
* output
= osc
->output
;
232 int last_amp
= osc
->last_amp
;
234 if ( output
&& last_amp
)
235 Synth_offset( synth
, time
, -osc
->last_amp
, output
);
238 void Apu_end_frame( struct Nes_Apu
* this, nes_time_t end_time
)
240 if ( end_time
> this->last_time
)
241 run_until_( this, end_time
);
243 if ( this->dmc
.nonlinear
)
245 zero_apu_osc( &this->square1
.osc
, this->square1
.synth
, this->last_time
);
246 zero_apu_osc( &this->square2
.osc
, this->square2
.synth
, this->last_time
);
247 zero_apu_osc( &this->triangle
.osc
, &this->triangle
.synth
, this->last_time
);
248 zero_apu_osc( &this->noise
.osc
, &this->noise
.synth
, this->last_time
);
249 zero_apu_osc( &this->dmc
.osc
, &this->dmc
.synth
, this->last_time
);
252 // make times relative to new frame
253 this->last_time
-= end_time
;
254 require( this->last_time
>= 0 );
256 this->last_dmc_time
-= end_time
;
257 require( this->last_dmc_time
>= 0 );
259 if ( this->next_irq
!= apu_no_irq
) {
260 this->next_irq
-= end_time
;
261 check( this->next_irq
>= 0 );
263 if ( this->dmc
.next_irq
!= apu_no_irq
) {
264 this->dmc
.next_irq
-= end_time
;
265 check( this->dmc
.next_irq
>= 0 );
267 if ( this->earliest_irq_
!= apu_no_irq
) {
268 this->earliest_irq_
-= end_time
;
269 if ( this->earliest_irq_
< 0 )
270 this->earliest_irq_
= 0;
276 static const unsigned char length_table
[0x20] ICONST_ATTR
= {
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
283 void Apu_write_register( struct Nes_Apu
* this, nes_time_t time
, addr_t addr
, int data
)
285 require( addr
> 0x20 ); // addr must be actual address (i.e. 0x40xx)
286 require( (unsigned) data
<= 0xFF );
288 // Ignore addresses outside range
289 if ( (unsigned) (addr
- apu_io_addr
) >= apu_io_size
)
292 run_until_( this, time
);
297 int osc_index
= (addr
- apu_io_addr
) >> 2;
298 struct Nes_Osc
* osc
= this->oscs
[osc_index
];
301 osc
->regs
[reg
] = data
;
302 osc
->reg_written
[reg
] = true;
304 if ( osc_index
== 4 )
306 // handle DMC specially
307 Dmc_write_register( &this->dmc
, reg
, data
);
311 // load length counter
312 if ( (this->osc_enables
>> osc_index
) & 1 )
313 osc
->length_counter
= length_table
[(data
>> 3) & 0x1F];
315 // reset square phase
316 if ( osc_index
== 0 ) this->square1
.phase
= square_phase_range
- 1;
317 else if ( osc_index
== 1 ) this->square2
.phase
= square_phase_range
- 1;
320 else if ( addr
== 0x4015 )
324 for ( i
= apu_osc_count
; i
--; )
325 if ( !((data
>> i
) & 1) )
326 this->oscs
[i
]->length_counter
= 0;
328 bool recalc_irq
= this->dmc
.irq_flag
;
329 this->dmc
.irq_flag
= false;
331 int old_enables
= this->osc_enables
;
332 this->osc_enables
= data
;
333 if ( !(data
& 0x10) ) {
334 this->dmc
.next_irq
= apu_no_irq
;
337 else if ( !(old_enables
& 0x10) ) {
338 Dmc_start( &this->dmc
); // dmc just enabled
342 Apu_irq_changed( this );
344 else if ( addr
== 0x4017 )
347 this->frame_mode
= data
;
349 bool irq_enabled
= !(data
& 0x40);
350 this->irq_flag
&= irq_enabled
;
351 this->next_irq
= apu_no_irq
;
354 this->frame_delay
= (this->frame_delay
& 1);
357 if ( !(data
& 0x80) )
361 this->frame_delay
+= this->frame_period
;
363 this->next_irq
= time
+ this->frame_delay
+ this->frame_period
* 3 + 1;
366 Apu_irq_changed( this );
370 int Apu_read_status( struct Nes_Apu
* this, nes_time_t time
)
372 run_until_( this, time
- 1 );
374 int result
= (this->dmc
.irq_flag
<< 7) | (this->irq_flag
<< 6);
377 for ( i
= 0; i
< apu_osc_count
; i
++ )
378 if ( this->oscs
[i
]->length_counter
)
381 run_until_( this, time
);
383 if ( this->irq_flag
)
386 this->irq_flag
= false;
387 Apu_irq_changed( this );
390 //debug_printf( "%6d/%d Read $4015->$%02X\n", frame_delay, frame, result );