Mercurial > audlegacy
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 |