1 // license:BSD-3-Clause
2 // copyright-holders:David Haywood
3
4 // Format not understood, it is not OKI ADPCM or IMA ADPCM, maybe something more basic?
5
6 #include "emu.h"
7 #include "elan_eu3a05.h"
8
9 DEFINE_DEVICE_TYPE(ELAN_EU3A05_SOUND, elan_eu3a05_sound_device, "elan_eu3a05sound", "Elan EU3A05 / EU3A14 Sound")
10
11 #define LOG_AUDIO (1U << 1)
12
13 #define LOG_ALL (LOG_AUDIO)
14
15 #define VERBOSE (0)
16 #include "logmacro.h"
17
18
elan_eu3a05_sound_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)19 elan_eu3a05_sound_device::elan_eu3a05_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
20 device_t(mconfig, ELAN_EU3A05_SOUND, tag, owner, clock),
21 device_sound_interface(mconfig, *this),
22 device_memory_interface(mconfig, *this),
23 m_space_config("regs", ENDIANNESS_NATIVE, 8, 6, 0, address_map_constructor(FUNC(elan_eu3a05_sound_device::map), this)),
24 m_stream(nullptr),
25 m_space_read_cb(*this),
26 m_sound_end_cb(*this)
27 {
28 }
29
memory_space_config() const30 device_memory_interface::space_config_vector elan_eu3a05_sound_device::memory_space_config() const
31 {
32 return space_config_vector {
33 std::make_pair(0, &m_space_config)
34 };
35 }
36
map(address_map & map)37 void elan_eu3a05_sound_device::map(address_map &map)
38 {
39 map(0x00, 0x3f).rw(FUNC(elan_eu3a05_sound_device::read_unmapped), FUNC(elan_eu3a05_sound_device::write_unmapped));
40
41 map(0x00, 0x11).rw(FUNC(elan_eu3a05_sound_device::elan_eu3a05_sound_addr_r), FUNC(elan_eu3a05_sound_device::elan_eu3a05_sound_addr_w)); // 6 * 24-bit (3 byte) channel addresses
42 map(0x12, 0x23).rw(FUNC(elan_eu3a05_sound_device::elan_eu3a05_sound_size_r), FUNC(elan_eu3a05_sound_device::elan_eu3a05_sound_size_w)); // 6 * 24-bit (3 byte) channel lengths
43 map(0x24, 0x24).rw(FUNC(elan_eu3a05_sound_device::reg50a4_r), FUNC(elan_eu3a05_sound_device::reg50a4_w)); // unknown TODO
44 map(0x25, 0x25).rw(FUNC(elan_eu3a05_sound_device::elan_eu3a05_sound_trigger_r), FUNC(elan_eu3a05_sound_device::elan_eu3a05_sound_trigger_w));
45 map(0x26, 0x27).rw(FUNC(elan_eu3a05_sound_device::elan_eu3a05_sound_volume_r), FUNC(elan_eu3a05_sound_device::elan_eu3a05_sound_volume_w)); // 0x26 = volume channels 0,1,2,3 0x27 = volume channels 5,6 (lunar rescue sets 0x03 0x00 and just uses a single channel)
46 map(0x28, 0x28).r(FUNC(elan_eu3a05_sound_device::elan_eu3a05_50a8_r)); // stopped status? (read only?)
47 map(0x29, 0x29).rw(FUNC(elan_eu3a05_sound_device::reg50a9_r), FUNC(elan_eu3a05_sound_device::reg50a9_w)); // IRQ mask?
48
49 // no other reads/writes seen?
50 }
51
52
53
device_start()54 void elan_eu3a05_sound_device::device_start()
55 {
56 m_space_read_cb.resolve_safe(0);
57 m_stream = stream_alloc(0, 1, 8000);
58
59 m_sound_end_cb.resolve_all_safe();
60
61 save_item(NAME(m_sound_byte_address));
62 save_item(NAME(m_sound_byte_len));
63 save_item(NAME(m_sound_current_nib_pos));
64 save_item(NAME(m_isstopped));
65 save_item(NAME(m_sound_trigger));
66 save_item(NAME(m_sound_unk));
67 save_item(NAME(m_volumes));
68 save_item(NAME(m_50a4));
69 save_item(NAME(m_50a9));
70 }
71
device_reset()72 void elan_eu3a05_sound_device::device_reset()
73 {
74 for (int i = 0; i < 6; i++)
75 {
76 m_sound_byte_address[i] = 0;
77 m_sound_byte_len[i] = 0;
78 m_sound_current_nib_pos[i] = 0;
79 m_adpcm[i].reset();
80 }
81
82 m_isstopped = 0x3f;
83
84 m_sound_trigger = 0x00;
85 m_sound_unk = 0x00;
86
87 m_volumes[0] = 0xff;
88 m_volumes[1] = 0x0f;
89
90 m_50a4 = 0x00;
91 m_50a9 = 0x00;
92 }
93
94 //-------------------------------------------------
95 // sound_stream_update - handle a stream update
96 //-------------------------------------------------
97
sound_stream_update(sound_stream & stream,std::vector<read_stream_view> const & inputs,std::vector<write_stream_view> & outputs)98 void elan_eu3a05_sound_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
99 {
100 // reset the output stream
101 outputs[0].fill(0);
102
103 int volume = m_volumes[0] | (m_volumes[1] << 8);
104
105 int outpos = 0;
106 // loop while we still have samples to generate
107 int samples = outputs[0].samples();
108 while (samples-- != 0)
109 {
110 int total = 0;
111 for (int channel = 0; channel < 6; channel++)
112 {
113 if (!((m_isstopped >> channel) & 1))
114 {
115 //LOGMASKED( LOG_AUDIO, "m_isstopped %02x channel %d is active %08x %06x\n", m_isstopped, channel, m_sound_byte_address[channel], m_sound_current_nib_pos[channel]);
116
117 int readoffset = m_sound_byte_address[channel] + (m_sound_current_nib_pos[channel] / 2);
118
119 int nibble = m_space_read_cb(readoffset);
120
121 nibble = nibble >> ((m_sound_current_nib_pos[channel] & 1) ? 0 : 4);
122 uint16_t decoded = (uint16_t)m_adpcm[channel].clock(nibble & 0xf);
123 decoded = (decoded * ((volume >> (channel * 2)) & 3)) / 3;
124 decoded <<= 4;
125
126 total += (int32_t)(int16_t)decoded - 0x8000;
127
128 m_sound_current_nib_pos[channel]++;
129
130 if (m_sound_current_nib_pos[channel] >= m_sound_byte_len[channel] * 2)
131 {
132 m_sound_current_nib_pos[channel] = 0;
133 m_adpcm[channel].reset();
134 m_isstopped |= (1 << channel);
135
136 // maybe, seems to match the system IRQ mask when the sound interrupts are enabled?
137 if (m_50a9 & (1 << channel))
138 m_sound_end_cb[channel](1); // generate interrupt based on which channel just stopped?
139 }
140 }
141 else
142 {
143 //LOGMASKED( LOG_AUDIO, "m_isstopped %02x channel %d is NOT active %08x %06x\n", m_isstopped, channel, m_sound_byte_address[channel], m_sound_current_nib_pos[channel]);
144 }
145 }
146 outputs[0].put_int(outpos, total, 32768 * 6);
147 outpos++;
148 }
149 }
150
151
152
153
handle_sound_addr_w(int which,int offset,uint8_t data)154 void elan_eu3a05_sound_device::handle_sound_addr_w(int which, int offset, uint8_t data)
155 {
156 switch (offset)
157 {
158 case 0x00:
159 m_sound_byte_address[which] = (m_sound_byte_address[which] & 0xffff00) | (data<<0);
160 LOGMASKED( LOG_AUDIO, "%s: sound_0 (%d) write lo address %02x (real address is now %08x)\n", machine().describe_context(), which, data, m_sound_byte_address[which]);
161 break;
162
163 case 0x01:
164 m_sound_byte_address[which] = (m_sound_byte_address[which] & 0xff00ff) | (data<<8);
165 LOGMASKED( LOG_AUDIO, "%s: sound_0 (%d) write md address %02x (real address is now %08x)\n", machine().describe_context(), which, data, m_sound_byte_address[which]);
166 break;
167
168 case 0x02:
169 m_sound_byte_address[which] = (m_sound_byte_address[which] & 0x00ffff) | (data<<16);
170 LOGMASKED( LOG_AUDIO, "%s: sound_0 (%d) write hi address %02x (real address is now %08x)\n", machine().describe_context(), which, data, m_sound_byte_address[which]);
171 break;
172 }
173 }
174
handle_sound_addr_r(int which,int offset)175 uint8_t elan_eu3a05_sound_device::handle_sound_addr_r(int which, int offset)
176 {
177 switch (offset)
178 {
179 case 0x00:
180 LOGMASKED( LOG_AUDIO, "%s: sound_0 (%d) read lo address\n", machine().describe_context(), which);
181 return (m_sound_byte_address[which]>>0) & 0xff;
182
183 case 0x01:
184 LOGMASKED( LOG_AUDIO, "%s: sound_0 (%d) read mid address\n", machine().describe_context(), which);
185 return (m_sound_byte_address[which]>>8) & 0xff;
186
187 case 0x02:
188 LOGMASKED( LOG_AUDIO, "%s: sound_0 (%d) read hi address\n", machine().describe_context(), which);
189 return (m_sound_byte_address[which]>>16) & 0xff;
190 }
191
192 return 0x00;
193 }
194
elan_eu3a05_sound_addr_w(offs_t offset,uint8_t data)195 void elan_eu3a05_sound_device::elan_eu3a05_sound_addr_w(offs_t offset, uint8_t data)
196 {
197 m_stream->update();
198 handle_sound_addr_w(offset / 3, offset % 3, data);
199 }
200
elan_eu3a05_sound_addr_r(offs_t offset)201 uint8_t elan_eu3a05_sound_device::elan_eu3a05_sound_addr_r(offs_t offset)
202 {
203 m_stream->update();
204 return handle_sound_addr_r(offset / 3, offset % 3);
205 }
206
handle_sound_size_w(int which,int offset,uint8_t data)207 void elan_eu3a05_sound_device::handle_sound_size_w(int which, int offset, uint8_t data)
208 {
209 switch (offset)
210 {
211 case 0x00:
212 m_sound_byte_len[which] = (m_sound_byte_len[which] & 0xffff00) | (data<<0);
213 LOGMASKED( LOG_AUDIO, "%s: sound_1 (%d) write lo size %02x (real size is now %08x)\n", machine().describe_context(), which, data, m_sound_byte_len[which]);
214 break;
215
216 case 0x01:
217 m_sound_byte_len[which] = (m_sound_byte_len[which] & 0xff00ff) | (data<<8);
218 LOGMASKED( LOG_AUDIO, "%s: sound_1 (%d) write md size %02x (real size is now %08x)\n", machine().describe_context(), which, data, m_sound_byte_len[which]);
219 break;
220
221 case 0x02:
222 m_sound_byte_len[which] = (m_sound_byte_len[which] & 0x00ffff) | (data<<16);
223 LOGMASKED( LOG_AUDIO, "%s: sound_1 (%d) write hi size %02x (real size is now %08x)\n", machine().describe_context(), which, data, m_sound_byte_len[which]);
224 break;
225 }
226 }
227
handle_sound_size_r(int which,int offset)228 uint8_t elan_eu3a05_sound_device::handle_sound_size_r(int which, int offset)
229 {
230 switch (offset)
231 {
232 case 0x00:
233 LOGMASKED( LOG_AUDIO, "%s: sound_1 (%d) read lo size\n", machine().describe_context(), which);
234 return (m_sound_byte_len[which]>>0) & 0xff;
235
236 case 0x01:
237 LOGMASKED( LOG_AUDIO, "%s: sound_1 (%d) read mid size\n", machine().describe_context(), which);
238 return (m_sound_byte_len[which]>>8) & 0xff;
239
240 case 0x02:
241 LOGMASKED( LOG_AUDIO, "%s: sound_1 (%d) read hi size\n", machine().describe_context(), which);
242 return (m_sound_byte_len[which]>>16) & 0xff;
243 }
244
245 return 0x00;
246 }
247
elan_eu3a05_sound_size_w(offs_t offset,uint8_t data)248 void elan_eu3a05_sound_device::elan_eu3a05_sound_size_w(offs_t offset, uint8_t data)
249 {
250 m_stream->update();
251 handle_sound_size_w(offset / 3, offset % 3, data);
252 }
253
elan_eu3a05_sound_size_r(offs_t offset)254 uint8_t elan_eu3a05_sound_device::elan_eu3a05_sound_size_r(offs_t offset)
255 {
256 m_stream->update();
257 return handle_sound_size_r(offset / 3, offset % 3);
258 }
259
elan_eu3a05_sound_trigger_r()260 uint8_t elan_eu3a05_sound_device::elan_eu3a05_sound_trigger_r()
261 {
262 m_stream->update();
263
264 LOGMASKED( LOG_AUDIO, "%s: sound read from trigger?\n", machine().describe_context());
265 return m_sound_trigger;
266 }
267
268
elan_eu3a05_sound_trigger_w(uint8_t data)269 void elan_eu3a05_sound_device::elan_eu3a05_sound_trigger_w(uint8_t data)
270 {
271 m_stream->update();
272
273 LOGMASKED( LOG_AUDIO, "%s: sound write to trigger? %02x\n", machine().describe_context(), data);
274 m_sound_trigger = data;
275
276 for (int i = 0; i < 6; i++)
277 {
278 int bit = (data >> i) & 1;
279
280 if (bit)
281 handle_sound_trigger(i);
282 }
283
284 if (data & 0xc0)
285 LOGMASKED( LOG_AUDIO, " UNEXPECTED BITS SET");
286 }
287
288 /* this is read/written with the same individual bits for each channel as the trigger
289 maybe related to interrupts? */
elan_eu3a05_sound_unk_r()290 uint8_t elan_eu3a05_sound_device::elan_eu3a05_sound_unk_r()
291 {
292 LOGMASKED( LOG_AUDIO, "%s: elan_eu3a05_sound_unk_r\n", machine().describe_context());
293 // don't think this reads back what was written probably a status of something instead?
294 return 0x00; //m_sound_unk;
295 }
296
elan_eu3a05_sound_unk_w(uint8_t data)297 void elan_eu3a05_sound_device::elan_eu3a05_sound_unk_w(uint8_t data)
298 {
299 LOGMASKED( LOG_AUDIO, "%s: elan_eu3a05_sound_unk_w %02x\n", machine().describe_context(), data);
300
301 for (int i = 0; i < 6; i++)
302 {
303 int bit = (data >> i) & 1;
304
305 if (bit)
306 LOGMASKED( LOG_AUDIO, "(unknown operation on channel %d)\n", i);
307 }
308
309 m_sound_unk = data;
310
311 if (data & 0xc0)
312 LOGMASKED( LOG_AUDIO, " UNEXPECTED BITS SET");
313 }
314
handle_sound_trigger(int which)315 void elan_eu3a05_sound_device::handle_sound_trigger(int which)
316 {
317 LOGMASKED( LOG_AUDIO, "Triggering operation on channel (%d) with params %08x %08x\n", which, m_sound_byte_address[which], m_sound_byte_len[which]);
318
319 if (m_isstopped & (1 << which)) // golden tee will repeatedly try to start the music on the title screen (although could depend on a status read first?)
320 {
321 if (m_sound_byte_len[which])
322 {
323 m_sound_current_nib_pos[which] = 0;
324 m_isstopped &= ~(1 << which);
325 m_adpcm[which].reset();
326 }
327 }
328 }
329
330
elan_eu3a05_50a8_r()331 uint8_t elan_eu3a05_sound_device::elan_eu3a05_50a8_r()
332 {
333 m_stream->update();
334
335 LOGMASKED( LOG_AUDIO, "%s: elan_eu3a05_50a8_r\n", machine().describe_context());
336 return m_isstopped;
337 }
338
elan_eu3a05_sound_volume_r(offs_t offset)339 uint8_t elan_eu3a05_sound_device::elan_eu3a05_sound_volume_r(offs_t offset)
340 {
341 LOGMASKED( LOG_AUDIO, "%s: sound_volume_r (offset %d, data %02x)\n", machine().describe_context(), offset, m_volumes[offset]);
342 return m_volumes[offset];
343 }
344
elan_eu3a05_sound_volume_w(offs_t offset,uint8_t data)345 void elan_eu3a05_sound_device::elan_eu3a05_sound_volume_w(offs_t offset, uint8_t data)
346 {
347 m_stream->update();
348
349 LOGMASKED( LOG_AUDIO, "%s: sound_volume_w (offset %d, data %02x)\n", machine().describe_context(), offset, data);
350 m_volumes[offset] = data;
351 }
352
read_unmapped(offs_t offset)353 uint8_t elan_eu3a05_sound_device::read_unmapped(offs_t offset)
354 {
355 LOGMASKED( LOG_AUDIO, "%s: elan_eu3a05_sound_device::read_unmapped (offset %02x)\n", machine().describe_context(), offset);
356 return 0x00;
357 }
358
write_unmapped(offs_t offset,uint8_t data)359 void elan_eu3a05_sound_device::write_unmapped(offs_t offset, uint8_t data)
360 {
361 LOGMASKED( LOG_AUDIO, "%s: elan_eu3a05_sound_device::write_unmapped (offset %02x) (data %02x)\n", machine().describe_context(), offset, data);
362 }
363
reg50a4_r()364 uint8_t elan_eu3a05_sound_device::reg50a4_r()
365 {
366 LOGMASKED( LOG_AUDIO, "%s: reg50a4_r (unknown) (data %02x)\n", machine().describe_context(), m_50a4);
367 return m_50a4;
368 }
369
reg50a4_w(uint8_t data)370 void elan_eu3a05_sound_device::reg50a4_w(uint8_t data)
371 {
372 LOGMASKED( LOG_AUDIO, "%s: reg50a4_w (unknown) (data %02x)\n", machine().describe_context(), data);
373 m_50a4 = data;
374 }
375
reg50a9_r()376 uint8_t elan_eu3a05_sound_device::reg50a9_r()
377 {
378 LOGMASKED( LOG_AUDIO, "%s: reg50a9_r (IRQ mask?) (data %02x)\n", machine().describe_context(), m_50a9);
379 return m_50a9;
380 }
381
reg50a9_w(uint8_t data)382 void elan_eu3a05_sound_device::reg50a9_w(uint8_t data)
383 {
384 LOGMASKED( LOG_AUDIO, "%s: reg50a4_w (IRQ mask?) (data %02x)\n", machine().describe_context(), data);
385 m_50a9 = data;
386 }
387
388