1 // Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
3 #include "nes_fds_apu.h"
5 /* Copyright (C) 2006 Shay Green. This module is free software; you
6 can redistribute it and/or modify it under the terms of the GNU Lesser
7 General Public License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version. This
9 module is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12 details. You should have received a copy of the GNU Lesser General Public
13 License along with this module; if not, write to the Free Software Foundation,
14 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
16 #include "blargg_source.h"
18 int const fract_range
= 65536;
20 void Fds_init( struct Nes_Fds_Apu
* this )
22 Synth_init( &this->synth
);
24 this->lfo_tempo
= lfo_base_tempo
;
25 Fds_set_output( this, 0, NULL
);
26 Fds_volume( this, 1.0 );
30 void Fds_reset( struct Nes_Fds_Apu
* this )
32 memset( this->regs_
, 0, sizeof this->regs_
);
33 memset( this->mod_wave
, 0, sizeof this->mod_wave
);
37 this->sweep_delay
= 0;
40 this->wave_fract
= fract_range
;
41 this->mod_fract
= fract_range
;
43 this->mod_write_pos
= 0;
45 static byte
const initial_regs
[0x0B] ICONST_ATTR
= {
46 0x80, // disable envelope
47 0, 0, 0xC0, // disable wave and lfo
48 0x80, // disable sweep
49 0, 0, 0x80, // disable modulation
50 0, 0, 0xFF // LFO period // TODO: use 0xE8 as FDS ROM does?
53 for ( i
= 0; i
< (int) sizeof initial_regs
; i
++ )
55 // two writes to set both gain and period for envelope registers
56 Fds_write_( this, fds_io_addr
+ fds_wave_size
+ i
, 0 );
57 Fds_write_( this, fds_io_addr
+ fds_wave_size
+ i
, initial_regs
[i
] );
61 void Fds_write_( struct Nes_Fds_Apu
* this, unsigned addr
, int data
)
63 unsigned reg
= addr
- fds_io_addr
;
64 if ( reg
< fds_io_size
)
66 if ( reg
< fds_wave_size
)
68 if ( *regs_nes (this, 0x4089) & 0x80 )
69 this->regs_
[reg
] = data
& fds_wave_sample_max
;
73 this->regs_
[reg
] = data
;
78 this->env_gain
= data
& 0x3F;
80 this->env_speed
= (data
& 0x3F) + 1;
85 this->sweep_gain
= data
& 0x3F;
87 this->sweep_speed
= (data
& 0x3F) + 1;
91 this->mod_pos
= this->mod_write_pos
;
92 *regs_nes (this, 0x4085) = data
& 0x7F;
96 if ( *regs_nes (this, 0x4087) & 0x80 )
98 int pos
= this->mod_write_pos
;
100 this->mod_wave
[pos
] = data
;
101 this->mod_wave
[pos
+ 1] = data
;
102 this->mod_write_pos
= (pos
+ 2) & (fds_wave_size
- 1);
103 this->mod_pos
= (this->mod_pos
+ 2) & (fds_wave_size
- 1);
111 void Fds_set_tempo( struct Nes_Fds_Apu
* this, double t
)
113 this->lfo_tempo
= lfo_base_tempo
;
116 this->lfo_tempo
= (int) ((double) lfo_base_tempo
/ t
+ 0.5);
117 if ( this->lfo_tempo
<= 0 )
122 void Fds_run_until( struct Nes_Fds_Apu
* this, blip_time_t final_end_time
)
124 int const wave_freq
= (*regs_nes (this, 0x4083) & 0x0F) * 0x100 + *regs_nes (this, 0x4082);
125 struct Blip_Buffer
* const output_
= this->output_
;
126 if ( wave_freq
&& output_
&& !((*regs_nes (this, 0x4089) | *regs_nes (this, 0x4083)) & 0x80) )
128 Blip_set_modified( output_
);
131 #define MVOL_ENTRY( percent ) (fds_master_vol_max * percent + 50) / 100
132 static unsigned char const master_volumes
[4] = {
133 MVOL_ENTRY( 100 ), MVOL_ENTRY( 67 ), MVOL_ENTRY( 50 ), MVOL_ENTRY( 40 )
135 int const master_volume
= master_volumes
[*regs_nes (this, 0x4089) & 0x03];
138 blip_time_t lfo_period
= *regs_nes (this, 0x408A) * this->lfo_tempo
;
139 if ( *regs_nes (this, 0x4083) & 0x40 )
143 blip_time_t sweep_time
= this->last_time
+ this->sweep_delay
;
144 blip_time_t
const sweep_period
= lfo_period
* this->sweep_speed
;
145 if ( !sweep_period
|| *regs_nes (this, 0x4084) & 0x80 )
146 sweep_time
= final_end_time
;
149 blip_time_t env_time
= this->last_time
+ this->env_delay
;
150 blip_time_t
const env_period
= lfo_period
* this->env_speed
;
151 if ( !env_period
|| *regs_nes (this, 0x4080) & 0x80 )
152 env_time
= final_end_time
;
156 if ( !(*regs_nes (this, 0x4087) & 0x80) )
157 mod_freq
= (*regs_nes (this, 0x4087) & 0x0F) * 0x100 + *regs_nes (this, 0x4086);
159 blip_time_t end_time
= this->last_time
;
163 if ( sweep_time
<= end_time
)
165 sweep_time
+= sweep_period
;
166 int mode
= *regs_nes (this, 0x4084) >> 5 & 2;
167 int new_sweep_gain
= this->sweep_gain
+ mode
- 1;
168 if ( (unsigned) new_sweep_gain
<= (unsigned) 0x80 >> mode
)
169 this->sweep_gain
= new_sweep_gain
;
171 *regs_nes (this, 0x4084) |= 0x80; // optimization only
175 if ( env_time
<= end_time
)
177 env_time
+= env_period
;
178 int mode
= *regs_nes (this, 0x4080) >> 5 & 2;
179 int new_env_gain
= this->env_gain
+ mode
- 1;
180 if ( (unsigned) new_env_gain
<= (unsigned) 0x80 >> mode
)
181 this->env_gain
= new_env_gain
;
183 *regs_nes (this, 0x4080) |= 0x80; // optimization only
187 blip_time_t
const start_time
= end_time
;
188 end_time
= final_end_time
;
189 if ( end_time
> env_time
) end_time
= env_time
;
190 if ( end_time
> sweep_time
) end_time
= sweep_time
;
192 // frequency modulation
193 int freq
= wave_freq
;
196 // time of next modulation clock
197 blip_time_t mod_time
= start_time
+ (this->mod_fract
+ mod_freq
- 1) / mod_freq
;
198 if ( end_time
> mod_time
)
201 // run modulator up to next clock and save old sweep_bias
202 int sweep_bias
= *regs_nes (this, 0x4085);
203 this->mod_fract
-= (end_time
- start_time
) * mod_freq
;
204 if ( this->mod_fract
<= 0 )
206 this->mod_fract
+= fract_range
;
207 check( (unsigned) this->mod_fract
<= fract_range
);
209 static short const mod_table
[8] = { 0, +1, +2, +4, 0, -4, -2, -1 };
210 int mod
= this->mod_wave
[this->mod_pos
];
211 this->mod_pos
= (this->mod_pos
+ 1) & (fds_wave_size
- 1);
212 int new_sweep_bias
= (sweep_bias
+ mod_table
[mod
]) & 0x7F;
215 *regs_nes (this, 0x4085) = new_sweep_bias
;
218 // apply frequency modulation
219 sweep_bias
= (sweep_bias
^ 0x40) - 0x40;
220 int factor
= sweep_bias
* this->sweep_gain
;
221 int extra
= factor
& 0x0F;
226 if ( sweep_bias
>= 0 )
229 if ( factor
> 193 ) factor
-= 258;
230 if ( factor
< -64 ) factor
+= 256;
231 freq
+= (freq
* factor
) >> 6;
237 int wave_fract
= this->wave_fract
;
238 blip_time_t delay
= (wave_fract
+ freq
- 1) / freq
;
239 blip_time_t time
= start_time
+ delay
;
241 if ( time
<= end_time
)
243 // at least one wave clock within start_time...end_time
245 blip_time_t
const min_delay
= fract_range
/ freq
;
246 int wave_pos
= this->wave_pos
;
248 int volume
= this->env_gain
;
249 if ( volume
> fds_vol_max
)
250 volume
= fds_vol_max
;
251 volume
*= master_volume
;
253 int const min_fract
= min_delay
* freq
;
258 int amp
= this->regs_
[wave_pos
] * volume
;
259 wave_pos
= (wave_pos
+ 1) & (fds_wave_size
- 1);
260 int delta
= amp
- this->last_amp
;
263 this->last_amp
= amp
;
264 Synth_offset_inline( &this->synth
, time
, delta
, output_
);
267 wave_fract
+= fract_range
- delay
* freq
;
268 check( unsigned (fract_range
- wave_fract
) < freq
);
270 // delay until next clock
272 if ( wave_fract
> min_fract
)
274 check( delay
&& delay
== (wave_fract
+ freq
- 1) / freq
);
278 while ( time
<= end_time
); // TODO: using < breaks things, but <= is wrong
280 this->wave_pos
= wave_pos
;
282 this->wave_fract
= wave_fract
- (end_time
- (time
- delay
)) * freq
;
283 check( this->wave_fract
> 0 );
285 while ( end_time
< final_end_time
);
287 this->env_delay
= env_time
- final_end_time
; check( env_delay
>= 0 );
288 this->sweep_delay
= sweep_time
- final_end_time
; check( sweep_delay
>= 0 );
290 this->last_time
= final_end_time
;