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
7can redistribute it and/or modify it under the terms of the GNU Lesser
8General Public License as published by the Free Software Foundation; either
9version 2.1 of the License, or (at your option) any later version. This
10module is distributed in the hope that it will be useful, but WITHOUT ANY
11WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
13more details. You should have received a copy of the GNU Lesser General
14Public License along with this module; if not, write to the Free Software
15Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
16
17#include BLARGG_SOURCE_BEGIN
18
19// Nes_Osc
20
21void Nes_Osc::clock_length( int halt_mask )
22{
23 if ( length_counter && !(regs [0] & halt_mask) )
24 length_counter--;
25}
26
27void 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
42int Nes_Envelope::volume() const
43{
44 return length_counter == 0 ? 0 : (regs [0] & 0x10) ? (regs [0] & 15) : envelope;
45}
46
47// Nes_Square
48
49void 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
82void 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
155void 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
166inline 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
174void 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
226void 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
243void 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
255int 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
282static 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
290inline void Nes_Dmc::reload_sample()
291{
292 address = 0x4000 + regs [2] * 0x40;
293 length_counter = regs [3] * 0x10 + 1;
294}
295
296static 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
307void 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
331void Nes_Dmc::start()
332{
333 reload_sample();
334 fill_buffer();
335 recalc_irq();
336}
337
338void 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
361void 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
430static 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
435void 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