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