comparison Plugins/Input/console/Nes_Apu.cpp @ 493:c04dff121e1d trunk

[svn] hostile merge, phase 2: reimport based on new plugin code
author nenolod
date Tue, 24 Jan 2006 20:19:01 -0800
parents
children f12d7e208b43
comparison
equal deleted inserted replaced
492:ccb68bad47b2 493:c04dff121e1d
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_BEGIN
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.rom_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 ) // to do: remove?
110 // dmc.last_amp = initial_dmc_dac; // prevent output transition
111 }
112
113 void Nes_Apu::irq_changed()
114 {
115 nes_time_t new_irq = dmc.next_irq;
116 if ( dmc.irq_flag | irq_flag ) {
117 new_irq = 0;
118 }
119 else if ( new_irq > next_irq ) {
120 new_irq = next_irq;
121 }
122
123 if ( new_irq != earliest_irq_ ) {
124 earliest_irq_ = new_irq;
125 if ( irq_notifier_ )
126 irq_notifier_( irq_data );
127 }
128 }
129
130 // frames
131
132 void Nes_Apu::run_until( nes_time_t end_time )
133 {
134 require( end_time >= last_dmc_time );
135 if ( end_time > next_dmc_read_time() )
136 {
137 nes_time_t start = last_dmc_time;
138 last_dmc_time = end_time;
139 dmc.run( start, end_time );
140 }
141 }
142
143 void Nes_Apu::run_until_( nes_time_t end_time )
144 {
145 require( end_time >= last_time );
146
147 if ( end_time == last_time )
148 return;
149
150 if ( last_dmc_time < end_time )
151 {
152 nes_time_t start = last_dmc_time;
153 last_dmc_time = end_time;
154 dmc.run( start, end_time );
155 }
156
157 while ( true )
158 {
159 // earlier of next frame time or end time
160 nes_time_t time = last_time + frame_delay;
161 if ( time > end_time )
162 time = end_time;
163 frame_delay -= time - last_time;
164
165 // run oscs to present
166 square1.run( last_time, time );
167 square2.run( last_time, time );
168 triangle.run( last_time, time );
169 noise.run( last_time, time );
170 last_time = time;
171
172 if ( time == end_time )
173 break; // no more frames to run
174
175 // take frame-specific actions
176 frame_delay = frame_period;
177 switch ( frame++ )
178 {
179 case 0:
180 if ( !(frame_mode & 0xc0) ) {
181 next_irq = time + frame_period * 4 + 1;
182 irq_flag = true;
183 }
184 // fall through
185 case 2:
186 // clock length and sweep on frames 0 and 2
187 square1.clock_length( 0x20 );
188 square2.clock_length( 0x20 );
189 noise.clock_length( 0x20 );
190 triangle.clock_length( 0x80 ); // different bit for halt flag on triangle
191
192 square1.clock_sweep( -1 );
193 square2.clock_sweep( 0 );
194 break;
195
196 case 1:
197 // frame 1 is slightly shorter
198 frame_delay -= 2;
199 break;
200
201 case 3:
202 frame = 0;
203
204 // frame 3 is almost twice as long in mode 1
205 if ( frame_mode & 0x80 )
206 frame_delay += frame_period - 6;
207 break;
208 }
209
210 // clock envelopes and linear counter every frame
211 triangle.clock_linear_counter();
212 square1.clock_envelope();
213 square2.clock_envelope();
214 noise.clock_envelope();
215 }
216 }
217
218 // to do: remove
219 static long abs_time;
220
221 template<class T>
222 inline void zero_apu_osc( T* osc, nes_time_t time )
223 {
224 Blip_Buffer* output = osc->output;
225 int last_amp = osc->last_amp;
226 osc->last_amp = 0;
227 if ( output && last_amp )
228 osc->synth.offset( time, -last_amp, output );
229 }
230
231 void Nes_Apu::end_frame( nes_time_t end_time )
232 {
233 if ( end_time > last_time )
234 run_until_( end_time );
235
236 abs_time += end_time;
237
238 if ( dmc.nonlinear )
239 {
240 zero_apu_osc( &square1, last_time );
241 zero_apu_osc( &square2, last_time );
242 zero_apu_osc( &triangle, last_time );
243 zero_apu_osc( &noise, last_time );
244 zero_apu_osc( &dmc, last_time );
245 }
246
247 // make times relative to new frame
248 last_time -= end_time;
249 require( last_time >= 0 );
250
251 last_dmc_time -= end_time;
252 require( last_dmc_time >= 0 );
253
254 if ( next_irq != no_irq ) {
255 next_irq -= end_time;
256 assert( next_irq >= 0 );
257 }
258 if ( dmc.next_irq != no_irq ) {
259 dmc.next_irq -= end_time;
260 assert( dmc.next_irq >= 0 );
261 }
262 if ( earliest_irq_ != no_irq ) {
263 earliest_irq_ -= end_time;
264 if ( earliest_irq_ < 0 )
265 earliest_irq_ = 0;
266 }
267 }
268
269 // registers
270
271 static const unsigned char length_table [0x20] = {
272 0x0A, 0xFE, 0x14, 0x02, 0x28, 0x04, 0x50, 0x06,
273 0xA0, 0x08, 0x3C, 0x0A, 0x0E, 0x0C, 0x1A, 0x0E,
274 0x0C, 0x10, 0x18, 0x12, 0x30, 0x14, 0x60, 0x16,
275 0xC0, 0x18, 0x48, 0x1A, 0x10, 0x1C, 0x20, 0x1E
276 };
277
278 void Nes_Apu::write_register( nes_time_t time, nes_addr_t addr, int data )
279 {
280 require( addr > 0x20 ); // addr must be actual address (i.e. 0x40xx)
281 require( (unsigned) data <= 0xff );
282
283 // Ignore addresses outside range
284 if ( addr < start_addr || end_addr < addr )
285 return;
286
287 run_until_( time );
288
289 if ( addr < 0x4014 )
290 {
291 // Write to channel
292 int osc_index = (addr - start_addr) >> 2;
293 Nes_Osc* osc = oscs [osc_index];
294
295 int reg = addr & 3;
296 osc->regs [reg] = data;
297 osc->reg_written [reg] = true;
298
299 if ( osc_index == 4 )
300 {
301 // handle DMC specially
302 dmc.write_register( reg, data );
303 }
304 else if ( reg == 3 )
305 {
306 // load length counter
307 if ( (osc_enables >> osc_index) & 1 )
308 osc->length_counter = length_table [(data >> 3) & 0x1f];
309
310 // reset square phase
311 if ( osc_index < 2 )
312 ((Nes_Square*) osc)->phase = Nes_Square::phase_range - 1;
313 }
314 }
315 else if ( addr == 0x4015 )
316 {
317 // Channel enables
318 for ( int i = osc_count; i--; )
319 if ( !((data >> i) & 1) )
320 oscs [i]->length_counter = 0;
321
322 bool recalc_irq = dmc.irq_flag;
323 dmc.irq_flag = false;
324
325 int old_enables = osc_enables;
326 osc_enables = data;
327 if ( !(data & 0x10) ) {
328 dmc.next_irq = no_irq;
329 recalc_irq = true;
330 }
331 else if ( !(old_enables & 0x10) ) {
332 dmc.start(); // dmc just enabled
333 }
334
335 if ( recalc_irq )
336 irq_changed();
337 }
338 else if ( addr == 0x4017 )
339 {
340 // Frame mode
341 frame_mode = data;
342
343 bool irq_enabled = !(data & 0x40);
344 irq_flag &= irq_enabled;
345 next_irq = no_irq;
346
347 // mode 1
348 frame_delay = (frame_delay & 1);
349 frame = 0;
350
351 if ( !(data & 0x80) )
352 {
353 // mode 0
354 frame = 1;
355 frame_delay += frame_period;
356 if ( irq_enabled )
357 next_irq = time + frame_delay + frame_period * 3;
358 }
359
360 irq_changed();
361 }
362 }
363
364 int Nes_Apu::read_status( nes_time_t time )
365 {
366 run_until_( time - 1 );
367
368 int result = (dmc.irq_flag << 7) | (irq_flag << 6);
369
370 for ( int i = 0; i < osc_count; i++ )
371 if ( oscs [i]->length_counter )
372 result |= 1 << i;
373
374 run_until_( time );
375
376 if ( irq_flag ) {
377 irq_flag = false;
378 irq_changed();
379 }
380
381 return result;
382 }
383