1 // Game_Music_Emu $vers. http://www.slack.net/~ant/
2 
3 #include "Nes_Fds_Apu.h"
4 
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 */
15 
16 #include "blargg_source.h"
17 
18 int const fract_range = 65536;
19 
reset()20 void Nes_Fds_Apu::reset()
21 {
22 	memset( regs_, 0, sizeof regs_ );
23 	memset( mod_wave, 0, sizeof mod_wave );
24 
25 	last_time     = 0;
26 	env_delay     = 0;
27 	sweep_delay   = 0;
28 	wave_pos      = 0;
29 	last_amp      = 0;
30 	wave_fract    = fract_range;
31 	mod_fract     = fract_range;
32 	mod_pos       = 0;
33 	mod_write_pos = 0;
34 
35 	static byte const initial_regs [0x0B] = {
36 		0x80,       // disable envelope
37 		0, 0, 0xC0, // disable wave and lfo
38 		0x80,       // disable sweep
39 		0, 0, 0x80, // disable modulation
40 		0, 0, 0xFF  // LFO period // TODO: use 0xE8 as FDS ROM does?
41 	};
42 	for ( int i = 0; i < (int) sizeof initial_regs; i++ )
43 	{
44 		// two writes to set both gain and period for envelope registers
45 		write_( io_addr + wave_size + i, 0 );
46 		write_( io_addr + wave_size + i, initial_regs [i] );
47 	}
48 }
49 
write_(unsigned addr,int data)50 void Nes_Fds_Apu::write_( unsigned addr, int data )
51 {
52 	unsigned reg = addr - io_addr;
53 	if ( reg < io_size )
54 	{
55 		if ( reg < wave_size )
56 		{
57 			if ( regs (0x4089) & 0x80 )
58 				regs_ [reg] = data & wave_sample_max;
59 		}
60 		else
61 		{
62 			regs_ [reg] = data;
63 			switch ( addr )
64 			{
65 			case 0x4080:
66 				if ( data & 0x80 )
67 					env_gain = data & 0x3F;
68 				else
69 					env_speed = (data & 0x3F) + 1;
70 				break;
71 
72 			case 0x4084:
73 				if ( data & 0x80 )
74 					sweep_gain = data & 0x3F;
75 				else
76 					sweep_speed = (data & 0x3F) + 1;
77 				break;
78 
79 			case 0x4085:
80 				mod_pos = mod_write_pos;
81 				regs (0x4085) = data & 0x7F;
82 				break;
83 
84 			case 0x4088:
85 				if ( regs (0x4087) & 0x80 )
86 				{
87 					int pos = mod_write_pos;
88 					data &= 0x07;
89 					mod_wave [pos    ] = data;
90 					mod_wave [pos + 1] = data;
91 					mod_write_pos = (pos     + 2) & (wave_size - 1);
92 					mod_pos       = (mod_pos + 2) & (wave_size - 1);
93 				}
94 				break;
95 			}
96 		}
97 	}
98 }
99 
set_tempo(double t)100 void Nes_Fds_Apu::set_tempo( double t )
101 {
102 	lfo_tempo = lfo_base_tempo;
103 	if ( t != 1.0 )
104 	{
105 		lfo_tempo = int ((double) lfo_base_tempo / t + 0.5);
106 		if ( lfo_tempo <= 0 )
107 			lfo_tempo = 1;
108 	}
109 }
110 
run_until(blip_time_t final_end_time)111 void Nes_Fds_Apu::run_until( blip_time_t final_end_time )
112 {
113 	int const wave_freq = (regs (0x4083) & 0x0F) * 0x100 + regs (0x4082);
114 	Blip_Buffer* const output_ = this->output_;
115 	if ( wave_freq && output_ && !((regs (0x4089) | regs (0x4083)) & 0x80) )
116 	{
117 		output_->set_modified();
118 
119 		// master_volume
120 		#define MVOL_ENTRY( percent ) (master_vol_max * percent + 50) / 100
121 		static unsigned char const master_volumes [4] = {
122 			MVOL_ENTRY( 100 ), MVOL_ENTRY( 67 ), MVOL_ENTRY( 50 ), MVOL_ENTRY( 40 )
123 		};
124 		int const master_volume = master_volumes [regs (0x4089) & 0x03];
125 
126 		// lfo_period
127 		blip_time_t lfo_period = regs (0x408A) * lfo_tempo;
128 		if ( regs (0x4083) & 0x40 )
129 			lfo_period = 0;
130 
131 		// sweep setup
132 		blip_time_t sweep_time = last_time + sweep_delay;
133 		blip_time_t const sweep_period = lfo_period * sweep_speed;
134 		if ( !sweep_period || regs (0x4084) & 0x80 )
135 			sweep_time = final_end_time;
136 
137 		// envelope setup
138 		blip_time_t env_time = last_time + env_delay;
139 		blip_time_t const env_period = lfo_period * env_speed;
140 		if ( !env_period || regs (0x4080) & 0x80 )
141 			env_time = final_end_time;
142 
143 		// modulation
144 		int mod_freq = 0;
145 		if ( !(regs (0x4087) & 0x80) )
146 			mod_freq = (regs (0x4087) & 0x0F) * 0x100 + regs (0x4086);
147 
148 		blip_time_t end_time = last_time;
149 		do
150 		{
151 			// sweep
152 			if ( sweep_time <= end_time )
153 			{
154 				sweep_time += sweep_period;
155 				int mode = regs (0x4084) >> 5 & 2;
156 				int new_sweep_gain = sweep_gain + mode - 1;
157 				if ( (unsigned) new_sweep_gain <= (unsigned) 0x80 >> mode )
158 					sweep_gain = new_sweep_gain;
159 				else
160 					regs (0x4084) |= 0x80; // optimization only
161 			}
162 
163 			// envelope
164 			if ( env_time <= end_time )
165 			{
166 				env_time += env_period;
167 				int mode = regs (0x4080) >> 5 & 2;
168 				int new_env_gain = env_gain + mode - 1;
169 				if ( (unsigned) new_env_gain <= (unsigned) 0x80 >> mode )
170 					env_gain = new_env_gain;
171 				else
172 					regs (0x4080) |= 0x80; // optimization only
173 			}
174 
175 			// new end_time
176 			blip_time_t const start_time = end_time;
177 			end_time = final_end_time;
178 			if ( end_time > env_time   ) end_time = env_time;
179 			if ( end_time > sweep_time ) end_time = sweep_time;
180 
181 			// frequency modulation
182 			int freq = wave_freq;
183 			if ( mod_freq )
184 			{
185 				// time of next modulation clock
186 				blip_time_t mod_time = start_time + (mod_fract + mod_freq - 1) / mod_freq;
187 				if ( end_time > mod_time )
188 					end_time = mod_time;
189 
190 				// run modulator up to next clock and save old sweep_bias
191 				int sweep_bias = regs (0x4085);
192 				mod_fract -= (end_time - start_time) * mod_freq;
193 				if ( mod_fract <= 0 )
194 				{
195 					mod_fract += fract_range;
196 					check( (unsigned) mod_fract <= fract_range );
197 
198 					static short const mod_table [8] = { 0, +1, +2, +4, 0, -4, -2, -1 };
199 					int mod = mod_wave [mod_pos];
200 					mod_pos = (mod_pos + 1) & (wave_size - 1);
201 					int new_sweep_bias = (sweep_bias + mod_table [mod]) & 0x7F;
202 					if ( mod == 4 )
203 						new_sweep_bias = 0;
204 					regs (0x4085) = new_sweep_bias;
205 				}
206 
207 				// apply frequency modulation
208 				sweep_bias = (sweep_bias ^ 0x40) - 0x40;
209 				int factor = sweep_bias * sweep_gain;
210 				int extra = factor & 0x0F;
211 				factor >>= 4;
212 				if ( extra )
213 				{
214 					factor--;
215 					if ( sweep_bias >= 0 )
216 						factor += 3;
217 				}
218 				if ( factor > 193 ) factor -= 258;
219 				if ( factor < -64 ) factor += 256;
220 				freq += (freq * factor) >> 6;
221 				if ( freq <= 0 )
222 					continue;
223 			}
224 
225 			// wave
226 			int wave_fract = this->wave_fract;
227 			blip_time_t delay = (wave_fract + freq - 1) / freq;
228 			blip_time_t time = start_time + delay;
229 
230 			if ( time <= end_time )
231 			{
232 				// at least one wave clock within start_time...end_time
233 
234 				blip_time_t const min_delay = fract_range / freq;
235 				int wave_pos = this->wave_pos;
236 
237 				int volume = env_gain;
238 				if ( volume > vol_max )
239 					volume = vol_max;
240 				volume *= master_volume;
241 
242 				int const min_fract = min_delay * freq;
243 
244 				do
245 				{
246 					// clock wave
247 					int amp = regs_ [wave_pos] * volume;
248 					wave_pos = (wave_pos + 1) & (wave_size - 1);
249 					int delta = amp - last_amp;
250 					if ( delta )
251 					{
252 						last_amp = amp;
253 						synth.offset_inline( time, delta, output_ );
254 					}
255 
256 					wave_fract += fract_range - delay * freq;
257 					check( unsigned (fract_range - wave_fract) < freq );
258 
259 					// delay until next clock
260 					delay = min_delay;
261 					if ( wave_fract > min_fract )
262 						delay++;
263 					check( delay && delay == (wave_fract + freq - 1) / freq );
264 
265 					time += delay;
266 				}
267 				while ( time <= end_time ); // TODO: using < breaks things, but <= is wrong
268 
269 				this->wave_pos = wave_pos;
270 			}
271 			this->wave_fract = wave_fract - (end_time - (time - delay)) * freq;
272 			check( this->wave_fract > 0 );
273 		}
274 		while ( end_time < final_end_time );
275 
276 		env_delay   = env_time   - final_end_time; check( env_delay >= 0 );
277 		sweep_delay = sweep_time - final_end_time; check( sweep_delay >= 0 );
278 	}
279 	last_time = final_end_time;
280 }
281