1 // license:BSD-3-Clause
2 // copyright-holders:R. Belmont, Olivier Galibert
3 /***************************************************************************
4 
5   Sega Z80 Digital Sound Board
6 
7   used for Model 1/2/3
8 
9 ***************************************************************************/
10 
11 
12 #include "emu.h"
13 #include "audio/dsbz80.h"
14 #include "machine/clock.h"
15 
16 #define Z80_TAG "mpegcpu"
17 
dsbz80_map(address_map & map)18 void dsbz80_device::dsbz80_map(address_map &map)
19 {
20 	map(0x0000, 0x7fff).rom().region(":mpegcpu", 0);
21 	map(0x8000, 0xffff).ram();
22 }
23 
dsbz80io_map(address_map & map)24 void dsbz80_device::dsbz80io_map(address_map &map)
25 {
26 	map.global_mask(0xff);
27 	map(0xe0, 0xe0).w(FUNC(dsbz80_device::mpeg_trigger_w));
28 	map(0xe2, 0xe4).rw(FUNC(dsbz80_device::mpeg_pos_r), FUNC(dsbz80_device::mpeg_start_w));
29 	map(0xe5, 0xe7).w(FUNC(dsbz80_device::mpeg_end_w));
30 	map(0xe8, 0xe8).w(FUNC(dsbz80_device::mpeg_volume_w));
31 	map(0xe9, 0xe9).w(FUNC(dsbz80_device::mpeg_stereo_w));
32 	map(0xf0, 0xf1).rw("uart", FUNC(i8251_device::read), FUNC(i8251_device::write));
33 }
34 
35 
36 //**************************************************************************
37 //  GLOBAL VARIABLES
38 //**************************************************************************
39 
40 DEFINE_DEVICE_TYPE(DSBZ80, dsbz80_device, "dsbz80_device", "Sega Z80-based Digital Sound Board")
41 
42 
43 //-------------------------------------------------
44 //  device_add_mconfig - add device configuration
45 //-------------------------------------------------
46 
device_add_mconfig(machine_config & config)47 void dsbz80_device::device_add_mconfig(machine_config &config)
48 {
49 	Z80(config, m_ourcpu, 4000000);     // unknown clock, but probably pretty slow considering the z80 does like nothing
50 	m_ourcpu->set_addrmap(AS_PROGRAM, &dsbz80_device::dsbz80_map);
51 	m_ourcpu->set_addrmap(AS_IO, &dsbz80_device::dsbz80io_map);
52 
53 	I8251(config, m_uart, 4000000);
54 	m_uart->rxrdy_handler().set_inputline(m_ourcpu, INPUT_LINE_IRQ0);
55 	m_uart->txd_handler().set(FUNC(dsbz80_device::output_txd));
56 
57 	clock_device &uart_clock(CLOCK(config, "uart_clock", 500000)); // 16 times 31.25MHz (standard Sega/MIDI sound data rate)
58 	uart_clock.signal_handler().set("uart", FUNC(i8251_device::write_rxc));
59 	uart_clock.signal_handler().append("uart", FUNC(i8251_device::write_txc));
60 }
61 
62 //**************************************************************************
63 //  LIVE DEVICE
64 //**************************************************************************
65 
66 //-------------------------------------------------
67 //  dsbz80_device - constructor
68 //-------------------------------------------------
69 
dsbz80_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)70 dsbz80_device::dsbz80_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
71 	device_t(mconfig, DSBZ80, tag, owner, clock),
72 	device_sound_interface(mconfig, *this),
73 	m_ourcpu(*this, Z80_TAG),
74 	m_uart(*this, "uart"),
75 	m_rxd_handler(*this)
76 {
77 }
78 
79 //-------------------------------------------------
80 //  device_start - device-specific startup
81 //-------------------------------------------------
82 
device_start()83 void dsbz80_device::device_start()
84 {
85 	m_rxd_handler.resolve_safe();
86 	uint8_t *rom_base = machine().root_device().memregion("mpeg")->base();
87 	decoder = new mpeg_audio(rom_base, mpeg_audio::L2, false, 0);
88 	stream_alloc(0, 2, 32000);
89 }
90 
91 //-------------------------------------------------
92 //  device_reset - device-specific reset
93 //-------------------------------------------------
94 
device_reset()95 void dsbz80_device::device_reset()
96 {
97 	start = end = 0;
98 	audio_pos = audio_avail = 0;
99 	memset(audio_buf, 0, sizeof(audio_buf));
100 	mp_state = 0;
101 
102 	m_uart->write_cts(0);
103 }
104 
WRITE_LINE_MEMBER(dsbz80_device::write_txd)105 WRITE_LINE_MEMBER(dsbz80_device::write_txd)
106 {
107 	m_uart->write_rxd(state);
108 }
109 
WRITE_LINE_MEMBER(dsbz80_device::output_txd)110 WRITE_LINE_MEMBER(dsbz80_device::output_txd)
111 {
112 	// not used by swa
113 	m_rxd_handler(state);
114 }
115 
mpeg_trigger_w(uint8_t data)116 void dsbz80_device::mpeg_trigger_w(uint8_t data)
117 {
118 	mp_state = data;
119 
120 	if (data == 0)  // stop
121 	{
122 		mp_state = 0;
123 		audio_pos = audio_avail = 0;
124 	}
125 	else if (data == 1) // play without loop
126 	{
127 		mp_pos = mp_start*8;
128 	}
129 	else if (data == 2) // play with loop
130 	{
131 		mp_pos = mp_start*8;
132 	}
133 }
134 
mpeg_pos_r(offs_t offset)135 uint8_t dsbz80_device::mpeg_pos_r(offs_t offset)
136 {
137 	int mp_prg = mp_pos >> 3;
138 
139 	switch (offset)
140 	{
141 		case 0:
142 			return (mp_prg>>16)&0xff;
143 		case 1:
144 			return (mp_prg>>8)&0xff;
145 		case 2:
146 			return mp_prg&0xff;
147 	}
148 
149 	return 0;
150 }
151 
152 /* NOTE: writes to the start and end while playback is already in progress
153    get latched.  When the current stream ends, the MPEG hardware starts playing
154    immediately from the latched start and end position.  In this way, the Z80
155    enforces looping where appropriate and multi-part songs in other cases
156    (song #16 is a good example)
157 */
158 
mpeg_start_w(offs_t offset,uint8_t data)159 void dsbz80_device::mpeg_start_w(offs_t offset, uint8_t data)
160 {
161 	switch (offset)
162 	{
163 		case 0:
164 			start &= 0x00ffff;
165 			start |= (int)data<<16;
166 			break;
167 		case 1:
168 			start &= 0xff00ff;
169 			start |= (int)data<<8;
170 			break;
171 		case 2:
172 			start &= 0xffff00;
173 			start |= data;
174 
175 			if (mp_state == 0)
176 			{
177 				mp_start = start;
178 			}
179 			else
180 			{
181 				lp_start = start;
182 				// SWA: if loop end is zero, it means "keep previous end marker"
183 				if (lp_end == 0)
184 				{
185 //                  MPEG_Set_Loop(ROM + lp_start, mp_end-lp_start);
186 				}
187 				else
188 				{
189 //                  MPEG_Set_Loop(ROM + lp_start, lp_end-lp_start);
190 				}
191 			}
192 			break;
193 	}
194 }
195 
mpeg_end_w(offs_t offset,uint8_t data)196 void dsbz80_device::mpeg_end_w(offs_t offset, uint8_t data)
197 {
198 	switch (offset)
199 	{
200 		case 0:
201 			end &= 0x00ffff;
202 			end |= (int)data<<16;
203 			break;
204 		case 1:
205 			end &= 0xff00ff;
206 			end |= (int)data<<8;
207 			break;
208 		case 2:
209 			end &= 0xffff00;
210 			end |= data;
211 
212 			if (mp_state == 0)
213 			{
214 				mp_end = end;
215 			}
216 			else
217 			{
218 				lp_end = end;
219 //              MPEG_Set_Loop(ROM + lp_start, lp_end-lp_start);
220 			}
221 			break;
222 	}
223 }
224 
mpeg_volume_w(uint8_t data)225 void dsbz80_device::mpeg_volume_w(uint8_t data)
226 {
227 	mp_vol = 0x7f - data;
228 }
229 
mpeg_stereo_w(uint8_t data)230 void dsbz80_device::mpeg_stereo_w(uint8_t data)
231 {
232 	mp_pan = data & 3;  // 0 = stereo, 1 = left on both channels, 2 = right on both channels
233 }
234 
sound_stream_update(sound_stream & stream,std::vector<read_stream_view> const & inputs,std::vector<write_stream_view> & outputs)235 void dsbz80_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
236 {
237 	auto &out_l = outputs[0];
238 	auto &out_r = outputs[1];
239 
240 	int samples = out_l.samples();
241 	int sampindex = 0;
242 	for(;;)
243 	{
244 		while(samples && audio_pos < audio_avail)
245 		{
246 			switch (mp_pan)
247 			{
248 				case 0: // stereo
249 					out_l.put_int(sampindex, audio_buf[audio_pos*2], 32768);
250 					out_r.put_int(sampindex, audio_buf[audio_pos*2+1], 32768);
251 					sampindex++;
252 					break;
253 
254 				case 1: // left only
255 					out_l.put_int(sampindex, audio_buf[audio_pos*2], 32768);
256 					out_r.put_int(sampindex, audio_buf[audio_pos*2], 32768);
257 					sampindex++;
258 					break;
259 
260 				case 2: // right only
261 					out_l.put_int(sampindex, audio_buf[audio_pos*2+1], 32768);
262 					out_r.put_int(sampindex, audio_buf[audio_pos*2+1], 32768);
263 					sampindex++;
264 					break;
265 			}
266 			audio_pos++;
267 			samples--;
268 		}
269 
270 		if(!samples)
271 		{
272 			break;
273 		}
274 
275 		if(mp_state == 0)
276 		{
277 			out_l.fill(0, sampindex);
278 			out_r.fill(0, sampindex);
279 			break;
280 
281 		}
282 		else
283 		{
284 			int sample_rate, channel_count;
285 			bool ok = decoder->decode_buffer(mp_pos, mp_end*8, audio_buf, audio_avail, sample_rate, channel_count);
286 
287 			if (ok)
288 			{
289 				audio_pos = 0;
290 			}
291 			else
292 			{
293 				if(mp_state == 2)
294 				{
295 					if (mp_pos == lp_start*8)
296 					{
297 						// We're looping on un-decodable crap, abort abort abort
298 						mp_state = 0;
299 					}
300 					mp_pos = lp_start*8;
301 
302 					if (lp_end)
303 					{
304 						mp_end = lp_end;
305 					}
306 				}
307 				else
308 				{
309 					mp_state = 0;
310 				}
311 			}
312 		}
313 	}
314 }
315