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
19Nes_Apu::Nes_Apu()
20{
21 dmc.apu = this;
22 dmc.rom_reader = NULL;
23 square1.synth = &square_synth;
24 square2.synth = &square_synth;
25 irq_notifier_ = NULL;
26
27 oscs [0] = &square1;
28 oscs [1] = &square2;
29 oscs [2] = ▵
30 oscs [3] = &noise;
31 oscs [4] = &dmc;
32
33 output( NULL );
34 volume( 1.0 );
35 reset( false );
36}
37
38Nes_Apu::~Nes_Apu()
39{
40}
41
42void Nes_Apu::treble_eq( const blip_eq_t& eq )
43{
44 square_synth.treble_eq( eq );
45 triangle.synth.treble_eq( eq );
46 noise.synth.treble_eq( eq );
47 dmc.synth.treble_eq( eq );
48}
49
50void Nes_Apu::buffer_cleared()
51{
52 square1.last_amp = 0;
53 square2.last_amp = 0;
54 triangle.last_amp = 0;
55 noise.last_amp = 0;
56 dmc.last_amp = 0;
57}
58
59void Nes_Apu::enable_nonlinear( double v )
60{
61 dmc.nonlinear = true;
62 square_synth.volume( 1.3 * 0.25751258 / 0.742467605 * 0.25 * v );
63
64 const double tnd = 0.75 / 202 * 0.48;
65 triangle.synth.volume_unit( 3 * tnd );
66 noise.synth.volume_unit( 2 * tnd );
67 dmc.synth.volume_unit( tnd );
68
69 buffer_cleared();
70}
71
72void Nes_Apu::volume( double v )
73{
74 dmc.nonlinear = false;
75 square_synth.volume( 0.1128 * v );
76 triangle.synth.volume( 0.12765 * v );
77 noise.synth.volume( 0.0741 * v );
78 dmc.synth.volume( 0.42545 * v );
79}
80
81void Nes_Apu::output( Blip_Buffer* buffer )
82{
83 for ( int i = 0; i < osc_count; i++ )
84 osc_output( i, buffer );
85}
86
87void Nes_Apu::reset( bool pal_mode, int initial_dmc_dac )
88{
89 // to do: time pal frame periods exactly
90 frame_period = pal_mode ? 8314 : 7458;
91 dmc.pal_mode = pal_mode;
92
93 square1.reset();
94 square2.reset();
95 triangle.reset();
96 noise.reset();
97 dmc.reset();
98
99 last_time = 0;
100 osc_enables = 0;
101 irq_flag = false;
102 earliest_irq_ = no_irq;
103 frame_delay = 1;
104 write_register( 0, 0x4017, 0x00 );
105 write_register( 0, 0x4015, 0x00 );
106
107 for ( cpu_addr_t addr = start_addr; addr <= 0x4013; addr++ )
108 write_register( 0, addr, (addr & 3) ? 0x00 : 0x10 );
109
110 dmc.dac = initial_dmc_dac;
111 if ( !dmc.nonlinear )
112 dmc.last_amp = initial_dmc_dac; // prevent output transition
113}
114
115void Nes_Apu::irq_changed()
116{
117 cpu_time_t new_irq = dmc.next_irq;
118 if ( dmc.irq_flag | irq_flag ) {
119 new_irq = 0;
120 }
121 else if ( new_irq > next_irq ) {
122 new_irq = next_irq;
123 }
124
125 if ( new_irq != earliest_irq_ ) {
126 earliest_irq_ = new_irq;
127 if ( irq_notifier_ )
128 irq_notifier_( irq_data );
129 }
130}
131
132// frames
133
134void Nes_Apu::run_until( cpu_time_t end_time )
135{
136 require( end_time >= last_time );
137
138 if ( end_time == last_time )
139 return;
140
141 while ( true )
142 {
143 // earlier of next frame time or end time
144 cpu_time_t time = last_time + frame_delay;
145 if ( time > end_time )
146 time = end_time;
147 frame_delay -= time - last_time;
148
149 // run oscs to present
150 square1.run( last_time, time );
151 square2.run( last_time, time );
152 triangle.run( last_time, time );
153 noise.run( last_time, time );
154 dmc.run( last_time, time );
155 last_time = time;
156
157 if ( time == end_time )
158 break; // no more frames to run
159
160 // take frame-specific actions
161 frame_delay = frame_period;
162 switch ( frame++ )
163 {
164 case 0:
165 if ( !(frame_mode & 0xc0) ) {
166 next_irq = time + frame_period * 4 + 1;
167 irq_flag = true;
168 }
169 // fall through
170 case 2:
171 // clock length and sweep on frames 0 and 2
172 square1.clock_length( 0x20 );
173 square2.clock_length( 0x20 );
174 noise.clock_length( 0x20 );
175 triangle.clock_length( 0x80 ); // different bit for halt flag on triangle
176
177 square1.clock_sweep( -1 );
178 square2.clock_sweep( 0 );
179 break;
180
181 case 1:
182 // frame 1 is slightly shorter
183 frame_delay -= 2;
184 break;
185
186 case 3:
187 frame = 0;
188
189 // frame 3 is almost twice as long in mode 1
190 if ( frame_mode & 0x80 )
191 frame_delay += frame_period - 6;
192 break;
193 }
194
195 // clock envelopes and linear counter every frame
196 triangle.clock_linear_counter();
197 square1.clock_envelope();
198 square2.clock_envelope();
199 noise.clock_envelope();
200 }
201}
202
203void Nes_Apu::end_frame( cpu_time_t end_time )
204{
205 if ( end_time > last_time )
206 run_until( end_time );
207
208 // make times relative to new frame
209 last_time -= end_time;
210 require( last_time >= 0 );
211
212 if ( next_irq != no_irq ) {
213 next_irq -= end_time;
214 assert( next_irq >= 0 );
215 }
216 if ( dmc.next_irq != no_irq ) {
217 dmc.next_irq -= end_time;
218 assert( dmc.next_irq >= 0 );
219 }
220 if ( earliest_irq_ != no_irq ) {
221 earliest_irq_ -= end_time;
222 if ( earliest_irq_ < 0 )
223 earliest_irq_ = 0;
224 }
225}
226
227// registers
228
229static const unsigned char length_table [0x20] = {
230 0x0A, 0xFE, 0x14, 0x02, 0x28, 0x04, 0x50, 0x06,
231 0xA0, 0x08, 0x3C, 0x0A, 0x0E, 0x0C, 0x1A, 0x0E,
232 0x0C, 0x10, 0x18, 0x12, 0x30, 0x14, 0x60, 0x16,
233 0xC0, 0x18, 0x48, 0x1A, 0x10, 0x1C, 0x20, 0x1E
234};
235
236void Nes_Apu::write_register( cpu_time_t time, cpu_addr_t addr, int data )
237{
238 require( addr > 0x20 ); // addr must be actual address (i.e. 0x40xx)
239 require( (unsigned) data <= 0xff );
240
241 // Ignore addresses outside range
242 if ( addr < start_addr || end_addr < addr )
243 return;
244
245 run_until( time );
246
247 if ( addr < 0x4014 )
248 {
249 // Write to channel
250 int osc_index = (addr - start_addr) >> 2;
251 Nes_Osc* osc = oscs [osc_index];
252
253 int reg = addr & 3;
254 osc->regs [reg] = data;
255 osc->reg_written [reg] = true;
256
257 if ( osc_index == 4 )
258 {
259 // handle DMC specially
260 dmc.write_register( reg, data );
261 }
262 else if ( reg == 3 )
263 {
264 // load length counter
265 if ( (osc_enables >> osc_index) & 1 )
266 osc->length_counter = length_table [(data >> 3) & 0x1f];
267
268 // reset square phase
269 if ( osc_index < 2 )
270 ((Nes_Square*) osc)->phase = Nes_Square::phase_range - 1;
271 }
272 }
273 else if ( addr == 0x4015 )
274 {
275 // Channel enables
276 for ( int i = osc_count; i--; )
277 if ( !((data >> i) & 1) )
278 oscs [i]->length_counter = 0;
279
280 bool recalc_irq = dmc.irq_flag;
281 dmc.irq_flag = false;
282
283 int old_enables = osc_enables;
284 osc_enables = data;
285 if ( !(data & 0x10) ) {
286 dmc.next_irq = no_irq;
287 recalc_irq = true;
288 }
289 else if ( !(old_enables & 0x10) ) {
290 dmc.start(); // dmc just enabled
291 }
292
293 if ( recalc_irq )
294 irq_changed();
295 }
296 else if ( addr == 0x4017 )
297 {
298 // Frame mode
299 frame_mode = data;
300
301 bool irq_enabled = !(data & 0x40);
302 irq_flag &= irq_enabled;
303 next_irq = no_irq;
304
305 // mode 1
306 frame_delay = (frame_delay & 1);
307 frame = 0;
308
309 if ( !(data & 0x80) )
310 {
311 // mode 0
312 frame = 1;
313 frame_delay += frame_period;
314 if ( irq_enabled )
315 next_irq = time + frame_delay + frame_period * 3;
316 }
317
318 irq_changed();
319 }
320}
321
322int Nes_Apu::read_status( cpu_time_t time )
323{
324 run_until( time - 1 );
325
326 int result = (dmc.irq_flag << 7) | (irq_flag << 6);
327
328 for ( int i = 0; i < osc_count; i++ )
329 if ( oscs [i]->length_counter )
330 result |= 1 << i;
331
332 run_until( time );
333
334 if ( irq_flag ) {
335 irq_flag = false;
336 irq_changed();
337 }
338
339 return result;
340}
341
342