1 // license:BSD-3-Clause
2 // copyright-holders:David Haywood,AJR
3
4 /***************************************************************************
5
6 ZSU Sound Control Unit (Proyectado 21/4/86 J. Gamell)
7 ZSU1 Sound Control Unit (Proyectado 12/6/86 J. Gamell)
8 Cedar Magnet Sound Board
9
10 The ZSU board is a component of the Z-Pinball hardware developed by
11 E.F.O. (Electrónica Funcional Operativa) S.A. of Barcelona, Spain. Its
12 sound generators are 2 AY-3-8910As and 1 OKI MSM5205, and 2 MF10s and
13 1 HC4066 are used to mix their outputs. The timing circuits are rather
14 intricate, using Z80-CTCs, HC74s and HC393s and various other gates to
15 drive both the 5205 and the SGS HCF40105BE (equivalent to CD40105B)
16 through which its samples are funneled.
17
18 There are no available schematics for the Cedar Magnet video game
19 system (also designed by E.F.O.), but its sound board is believed to be
20 a close analogue of ZSU, since it includes all of the aforementioned
21 devices. The main known difference is that the Cedar Magnet sound
22 code and data are externally loaded into 64K of RAM (2xTMM41464P-15),
23 whereas ZSU's memory map consists primarily of a bank of up to 5 27256
24 EPROMs switched from two output lines of the first 8910, overlaid with
25 a mere 2K of RAM.
26
27 irq vectors
28
29 0xe6 - from ctc0 channel 3 (vector = E0) used to drive MSM5205 through FIFO
30 0xee - from ctc0 channel 3 (vector = E8) ^^
31 0xf6 - drive AY (once per frame?) triggered by ctc1 channel 3 (vector = F0)
32 0xff - read sound latch (triggered by write from master board; default vector set by 5K/+5 pullups on D0-D7)
33
34 ***************************************************************************/
35
36 #include "emu.h"
37 #include "audio/efo_zsu.h"
38
39 #include "machine/clock.h"
40 #include "machine/input_merger.h"
41 #include "speaker.h"
42
43
44 DEFINE_DEVICE_TYPE(EFO_ZSU, efo_zsu_device, "efo_zsu", "ZSU Sound Control Unit")
45 DEFINE_DEVICE_TYPE(EFO_ZSU1, efo_zsu1_device, "efo_zsu1", "ZSU1 Sound Control Unit")
46 DEFINE_DEVICE_TYPE(CEDAR_MAGNET_SOUND, cedar_magnet_sound_device, "gedmag_sound", "Cedar Sound")
47
48
efo_zsu_device(const machine_config & mconfig,device_type type,const char * tag,device_t * owner,u32 clock)49 efo_zsu_device::efo_zsu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock)
50 : device_t(mconfig, type, tag, owner, clock)
51 , m_ctc0(*this, "ctc0")
52 , m_ctc1(*this, "ctc1")
53 , m_soundlatch(*this, "soundlatch")
54 , m_fifo(*this, "fifo")
55 , m_adpcm(*this, "adpcm")
56 {
57 }
58
59
efo_zsu_device(const machine_config & mconfig,const char * tag,device_t * owner,u32 clock)60 efo_zsu_device::efo_zsu_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
61 : efo_zsu_device(mconfig, EFO_ZSU, tag, owner, clock)
62 {
63 }
64
65
efo_zsu1_device(const machine_config & mconfig,const char * tag,device_t * owner,u32 clock)66 efo_zsu1_device::efo_zsu1_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
67 : efo_zsu_device(mconfig, EFO_ZSU1, tag, owner, clock)
68 {
69 }
70
71
cedar_magnet_sound_device(const machine_config & mconfig,const char * tag,device_t * owner,u32 clock)72 cedar_magnet_sound_device::cedar_magnet_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
73 : efo_zsu_device(mconfig, CEDAR_MAGNET_SOUND, tag, owner, clock)
74 , cedar_magnet_board_interface(mconfig, *this, "soundcpu", "ram")
75 {
76 }
77
78
sound_command_w(u8 data)79 void efo_zsu_device::sound_command_w(u8 data)
80 {
81 m_soundlatch->write(data);
82 }
83
84
85
zsu_map(address_map & map)86 void efo_zsu_device::zsu_map(address_map &map)
87 {
88 map(0x0000, 0x6fff).rom();
89 map(0x7000, 0x77ff).mirror(0x0800).ram();
90 map(0x8000, 0xffff).bankr("rombank");
91 }
92
cedar_magnet_sound_map(address_map & map)93 void cedar_magnet_sound_device::cedar_magnet_sound_map(address_map &map)
94 {
95 map(0x0000, 0xffff).ram().share("ram");
96 }
97
zsu_io(address_map & map)98 void efo_zsu_device::zsu_io(address_map &map)
99 {
100 map.global_mask(0xff);
101 map.unmap_value_high();
102
103 map(0x00, 0x03).rw(m_ctc0, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
104 map(0x04, 0x07).rw(m_ctc1, FUNC(z80ctc_device::read), FUNC(z80ctc_device::write));
105
106 map(0x08, 0x08).w(FUNC(efo_zsu_device::adpcm_fifo_w));
107
108 map(0x0c, 0x0c).w("aysnd0", FUNC(ay8910_device::address_w));
109 map(0x0d, 0x0d).w("aysnd0", FUNC(ay8910_device::data_w));
110
111 map(0x10, 0x10).w("aysnd1", FUNC(ay8910_device::address_w));
112 map(0x11, 0x11).w("aysnd1", FUNC(ay8910_device::data_w));
113
114 map(0x14, 0x14).r(m_soundlatch, FUNC(generic_latch_8_device::read));
115
116 }
117
adpcm_fifo_w(u8 data)118 void efo_zsu_device::adpcm_fifo_w(u8 data)
119 {
120 // Z80 code first unpacks 8 bytes of ADPCM sample data into nibbles
121 // and, upon receiving interrupt vector E6, fills FIFO at once using OTIR
122 // 4-bit data is shifted out of the FIFO to the MSM5205 by another timer
123 m_fifo->write(data & 0x0f); // only low nibble is used here
124 m_fifo->si_w(1);
125 m_fifo->si_w(0);
126 }
127
ay0_porta_w(u8 data)128 void cedar_magnet_sound_device::ay0_porta_w(u8 data)
129 {
130 // unknown (not in ZSU schematic); 0x80 written on reset
131 }
132
ay1_porta_w(u8 data)133 void efo_zsu_device::ay1_porta_w(u8 data)
134 {
135 m_adpcm->reset_w(data & 1);
136 if (data & 1)
137 m_fifo->reset();
138 // D4-D6 likely used to select clock for ctc0 channel 2
139 // other bits probably used to modulate analog sound output
140 }
141
WRITE_LINE_MEMBER(efo_zsu_device::ctc0_z0_w)142 WRITE_LINE_MEMBER(efo_zsu_device::ctc0_z0_w)
143 {
144 // printf("USED ctc0_z0_w %d\n", state);
145 }
146
WRITE_LINE_MEMBER(efo_zsu_device::ctc0_z1_w)147 WRITE_LINE_MEMBER(efo_zsu_device::ctc0_z1_w)
148 {
149 // printf("USED ctc0_z1_w %d\n", state);
150 }
151
WRITE_LINE_MEMBER(efo_zsu_device::ctc1_z0_w)152 WRITE_LINE_MEMBER(efo_zsu_device::ctc1_z0_w)
153 {
154 printf("ctc1_z0_w %d\n", state);
155 }
156
WRITE_LINE_MEMBER(efo_zsu_device::ctc1_z1_w)157 WRITE_LINE_MEMBER(efo_zsu_device::ctc1_z1_w)
158 {
159 printf("ctc1_z1_w %d\n", state);
160 }
161
WRITE_LINE_MEMBER(efo_zsu_device::ctc1_z2_w)162 WRITE_LINE_MEMBER(efo_zsu_device::ctc1_z2_w)
163 {
164 printf("ctc1_z2_w %d\n", state);
165 }
166
WRITE_LINE_MEMBER(efo_zsu_device::ctc0_z2_w)167 WRITE_LINE_MEMBER(efo_zsu_device::ctc0_z2_w)
168 {
169 printf("ctc0_z2_w %d\n", state);
170 }
171
WRITE_LINE_MEMBER(efo_zsu_device::fifo_dor_w)172 WRITE_LINE_MEMBER(efo_zsu_device::fifo_dor_w)
173 {
174 // combined with a clock signal and used to drive ctc0 channel 3
175 }
176
177 static const z80_daisy_config daisy_chain[] =
178 {
179 { "ctc1" },
180 { "ctc0" },
181 { nullptr }
182 };
183
TIMER_CALLBACK_MEMBER(cedar_magnet_sound_device::reset_assert_callback)184 TIMER_CALLBACK_MEMBER(cedar_magnet_sound_device::reset_assert_callback)
185 {
186 cedar_magnet_board_interface::reset_assert_callback(ptr,param);
187 // reset lines go to the ctc as well?
188 m_ctc0->reset();
189 m_ctc1->reset();
190 }
191
192
device_add_mconfig(machine_config & config)193 void efo_zsu_device::device_add_mconfig(machine_config &config)
194 {
195 z80_device& soundcpu(Z80(config, "soundcpu", 4000000));
196 soundcpu.set_addrmap(AS_PROGRAM, &efo_zsu_device::zsu_map);
197 soundcpu.set_addrmap(AS_IO, &efo_zsu_device::zsu_io);
198 soundcpu.set_daisy_config(daisy_chain);
199
200 Z80CTC(config, m_ctc0, 4000000);
201 m_ctc0->intr_callback().set("soundirq", FUNC(input_merger_device::in_w<0>));
202 m_ctc0->zc_callback<0>().set(FUNC(efo_zsu_device::ctc0_z0_w));
203 m_ctc0->zc_callback<1>().set(FUNC(efo_zsu_device::ctc0_z1_w));
204 m_ctc0->zc_callback<2>().set(FUNC(efo_zsu_device::ctc0_z2_w));
205
206 Z80CTC(config, m_ctc1, 4000000);
207 m_ctc1->intr_callback().set("soundirq", FUNC(input_merger_device::in_w<1>));
208 m_ctc1->zc_callback<0>().set(FUNC(efo_zsu_device::ctc1_z0_w));
209 m_ctc1->zc_callback<1>().set(FUNC(efo_zsu_device::ctc1_z1_w));
210 m_ctc1->zc_callback<2>().set(FUNC(efo_zsu_device::ctc1_z2_w));
211
212 #if 0 // does nothing useful now
213 clock_device &ck1mhz(CLOCK(config, "ck1mhz", 4000000/4);
214 ck1mhz.signal_handler().set(m_ctc1, FUNC(z80ctc_device::trg0));
215 ck1mhz.signal_handler().append(m_ctc1, FUNC(z80ctc_device::trg1));
216 ck1mhz.signal_handler().append(m_ctc1, FUNC(z80ctc_device::trg2));
217 #endif
218
219 GENERIC_LATCH_8(config, m_soundlatch);
220 m_soundlatch->data_pending_callback().set("soundirq", FUNC(input_merger_device::in_w<2>));
221
222 INPUT_MERGER_ANY_HIGH(config, "soundirq").output_handler().set_inputline("soundcpu", INPUT_LINE_IRQ0); // 74HC03 NAND gate
223
224 SPEAKER(config, "mono").front_center();
225
226 ay8910_device &aysnd0(AY8910(config, "aysnd0", 4000000/2));
227 aysnd0.port_a_write_callback().set_membank("rombank").mask(0x03);
228 aysnd0.add_route(ALL_OUTPUTS, "mono", 0.5);
229
230 ay8910_device &aysnd1(AY8910(config, "aysnd1", 4000000/2));
231 aysnd1.port_a_write_callback().set(FUNC(efo_zsu_device::ay1_porta_w));
232 aysnd1.add_route(ALL_OUTPUTS, "mono", 0.5);
233
234 CD40105(config, m_fifo, 0);
235 m_fifo->out_ready_cb().set(FUNC(efo_zsu_device::fifo_dor_w));
236 m_fifo->out_cb().set(m_adpcm, FUNC(msm5205_device::data_w));
237
238 MSM5205(config, m_adpcm, 4000000/8).add_route(ALL_OUTPUTS, "mono", 0.50);
239 }
240
device_add_mconfig(machine_config & config)241 void cedar_magnet_sound_device::device_add_mconfig(machine_config &config)
242 {
243 efo_zsu_device::device_add_mconfig(config);
244
245 subdevice<z80_device>("soundcpu")->set_addrmap(AS_PROGRAM, &cedar_magnet_sound_device::cedar_magnet_sound_map);
246
247 subdevice<ay8910_device>("aysnd0")->port_a_write_callback().set(FUNC(cedar_magnet_sound_device::ay0_porta_w));
248 }
249
device_start()250 void efo_zsu_device::device_start()
251 {
252 memory_bank *rombank = membank("rombank");
253 rombank->configure_entries(0, 4, &static_cast<u8 *>(memregion("soundcpu")->base())[0x8000], 0x8000);
254 rombank->set_entry(0); // 10K/GND pulldowns on banking lines
255 }
256
device_start()257 void efo_zsu1_device::device_start()
258 {
259 memory_bank *rombank = membank("rombank");
260 rombank->configure_entries(0, 4, &static_cast<u8 *>(memregion("soundcpu")->base())[0x8000], 0x8000);
261 rombank->set_entry(3); // 10K/+5 pullups on banking lines
262 }
263
device_start()264 void cedar_magnet_sound_device::device_start()
265 {
266 }
267