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