1 // license:BSD-3-Clause
2 // copyright-holders:Wilbert Pol
3 /**************************************************************************************
4
5 Wonderswan sound emulation
6
7 Wilbert Pol
8
9 Sound emulation is preliminary and not complete
10
11
12 The noise taps and behavior are the same as the Virtual Boy.
13
14 **************************************************************************************/
15
16 #include "emu.h"
17 #include "wswan.h"
18
19
20 // device type definition
21 DEFINE_DEVICE_TYPE(WSWAN_SND, wswan_sound_device, "wswan_sound", "WonderSwan Custom Sound")
22
23
24 //**************************************************************************
25 // LIVE DEVICE
26 //**************************************************************************
27
28 //-------------------------------------------------
29 // wswan_sound_device - constructor
30 //-------------------------------------------------
31
wswan_sound_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)32 wswan_sound_device::wswan_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
33 : device_t(mconfig, WSWAN_SND, tag, owner, clock),
34 device_sound_interface(mconfig, *this),
35 device_rom_interface(mconfig, *this),
36 m_channel(nullptr),
37 m_sweep_step(0),
38 m_sweep_time(0),
39 m_sweep_count(0),
40 m_noise_type(0),
41 m_noise_reset(0),
42 m_noise_enable(0),
43 m_noise_output(0),
44 m_sample_address(0),
45 m_audio2_voice(0),
46 m_audio3_sweep(0),
47 m_audio4_noise(0),
48 m_mono(0),
49 m_output_volume(0),
50 m_external_stereo(0),
51 m_external_speaker(0),
52 m_noise_shift(0),
53 m_master_volume(0)
54 {
55 }
56
57 constexpr int clk_div = 64;
58
59 //-------------------------------------------------
60 // device_start - device-specific startup
61 //-------------------------------------------------
62
device_start()63 void wswan_sound_device::device_start()
64 {
65 m_channel = stream_alloc(0, 2, clock() / clk_div);
66
67 save_item(NAME(m_sweep_step));
68 save_item(NAME(m_sweep_time));
69 save_item(NAME(m_sweep_count));
70 save_item(NAME(m_noise_type));
71 save_item(NAME(m_noise_reset));
72 save_item(NAME(m_noise_enable));
73 save_item(NAME(m_sample_address));
74 save_item(NAME(m_audio2_voice));
75 save_item(NAME(m_audio3_sweep));
76 save_item(NAME(m_audio4_noise));
77 save_item(NAME(m_mono));
78 save_item(NAME(m_output_volume));
79 save_item(NAME(m_external_stereo));
80 save_item(NAME(m_external_speaker));
81 save_item(NAME(m_noise_shift));
82 save_item(NAME(m_master_volume));
83
84 save_item(NAME(m_audio1.freq));
85 save_item(NAME(m_audio1.period));
86 save_item(NAME(m_audio1.pos));
87 save_item(NAME(m_audio1.vol_left));
88 save_item(NAME(m_audio1.vol_right));
89 save_item(NAME(m_audio1.on));
90 save_item(NAME(m_audio1.offset));
91 save_item(NAME(m_audio1.signal));
92
93 save_item(NAME(m_audio2.freq));
94 save_item(NAME(m_audio2.period));
95 save_item(NAME(m_audio2.pos));
96 save_item(NAME(m_audio2.vol_left));
97 save_item(NAME(m_audio2.vol_right));
98 save_item(NAME(m_audio2.on));
99 save_item(NAME(m_audio2.offset));
100 save_item(NAME(m_audio2.signal));
101
102 save_item(NAME(m_audio3.freq));
103 save_item(NAME(m_audio3.period));
104 save_item(NAME(m_audio3.pos));
105 save_item(NAME(m_audio3.vol_left));
106 save_item(NAME(m_audio3.vol_right));
107 save_item(NAME(m_audio3.on));
108 save_item(NAME(m_audio3.offset));
109 save_item(NAME(m_audio3.signal));
110
111 save_item(NAME(m_audio4.freq));
112 save_item(NAME(m_audio4.period));
113 save_item(NAME(m_audio4.pos));
114 save_item(NAME(m_audio4.vol_left));
115 save_item(NAME(m_audio4.vol_right));
116 save_item(NAME(m_audio4.on));
117 save_item(NAME(m_audio4.offset));
118 save_item(NAME(m_audio4.signal));
119 }
120
device_clock_changed()121 void wswan_sound_device::device_clock_changed()
122 {
123 m_channel->set_sample_rate(clock() / clk_div);
124 }
125
rom_bank_updated()126 void wswan_sound_device::rom_bank_updated()
127 {
128 m_channel->update();
129 }
130
131 //-------------------------------------------------
132 // device_reset
133 //-------------------------------------------------
134
device_reset()135 void wswan_sound_device::device_reset()
136 {
137 m_audio1.on = 0;
138 m_audio1.signal = 0;
139 m_audio1.offset = 0;
140 m_audio1.pos = 0;
141 m_audio2.on = 0;
142 m_audio2.signal = 0;
143 m_audio2.offset = 0;
144 m_audio2.pos = 0;
145 m_audio3.on = 0;
146 m_audio3.signal = 0;
147 m_audio3.offset = 0;
148 m_audio3.pos = 0;
149 m_audio4.on = 0;
150 m_audio4.signal = 0;
151 m_audio4.offset = 0;
152 m_audio4.pos = 0;
153 m_noise_output = 0;
154 }
155
fetch_sample(int channel,int offset)156 int wswan_sound_device::fetch_sample(int channel, int offset)
157 {
158 uint8_t b = read_byte(m_sample_address + ((channel & 3) << 4) + ((offset & 0x1f) >> 1));
159
160 if (offset & 1)
161 return b >> 4;
162 else
163 return b & 0xf;
164 }
165
166 //-------------------------------------------------
167 // sound_stream_update - handle a stream update
168 //-------------------------------------------------
169
sound_stream_update(sound_stream & stream,std::vector<read_stream_view> const & inputs,std::vector<write_stream_view> & outputs)170 void wswan_sound_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
171 {
172 s32 sample, left, right;
173
174 auto &outputl = outputs[0];
175 auto &outputr = outputs[1];
176 for (int sampindex = 0; sampindex < outputl.samples(); sampindex++)
177 {
178 left = right = 0;
179
180 if ( m_audio1.on )
181 {
182 sample = m_audio1.signal;
183 m_audio1.pos += clk_div;
184 if (m_audio1.pos >= m_audio1.period)
185 {
186 m_audio1.pos -= m_audio1.period;
187 m_audio1.signal = fetch_sample(0, m_audio1.offset++);
188 }
189 left += m_audio1.vol_left * sample;
190 right += m_audio1.vol_right * sample;
191 }
192
193 if ( m_audio2.on )
194 {
195 if ( m_audio2_voice )
196 {
197 uint8_t voice_data = m_audio2.vol_left << 4 | m_audio2.vol_right;
198 left += voice_data * (m_master_volume & 0x0f);
199 right += voice_data * (m_master_volume & 0x0f);
200 }
201 else
202 {
203 sample = m_audio2.signal;
204 m_audio2.pos += clk_div;
205 if (m_audio2.pos >= m_audio2.period)
206 {
207 m_audio2.pos -= m_audio2.period;
208 m_audio2.signal = fetch_sample(1, m_audio2.offset++);
209 }
210 left += m_audio2.vol_left * sample;
211 right += m_audio2.vol_right * sample;
212 }
213 }
214
215 if ( m_audio3.on )
216 {
217 sample = m_audio3.signal;
218 m_audio3.pos += clk_div;
219 if (m_audio3.pos >= m_audio3.period)
220 {
221 m_audio3.pos -= m_audio3.period;
222 m_audio3.signal = fetch_sample(2, m_audio3.offset++);
223 }
224 if ( m_audio3_sweep && m_sweep_time )
225 {
226 m_sweep_count += clk_div;
227 if ( m_sweep_count >= m_sweep_time )
228 {
229 m_sweep_count -= m_sweep_time;
230 m_audio3.freq += m_sweep_step;
231 m_audio3.freq &= 0x7ff;
232 m_audio3.period = 2048 - m_audio3.freq;
233 }
234 }
235 left += m_audio3.vol_left * sample;
236 right += m_audio3.vol_right * sample;
237 }
238
239 if ( m_audio4.on )
240 {
241 sample = m_audio4.signal;
242 m_audio4.pos += clk_div;
243 if (m_audio4.pos >= m_audio4.period)
244 {
245 if (m_audio4_noise)
246 m_audio4.signal = m_noise_output ? 0xf : 0;
247 else
248 m_audio4.signal = fetch_sample(3, m_audio4.offset++);
249
250 m_audio4.pos -= m_audio4.period;
251
252 if (m_noise_reset)
253 {
254 m_noise_reset = 0;
255 m_noise_shift = 0;
256 m_noise_output = 0;
257 }
258
259 if (m_noise_enable)
260 {
261 static int shift_bit[] = { 14, 10, 13, 4, 8, 6, 9, 11 };
262
263 m_noise_output = (1 ^ (m_noise_shift >> 7) ^ (m_noise_shift >> shift_bit[m_noise_type])) & 1;
264 m_noise_shift = m_noise_shift << 1 | m_noise_output;
265 }
266 }
267 left += m_audio4.vol_left * sample;
268 right += m_audio4.vol_right * sample;
269 }
270
271 outputl.put_int(sampindex, left, 32768 >> 5);
272 outputr.put_int(sampindex, right, 32768 >> 5);
273 }
274 }
275
276
wswan_ch_set_freq(CHAN * ch,uint16_t freq)277 void wswan_sound_device::wswan_ch_set_freq( CHAN *ch, uint16_t freq )
278 {
279 freq &= 0x7ff; // docs say freq is 11bits and a few games (Morita Shougi, World Stadium + others) write 0x800 causing a divide by 0 crash
280 ch->freq = freq;
281 ch->period = 2048 - freq;
282 }
283
port_w(offs_t offset,uint8_t data)284 void wswan_sound_device::port_w(offs_t offset, uint8_t data)
285 {
286 m_channel->update();
287
288 switch( offset )
289 {
290 case 0x80: /* Audio 1 freq (lo) */
291 wswan_ch_set_freq(&m_audio1, (m_audio1.freq & 0xff00) | data);
292 break;
293
294 case 0x81: /* Audio 1 freq (hi) */
295 wswan_ch_set_freq(&m_audio1, (data << 8 ) | (m_audio1.freq & 0x00ff));
296 break;
297
298 case 0x82: /* Audio 2 freq (lo) */
299 wswan_ch_set_freq(&m_audio2, (m_audio2.freq & 0xff00) | data);
300 break;
301
302 case 0x83: /* Audio 2 freq (hi) */
303 wswan_ch_set_freq(&m_audio2, (data << 8 ) | (m_audio2.freq & 0x00ff));
304 break;
305
306 case 0x84: /* Audio 3 freq (lo) */
307 wswan_ch_set_freq(&m_audio3, (m_audio3.freq & 0xff00) | data);
308 break;
309
310 case 0x85: /* Audio 3 freq (hi) */
311 wswan_ch_set_freq(&m_audio3, (data << 8) | (m_audio3.freq & 0x00ff));
312 break;
313
314 case 0x86: /* Audio 4 freq (lo) */
315 wswan_ch_set_freq(&m_audio4, (m_audio4.freq & 0xff00) | data);
316 break;
317
318 case 0x87: /* Audio 4 freq (hi) */
319 wswan_ch_set_freq(&m_audio4, (data << 8) | (m_audio4.freq & 0x00ff));
320 break;
321
322 case 0x88: /* Audio 1 volume */
323 m_audio1.vol_left = ( data & 0xF0 ) >> 4;
324 m_audio1.vol_right = data & 0x0F;
325 break;
326
327 case 0x89: /* Audio 2 volume */
328 m_audio2.vol_left = ( data & 0xF0 ) >> 4;
329 m_audio2.vol_right = data & 0x0F;
330 break;
331
332 case 0x8A: /* Audio 3 volume */
333 m_audio3.vol_left = ( data & 0xF0 ) >> 4;
334 m_audio3.vol_right = data & 0x0F;
335 break;
336
337 case 0x8B: /* Audio 4 volume */
338 m_audio4.vol_left = ( data & 0xF0 ) >> 4;
339 m_audio4.vol_right = data & 0x0F;
340 break;
341
342 case 0x8C: /* Sweep step */
343 m_sweep_step = (int8_t)data;
344 break;
345
346 case 0x8D: /* Sweep time */
347 m_sweep_time = 8192 * (data + 1);
348 break;
349
350 case 0x8E: /* Noise control */
351 m_noise_type = data & 0x07;
352 m_noise_reset = ( data & 0x08 ) >> 3;
353 m_noise_enable = ( data & 0x10 ) >> 4;
354 break;
355
356 case 0x8F: /* Sample location */
357 m_sample_address = data << 6;
358 break;
359
360 case 0x90: /* Audio control */
361 m_audio1.on = data & 0x01;
362 m_audio2.on = ( data & 0x02 ) >> 1;
363 m_audio3.on = ( data & 0x04 ) >> 2;
364 m_audio4.on = ( data & 0x08 ) >> 3;
365 m_audio2_voice = ( data & 0x20 ) >> 5;
366 m_audio3_sweep = ( data & 0x40 ) >> 6;
367 m_audio4_noise = ( data & 0x80 ) >> 7;
368 break;
369
370 case 0x91: /* Audio output */
371 m_mono = data & 0x01;
372 m_output_volume = ( data & 0x06 ) >> 1;
373 m_external_stereo = ( data & 0x08 ) >> 3;
374 m_external_speaker = 1;
375 break;
376
377 case 0x92: /* Noise counter shift register (lo) */
378 m_noise_shift = ( m_noise_shift & 0xFF00 ) | data;
379 break;
380
381 case 0x93: /* Noise counter shift register (hi) */
382 m_noise_shift = ( ( data & 0x7f ) << 8 ) | ( m_noise_shift & 0x00FF );
383 break;
384
385 case 0x94: /* Master volume */
386 m_master_volume = data;
387 break;
388 }
389 }
390