1 // Gb_Snd_Emu 0.1.5. http://www.slack.net/~ant/
2
3 #include "Gb_Apu.h"
4
5 #include <string.h>
6
7 /* Copyright (C) 2003-2006 Shay Green. This module is free software; you
8 can redistribute it and/or modify it under the terms of the GNU Lesser
9 General Public License as published by the Free Software Foundation; either
10 version 2.1 of the License, or (at your option) any later version. This
11 module is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
14 details. You should have received a copy of the GNU Lesser General Public
15 License along with this module; if not, write to the Free Software Foundation,
16 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
17
18 #include "blargg_source.h"
19
20 // Gb_Osc
21
reset()22 void Gb_Osc::reset()
23 {
24 delay = 0;
25 last_amp = 0;
26 length = 0;
27 output_select = 3;
28 output = outputs [output_select];
29 }
30
clock_length()31 void Gb_Osc::clock_length()
32 {
33 if ( (regs [4] & len_enabled_mask) && length )
34 length--;
35 }
36
37 // Gb_Env
38
clock_envelope()39 void Gb_Env::clock_envelope()
40 {
41 if ( env_delay && !--env_delay )
42 {
43 env_delay = regs [2] & 7;
44 int v = volume - 1 + (regs [2] >> 2 & 2);
45 if ( (unsigned) v < 15 )
46 volume = v;
47 }
48 }
49
write_register(int reg,int data)50 bool Gb_Env::write_register( int reg, int data )
51 {
52 switch ( reg )
53 {
54 case 1:
55 length = 64 - (regs [1] & 0x3F);
56 break;
57
58 case 2:
59 if ( !(data >> 4) )
60 enabled = false;
61 break;
62
63 case 4:
64 if ( data & trigger )
65 {
66 env_delay = regs [2] & 7;
67 volume = regs [2] >> 4;
68 enabled = true;
69 if ( length == 0 )
70 length = 64;
71 return true;
72 }
73 }
74 return false;
75 }
76
77 // Gb_Square
78
reset()79 void Gb_Square::reset()
80 {
81 phase = 0;
82 sweep_freq = 0;
83 sweep_delay = 0;
84 Gb_Env::reset();
85 }
86
clock_sweep()87 void Gb_Square::clock_sweep()
88 {
89 int sweep_period = (regs [0] & period_mask) >> 4;
90 if ( sweep_period && sweep_delay && !--sweep_delay )
91 {
92 sweep_delay = sweep_period;
93 regs [3] = sweep_freq & 0xFF;
94 regs [4] = (regs [4] & ~0x07) | (sweep_freq >> 8 & 0x07);
95
96 int offset = sweep_freq >> (regs [0] & shift_mask);
97 if ( regs [0] & 0x08 )
98 offset = -offset;
99 sweep_freq += offset;
100
101 if ( sweep_freq < 0 )
102 {
103 sweep_freq = 0;
104 }
105 else if ( sweep_freq >= 2048 )
106 {
107 sweep_delay = 0; // don't modify channel frequency any further
108 sweep_freq = 2048; // silence sound immediately
109 }
110 }
111 }
112
run(blip_time_t time,blip_time_t end_time,int playing)113 void Gb_Square::run( blip_time_t time, blip_time_t end_time, int playing )
114 {
115 if ( sweep_freq == 2048 )
116 playing = false;
117
118 static unsigned char const table [4] = { 1, 2, 4, 6 };
119 int const duty = table [regs [1] >> 6];
120 int amp = volume & playing;
121 if ( phase >= duty )
122 amp = -amp;
123
124 int frequency = this->frequency();
125 if ( unsigned (frequency - 1) > 2040 ) // frequency < 1 || frequency > 2041
126 {
127 // really high frequency results in DC at half volume
128 amp = volume >> 1;
129 playing = false;
130 }
131
132 {
133 int delta = amp - last_amp;
134 if ( delta )
135 {
136 last_amp = amp;
137 synth->offset( time, delta, output );
138 }
139 }
140
141 time += delay;
142 if ( !playing )
143 time = end_time;
144
145 if ( time < end_time )
146 {
147 int const period = (2048 - frequency) * 4;
148 Blip_Buffer* const output = this->output;
149 int phase = this->phase;
150 int delta = amp * 2;
151 do
152 {
153 phase = (phase + 1) & 7;
154 if ( phase == 0 || phase == duty )
155 {
156 delta = -delta;
157 synth->offset_inline( time, delta, output );
158 }
159 time += period;
160 }
161 while ( time < end_time );
162
163 this->phase = phase;
164 last_amp = delta >> 1;
165 }
166 delay = time - end_time;
167 }
168
169 // Gb_Noise
170
run(blip_time_t time,blip_time_t end_time,int playing)171 void Gb_Noise::run( blip_time_t time, blip_time_t end_time, int playing )
172 {
173 int amp = volume & playing;
174 int tap = 13 - (regs [3] & 8);
175 if ( bits >> tap & 2 )
176 amp = -amp;
177
178 {
179 int delta = amp - last_amp;
180 if ( delta )
181 {
182 last_amp = amp;
183 synth->offset( time, delta, output );
184 }
185 }
186
187 time += delay;
188 if ( !playing )
189 time = end_time;
190
191 if ( time < end_time )
192 {
193 static unsigned char const table [8] = { 8, 16, 32, 48, 64, 80, 96, 112 };
194 int period = table [regs [3] & 7] << (regs [3] >> 4);
195
196 // keep parallel resampled time to eliminate time conversion in the loop
197 Blip_Buffer* const output = this->output;
198 const blip_resampled_time_t resampled_period =
199 output->resampled_duration( period );
200 blip_resampled_time_t resampled_time = output->resampled_time( time );
201 unsigned bits = this->bits;
202 int delta = amp * 2;
203
204 do
205 {
206 unsigned changed = (bits >> tap) + 1;
207 time += period;
208 bits <<= 1;
209 if ( changed & 2 )
210 {
211 delta = -delta;
212 bits |= 1;
213 synth->offset_resampled( resampled_time, delta, output );
214 }
215 resampled_time += resampled_period;
216 }
217 while ( time < end_time );
218
219 this->bits = bits;
220 last_amp = delta >> 1;
221 }
222 delay = time - end_time;
223 }
224
225 // Gb_Wave
226
write_register(int reg,int data)227 inline void Gb_Wave::write_register( int reg, int data )
228 {
229 switch ( reg )
230 {
231 case 0:
232 if ( !(data & 0x80) )
233 enabled = false;
234 break;
235
236 case 1:
237 length = 256 - regs [1];
238 break;
239
240 case 2:
241 volume = data >> 5 & 3;
242 break;
243
244 case 4:
245 if ( data & trigger & regs [0] )
246 {
247 wave_pos = 0;
248 enabled = true;
249 if ( length == 0 )
250 length = 256;
251 }
252 }
253 }
254
run(blip_time_t time,blip_time_t end_time,int playing)255 void Gb_Wave::run( blip_time_t time, blip_time_t end_time, int playing )
256 {
257 int volume_shift = (volume - 1) & 7; // volume = 0 causes shift = 7
258 int frequency;
259 {
260 int amp = (wave [wave_pos] >> volume_shift & playing) * 2;
261
262 frequency = this->frequency();
263 if ( unsigned (frequency - 1) > 2044 ) // frequency < 1 || frequency > 2045
264 {
265 amp = 30 >> volume_shift & playing;
266 playing = false;
267 }
268
269 int delta = amp - last_amp;
270 if ( delta )
271 {
272 last_amp = amp;
273 synth->offset( time, delta, output );
274 }
275 }
276
277 time += delay;
278 if ( !playing )
279 time = end_time;
280
281 if ( time < end_time )
282 {
283 Blip_Buffer* const output = this->output;
284 int const period = (2048 - frequency) * 2;
285 int wave_pos = (this->wave_pos + 1) & (wave_size - 1);
286
287 do
288 {
289 int amp = (wave [wave_pos] >> volume_shift) * 2;
290 wave_pos = (wave_pos + 1) & (wave_size - 1);
291 int delta = amp - last_amp;
292 if ( delta )
293 {
294 last_amp = amp;
295 synth->offset_inline( time, delta, output );
296 }
297 time += period;
298 }
299 while ( time < end_time );
300
301 this->wave_pos = (wave_pos - 1) & (wave_size - 1);
302 }
303 delay = time - end_time;
304 }
305
306 // Gb_Apu::write_osc
307
write_osc(int index,int reg,int data)308 void Gb_Apu::write_osc( int index, int reg, int data )
309 {
310 reg -= index * 5;
311 Gb_Square* sq = &square2;
312 switch ( index )
313 {
314 case 0:
315 sq = &square1;
316 case 1:
317 if ( sq->write_register( reg, data ) && index == 0 )
318 {
319 square1.sweep_freq = square1.frequency();
320 if ( (regs [0] & sq->period_mask) && (regs [0] & sq->shift_mask) )
321 {
322 square1.sweep_delay = 1; // cause sweep to recalculate now
323 square1.clock_sweep();
324 }
325 }
326 break;
327
328 case 2:
329 wave.write_register( reg, data );
330 break;
331
332 case 3:
333 if ( noise.write_register( reg, data ) )
334 noise.bits = 0x7FFF;
335 }
336 }
337