1 | |
2 | // Nes_Snd_Emu 0.1.7. http://www.slack.net/~ant/libs/ |
3 | |
4 | #include "Nes_Apu.h" |
5 | |
6 | /* Copyright (C) 2003-2005 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 | // Nes_Osc |
20 | |
21 | void Nes_Osc::clock_length( int halt_mask ) |
22 | { |
23 | if ( length_counter && !(regs [0] & halt_mask) ) |
24 | length_counter--; |
25 | } |
26 | |
27 | void Nes_Envelope::clock_envelope() |
28 | { |
29 | int period = regs [0] & 15; |
30 | if ( reg_written [3] ) { |
31 | reg_written [3] = false; |
32 | env_delay = period; |
33 | envelope = 15; |
34 | } |
35 | else if ( --env_delay < 0 ) { |
36 | env_delay = period; |
37 | if ( envelope | (regs [0] & 0x20) ) |
38 | envelope = (envelope - 1) & 15; |
39 | } |
40 | } |
41 | |
42 | int Nes_Envelope::volume() const |
43 | { |
44 | return length_counter == 0 ? 0 : (regs [0] & 0x10) ? (regs [0] & 15) : envelope; |
45 | } |
46 | |
47 | // Nes_Square |
48 | |
49 | void Nes_Square::clock_sweep( int negative_adjust ) |
50 | { |
51 | int sweep = regs [1]; |
52 | |
53 | if ( --sweep_delay < 0 ) |
54 | { |
55 | reg_written [1] = true; |
56 | |
57 | int period = this->period(); |
58 | int shift = sweep & shift_mask; |
59 | if ( shift && (sweep & 0x80) && period >= 8 ) |
60 | { |
61 | int offset = period >> shift; |
62 | |
63 | if ( sweep & negate_flag ) |
64 | offset = negative_adjust - offset; |
65 | |
66 | if ( period + offset < 0x800 ) |
67 | { |
68 | period += offset; |
69 | // rewrite period |
70 | regs [2] = period & 0xff; |
71 | regs [3] = (regs [3] & ~7) | ((period >> 8) & 7); |
72 | } |
73 | } |
74 | } |
75 | |
76 | if ( reg_written [1] ) { |
77 | reg_written [1] = false; |
78 | sweep_delay = (sweep >> 4) & 7; |
79 | } |
80 | } |
81 | |
82 | void Nes_Square::run( cpu_time_t time, cpu_time_t end_time ) |
83 | { |
84 | if ( !output ) |
85 | return; |
86 | |
87 | const int volume = this->volume(); |
88 | const int period = this->period(); |
89 | int offset = period >> (regs [1] & shift_mask); |
90 | if ( regs [1] & negate_flag ) |
91 | offset = 0; |
92 | |
93 | const int timer_period = (period + 1) * 2; |
94 | if ( volume == 0 || period < 8 || (period + offset) >= 0x800 ) |
95 | { |
96 | if ( last_amp ) { |
97 | synth->offset( time, -last_amp, output ); |
98 | last_amp = 0; |
99 | } |
100 | |
101 | time += delay; |
102 | if ( time < end_time ) |
103 | { |
104 | // maintain proper phase |
105 | int count = (end_time - time + timer_period - 1) / timer_period; |
106 | phase = (phase + count) & (phase_range - 1); |
107 | time += (long) count * timer_period; |
108 | } |
109 | } |
110 | else |
111 | { |
112 | // handle duty select |
113 | int duty_select = (regs [0] >> 6) & 3; |
114 | int duty = 1 << duty_select; // 1, 2, 4, 2 |
115 | int amp = 0; |
116 | if ( duty_select == 3 ) { |
117 | duty = 2; // negated 25% |
118 | amp = volume; |
119 | } |
120 | if ( phase < duty ) |
121 | amp ^= volume; |
122 | |
123 | int delta = update_amp( amp ); |
124 | if ( delta ) |
125 | synth->offset( time, delta, output ); |
126 | |
127 | time += delay; |
128 | if ( time < end_time ) |
129 | { |
130 | Blip_Buffer* const output = this->output; |
131 | const Synth* synth = this->synth; |
132 | int delta = amp * 2 - volume; |
133 | int phase = this->phase; |
134 | |
135 | do { |
136 | phase = (phase + 1) & (phase_range - 1); |
137 | if ( phase == 0 || phase == duty ) { |
138 | delta = -delta; |
139 | synth->offset_inline( time, delta, output ); |
140 | } |
141 | time += timer_period; |
142 | } |
143 | while ( time < end_time ); |
144 | |
145 | last_amp = (delta + volume) >> 1; |
146 | this->phase = phase; |
147 | } |
148 | } |
149 | |
150 | delay = time - end_time; |
151 | } |
152 | |
153 | // Nes_Triangle |
154 | |
155 | void Nes_Triangle::clock_linear_counter() |
156 | { |
157 | if ( reg_written [3] ) |
158 | linear_counter = regs [0] & 0x7f; |
159 | else if ( linear_counter ) |
160 | linear_counter--; |
161 | |
162 | if ( !(regs [0] & 0x80) ) |
163 | reg_written [3] = false; |
164 | } |
165 | |
166 | inline int Nes_Triangle::calc_amp() const |
167 | { |
168 | int amp = phase_range - phase; |
169 | if ( amp < 0 ) |
170 | amp = phase - (phase_range + 1); |
171 | return amp; |
172 | } |
173 | |
174 | void Nes_Triangle::run( cpu_time_t time, cpu_time_t end_time ) |
175 | { |
176 | if ( !output ) |
177 | return; |
178 | |
179 | // to do: track phase when period < 3 |
180 | // to do: Output 7.5 on dac when period < 2? More accurate, but results in more clicks. |
181 | |
182 | int delta = update_amp( calc_amp() ); |
183 | if ( delta ) |
184 | synth.offset( time, delta, output ); |
185 | |
186 | time += delay; |
187 | const int timer_period = period() + 1; |
188 | if ( length_counter == 0 || linear_counter == 0 || timer_period < 3 ) |
189 | { |
190 | time = end_time; |
191 | } |
192 | else if ( time < end_time ) |
193 | { |
194 | Blip_Buffer* const output = this->output; |
195 | |
196 | int phase = this->phase; |
197 | int volume = 1; |
198 | if ( phase > phase_range ) { |
199 | phase -= phase_range; |
200 | volume = -volume; |
201 | } |
202 | |
203 | do { |
204 | if ( --phase == 0 ) { |
205 | phase = phase_range; |
206 | volume = -volume; |
207 | } |
208 | else { |
209 | synth.offset_inline( time, volume, output ); |
210 | } |
211 | |
212 | time += timer_period; |
213 | } |
214 | while ( time < end_time ); |
215 | |
216 | if ( volume < 0 ) |
217 | phase += phase_range; |
218 | this->phase = phase; |
219 | last_amp = calc_amp(); |
220 | } |
221 | delay = time - end_time; |
222 | } |
223 | |
224 | // Nes_Dmc |
225 | |
226 | void Nes_Dmc::reset() |
227 | { |
228 | address = 0; |
229 | dac = 0; |
230 | buf = 0; |
231 | bits_remain = 1; |
232 | bits = 0; |
233 | buf_empty = true; |
234 | silence = true; |
235 | next_irq = Nes_Apu::no_irq; |
236 | irq_flag = false; |
237 | irq_enabled = false; |
238 | |
239 | Nes_Osc::reset(); |
240 | period = 0x036; |
241 | } |
242 | |
243 | void Nes_Dmc::recalc_irq() |
244 | { |
245 | cpu_time_t irq = Nes_Apu::no_irq; |
246 | if ( irq_enabled && length_counter ) |
247 | irq = apu->last_time + delay + |
248 | ((length_counter - 1) * 8 + bits_remain - 1) * cpu_time_t (period) + 1; |
249 | if ( irq != next_irq ) { |
250 | next_irq = irq; |
251 | apu->irq_changed(); |
252 | } |
253 | } |
254 | |
255 | int Nes_Dmc::count_reads( cpu_time_t time, cpu_time_t* last_read ) const |
256 | { |
257 | if ( last_read ) |
258 | *last_read = time; |
259 | |
260 | if ( length_counter == 0 ) |
261 | return 0; // not reading |
262 | |
263 | long first_read = apu->last_time + delay + long (bits_remain - 1) * period; |
264 | long avail = time - first_read; |
265 | if ( avail <= 0 ) |
266 | return 0; |
267 | |
268 | int count = (avail - 1) / (period * 8) + 1; |
269 | if ( !(regs [0] & loop_flag) && count > length_counter ) |
270 | count = length_counter; |
271 | |
272 | if ( last_read ) { |
273 | *last_read = first_read + (count - 1) * (period * 8) + 1; |
274 | assert( *last_read <= time ); |
275 | assert( count == count_reads( *last_read, NULL ) ); |
276 | assert( count - 1 == count_reads( *last_read - 1, NULL ) ); |
277 | } |
278 | |
279 | return count; |
280 | } |
281 | |
282 | static const short dmc_period_table [2] [16] = { |
283 | 0x1ac, 0x17c, 0x154, 0x140, 0x11e, 0x0fe, 0x0e2, 0x0d6, // NTSC |
284 | 0x0be, 0x0a0, 0x08e, 0x080, 0x06a, 0x054, 0x048, 0x036, |
285 | |
286 | 0x18e, 0x161, 0x13c, 0x129, 0x10a, 0x0ec, 0x0d2, 0x0c7, // PAL (totally untested) |
287 | 0x0b1, 0x095, 0x084, 0x077, 0x062, 0x04e, 0x043, 0x032 // to do: verify PAL periods |
288 | }; |
289 | |
290 | inline void Nes_Dmc::reload_sample() |
291 | { |
292 | address = 0x4000 + regs [2] * 0x40; |
293 | length_counter = regs [3] * 0x10 + 1; |
294 | } |
295 | |
296 | static const unsigned char dac_table [128] = { |
297 | 0, 0, 1, 2, 2, 3, 3, 4, 5, 5, 6, 7, 7, 8, 8, 9, |
298 | 10, 10, 11, 11, 12, 13, 13, 14, 14, 15, 15, 16, 17, 17, 18, 18, |
299 | 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26, |
300 | 27, 27, 28, 28, 29, 29, 30, 30, 31, 31, 32, 32, 32, 33, 33, 34, |
301 | 34, 35, 35, 35, 36, 36, 37, 37, 38, 38, 38, 39, 39, 40, 40, 40, |
302 | 41, 41, 42, 42, 42, 43, 43, 44, 44, 44, 45, 45, 45, 46, 46, 47, |
303 | 47, 47, 48, 48, 48, 49, 49, 49, 50, 50, 50, 51, 51, 51, 52, 52, |
304 | 52, 53, 53, 53, 54, 54, 54, 55, 55, 55, 56, 56, 56, 57, 57, 57 |
305 | }; |
306 | |
307 | void Nes_Dmc::write_register( int addr, int data ) |
308 | { |
309 | if ( addr == 0 ) { |
310 | period = dmc_period_table [pal_mode] [data & 15]; |
311 | irq_enabled = (data & 0xc0) == 0x80; // enabled only if loop disabled |
312 | irq_flag &= irq_enabled; |
313 | recalc_irq(); |
314 | } |
315 | else if ( addr == 1 ) |
316 | { |
317 | if ( !nonlinear ) |
318 | { |
319 | // adjust last_amp so that "pop" amplitude will be properly non-linear |
320 | // with respect to change in dac |
321 | int old_amp = dac_table [dac]; |
322 | dac = data & 0x7F; |
323 | int diff = dac_table [dac] - old_amp; |
324 | last_amp = dac - diff; |
325 | } |
326 | |
327 | dac = data & 0x7F; |
328 | } |
329 | } |
330 | |
331 | void Nes_Dmc::start() |
332 | { |
333 | reload_sample(); |
334 | fill_buffer(); |
335 | recalc_irq(); |
336 | } |
337 | |
338 | void Nes_Dmc::fill_buffer() |
339 | { |
340 | if ( buf_empty && length_counter ) |
341 | { |
342 | require( rom_reader ); // rom_reader must be set |
343 | buf = rom_reader( rom_reader_data, 0x8000u + address ); |
344 | address = (address + 1) & 0x7FFF; |
345 | buf_empty = false; |
346 | if ( --length_counter == 0 ) |
347 | { |
348 | if ( regs [0] & loop_flag ) { |
349 | reload_sample(); |
350 | } |
351 | else { |
352 | apu->osc_enables &= ~0x10; |
353 | irq_flag = irq_enabled; |
354 | next_irq = Nes_Apu::no_irq; |
355 | apu->irq_changed(); |
356 | } |
357 | } |
358 | } |
359 | } |
360 | |
361 | void Nes_Dmc::run( cpu_time_t time, cpu_time_t end_time ) |
362 | { |
363 | if ( !output ) |
364 | return; |
365 | |
366 | int delta = update_amp( dac ); |
367 | if ( delta ) |
368 | synth.offset( time, delta, output ); |
369 | |
370 | time += delay; |
371 | if ( time < end_time ) |
372 | { |
373 | int bits_remain = this->bits_remain; |
374 | if ( silence && buf_empty ) |
375 | { |
376 | int count = (end_time - time + period - 1) / period; |
377 | bits_remain = (bits_remain - 1 + 8 - (count % 8)) % 8 + 1; |
378 | time += count * period; |
379 | } |
380 | else |
381 | { |
382 | Blip_Buffer* const output = this->output; |
383 | const int period = this->period; |
384 | int bits = this->bits; |
385 | int dac = this->dac; |
386 | |
387 | do |
388 | { |
389 | if ( !silence ) |
390 | { |
391 | const int step = (bits & 1) * 4 - 2; |
392 | bits >>= 1; |
393 | if ( unsigned (dac + step) <= 0x7F ) { |
394 | dac += step; |
395 | synth.offset_inline( time, step, output ); |
396 | } |
397 | } |
398 | |
399 | time += period; |
400 | |
401 | if ( --bits_remain == 0 ) |
402 | { |
403 | bits_remain = 8; |
404 | if ( buf_empty ) { |
405 | silence = true; |
406 | } |
407 | else { |
408 | silence = false; |
409 | bits = buf; |
410 | buf_empty = true; |
411 | fill_buffer(); |
412 | } |
413 | } |
414 | } |
415 | while ( time < end_time ); |
416 | |
417 | this->dac = dac; |
418 | this->last_amp = dac; |
419 | this->bits = bits; |
420 | } |
421 | this->bits_remain = bits_remain; |
422 | } |
423 | delay = time - end_time; |
424 | } |
425 | |
426 | // Nes_Noise |
427 | |
428 | #include BLARGG_ENABLE_OPTIMIZER |
429 | |
430 | static const short noise_period_table [16] = { |
431 | 0x004, 0x008, 0x010, 0x020, 0x040, 0x060, 0x080, 0x0A0, |
432 | 0x0CA, 0x0FE, 0x17C, 0x1FC, 0x2FA, 0x3F8, 0x7F2, 0xFE4 |
433 | }; |
434 | |
435 | void Nes_Noise::run( cpu_time_t time, cpu_time_t end_time ) |
436 | { |
437 | if ( !output ) |
438 | return; |
439 | |
440 | const int volume = this->volume(); |
441 | int amp = (noise & 1) ? volume : 0; |
442 | int delta = update_amp( amp ); |
443 | if ( delta ) |
444 | synth.offset( time, delta, output ); |
445 | |
446 | time += delay; |
447 | if ( time < end_time ) |
448 | { |
449 | const int mode_flag = 0x80; |
450 | |
451 | int period = noise_period_table [regs [2] & 15]; |
452 | if ( !volume ) |
453 | { |
454 | // round to next multiple of period |
455 | time += (end_time - time + period - 1) / period * period; |
456 | |
457 | // approximate noise cycling while muted, by shuffling up noise register |
458 | // to do: precise muted noise cycling? |
459 | if ( !(regs [2] & mode_flag) ) { |
460 | int feedback = (noise << 13) ^ (noise << 14); |
461 | noise = (feedback & 0x4000) | (noise >> 1); |
462 | } |
463 | } |
464 | else |
465 | { |
466 | Blip_Buffer* const output = this->output; |
467 | |
468 | // using resampled time avoids conversion in synth.offset() |
469 | Blip_Buffer::resampled_time_t rperiod = output->resampled_duration( period ); |
470 | Blip_Buffer::resampled_time_t rtime = output->resampled_time( time ); |
471 | |
472 | int noise = this->noise; |
473 | int delta = amp * 2 - volume; |
474 | const int tap = (regs [2] & mode_flag ? 8 : 13); |
475 | |
476 | do { |
477 | int feedback = (noise << tap) ^ (noise << 14); |
478 | time += period; |
479 | |
480 | if ( (noise + 1) & 2 ) { |
481 | // bits 0 and 1 of noise differ |
482 | delta = -delta; |
483 | synth.offset_resampled( rtime, delta, output ); |
484 | } |
485 | |
486 | rtime += rperiod; |
487 | noise = (feedback & 0x4000) | (noise >> 1); |
488 | } |
489 | while ( time < end_time ); |
490 | |
491 | last_amp = (delta + volume) >> 1; |
492 | this->noise = noise; |
493 | } |
494 | } |
495 | |
496 | delay = time - end_time; |
497 | } |
498 | |
499 | |