1 // license:BSD-3-Clause
2 // copyright-holders:Barry Rodewald
3 /*
4  * wpcsnd.c - A-12738-50003 Williams WPC Sound Board
5  *   M6809E + YM2151 + HC55516 + DAC
6  *
7  * TODO: The sound and io interface here needs to be verified from the ASIC on a real PCB, and documented better.
8  *       The WPC mainboard has two connectors used for IO/sound:
9  *       HDR 17X2 "I/O SOUND"
10  *         has the 68B09E signals:
11  *           A0-A4, D0-D7, and /FIRQ (with pullup)
12  *         it also has the WPC ASIC signals:
13  *           BLANK, WDEN, RESET
14  *       HDR 2X13 "I/O EXTEND"
15  *         has the 68B09E signals:
16  *           A5-A12, /FIRQ (with pullup (as above)), /IRQ (with pullup), /E, /Q
17  *         it also has the WPC ASIC signal:
18  *           /IO
19  *
20  *       The funhouse schematics show the A-12738-50003 WPC Sound Board to the HDR 17X2 "I/O SOUND" connector as such:
21  *       A4 A3 A2 A1 A0 WDEN R/W
22  *        0  x  x  x  x    x   x   open bus
23  *        x  0  x  x  x    x   x   open bus
24  *        x  x  0  x  x    x   x   open bus
25  *        x  x  x  x  x    1   x   open bus
26  *        1  1  1  1  *    0   *   open bus* (technically the 74LS138 is enabled here, with ** selecting between 4 unused outputs)
27  *        1  1  1  0  *    0   *   The used registers, see below
28  *        1  1  1  0  0    0   W   write to input gen_8_latch and set semamphore to assert /IRQ on the sound board 68B09E
29  *        1  1  1  0  0    0   R   read the output gen_8_latch and clear semamphore to deassert /FIRQ on the main board 68B09E
30  *        1  1  1  0  1    0   W   any write here causes the sound board to be reset
31  *        1  1  1  0  1    0   R   read from here returns the sound->mainboard semaphore status on D7, all other pins are open bus
32  *
33  *      Exactly which addresses cause the WPC ASIC to assert low the WDEN pin is not clear,
34  *      but presumably it is asserted in the 0x0x3fc0-3fdf area, meaning that the addresses
35  *      actually used are 0x3fdc and 0x3fdd (offsets 0x2c and 0x2d).
36  *      See /machine/wpc.h
37  *
38  * TODO: add generic_latch_8 devices for the two latch+semaphore pairs.
39  *
40  *  Created on: 4/10/2013
41  */
42 
43 #include "emu.h"
44 #include "wpcsnd.h"
45 #include "sound/dac.h"
46 
47 //#define VERBOSE 1
48 #include "logmacro.h"
49 
50 
51 DEFINE_DEVICE_TYPE(WPCSND, wpcsnd_device, "wpcsnd", "Williams WPC Sound")
52 
wpcsnd_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)53 wpcsnd_device::wpcsnd_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
54 	: device_t(mconfig, WPCSND, tag, owner, clock)
55 	, device_mixer_interface(mconfig, *this)
56 	, m_cpu(*this, "bgcpu")
57 	, m_ym2151(*this, "ym2151")
58 	, m_hc55516(*this, "hc55516")
59 	, m_cpubank(*this, "rombank")
60 	, m_fixedbank(*this, "fixed")
61 	, m_rom(*this, finder_base::DUMMY_TAG)
62 	, m_reply_cb(*this)
63 {
64 }
65 
wpcsnd_map(address_map & map)66 void wpcsnd_device::wpcsnd_map(address_map &map)
67 {
68 	map(0x0000, 0x1fff).ram();
69 	map(0x2000, 0x2000).mirror(0x03ff).w(FUNC(wpcsnd_device::rombank_w));
70 	map(0x2400, 0x2401).mirror(0x03fe).rw(m_ym2151, FUNC(ym2151_device::read), FUNC(ym2151_device::write));
71 	map(0x2800, 0x2800).mirror(0x03ff).w("dac", FUNC(dac_byte_interface::data_w));
72 	map(0x2c00, 0x2c00).mirror(0x03ff).w(FUNC(wpcsnd_device::bg_cvsd_clock_set_w));
73 	map(0x3000, 0x3000).mirror(0x03ff).r(FUNC(wpcsnd_device::latch_r));
74 	map(0x3400, 0x3400).mirror(0x03ff).w(FUNC(wpcsnd_device::bg_cvsd_digit_clock_clear_w));
75 	map(0x3800, 0x3800).mirror(0x03ff).w(FUNC(wpcsnd_device::volume_w));
76 	map(0x3c00, 0x3c00).mirror(0x03ff).w(FUNC(wpcsnd_device::latch_w));
77 	map(0x4000, 0xbfff).bankr("rombank");
78 	map(0xc000, 0xffff).bankr("fixed");
79 }
80 
ctrl_w(uint8_t data)81 void wpcsnd_device::ctrl_w(uint8_t data)
82 {
83 	m_cpu->pulse_input_line(INPUT_LINE_RESET, attotime::zero);
84 }
85 
data_w(uint8_t data)86 void wpcsnd_device::data_w(uint8_t data)
87 {
88 	m_latch = data;
89 	m_cpu->set_input_line(M6809_IRQ_LINE,ASSERT_LINE);
90 }
91 
ctrl_r()92 uint8_t wpcsnd_device::ctrl_r()
93 {
94 	return (m_reply_available) ? 0x01 : 0x00;
95 }
96 
data_r()97 uint8_t wpcsnd_device::data_r()
98 {
99 	m_reply_available = false;
100 	m_reply_cb(0);
101 	return m_reply;
102 }
103 
device_add_mconfig(machine_config & config)104 void wpcsnd_device::device_add_mconfig(machine_config &config)
105 {
106 	MC6809E(config, m_cpu, XTAL(8'000'000) / 4); // MC68B09E
107 	m_cpu->set_addrmap(AS_PROGRAM, &wpcsnd_device::wpcsnd_map);
108 	config.set_maximum_quantum(attotime::from_hz(50));
109 
110 	YM2151(config, m_ym2151, 3580000);
111 	m_ym2151->irq_handler().set(FUNC(wpcsnd_device::ym2151_irq_w));
112 	m_ym2151->add_route(ALL_OUTPUTS, *this, 0.25);
113 
114 	AD7524(config, "dac", 0).add_route(ALL_OUTPUTS, *this, 0.25);
115 
116 	HC55516(config, m_hc55516, 0).add_route(ALL_OUTPUTS, *this, 0.5);
117 }
118 
119 
device_start()120 void wpcsnd_device::device_start()
121 {
122 	// resolve callback
123 	m_reply_cb.resolve_safe();
124 }
125 
device_reset()126 void wpcsnd_device::device_reset()
127 {
128 	uint8_t* ROM = m_rom->base();
129 	m_cpubank->configure_entries(0, 0x80, &ROM[0], 0x8000);
130 	m_cpubank->set_entry(0);
131 	m_fixedbank->configure_entries(0, 1, &ROM[0x17c000], 0x4000);
132 	m_fixedbank->set_entry(0);
133 
134 	// reset the CPU again, so that the CPU is starting with the right vectors (otherwise sound may die on reset)
135 	m_cpu->pulse_input_line(INPUT_LINE_RESET, attotime::zero);
136 
137 	m_reply_available = false;
138 }
139 
WRITE_LINE_MEMBER(wpcsnd_device::ym2151_irq_w)140 WRITE_LINE_MEMBER( wpcsnd_device::ym2151_irq_w)
141 {
142 	m_cpu->set_input_line(M6809_FIRQ_LINE,state ? ASSERT_LINE : CLEAR_LINE);
143 }
144 
145 //------------------------------------------------------
146 //  bg_cvsd_clock_set_w - set the clk pin on the HC555xx
147 //------------------------------------------------------
148 
bg_cvsd_clock_set_w(uint8_t data)149 void wpcsnd_device::bg_cvsd_clock_set_w(uint8_t data)
150 {
151 	m_hc55516->clock_w(1);
152 }
153 
154 //----------------------------------------------------
155 //  bg_cvsd_digit_clock_clear_w - clear the clk pin on
156 //  the HC555xx and clock the data latch
157 //----------------------------------------------------
158 
bg_cvsd_digit_clock_clear_w(uint8_t data)159 void wpcsnd_device::bg_cvsd_digit_clock_clear_w(uint8_t data)
160 {
161 	m_hc55516->clock_w(0);
162 	m_hc55516->digit_w(data&1);
163 }
164 
rombank_w(uint8_t data)165 void wpcsnd_device::rombank_w(uint8_t data)
166 {
167 	uint8_t bank = data & 0x0f;
168 
169 	switch((~data) & 0xe0)
170 	{
171 	case 0x80:
172 		bank |= 0x20;
173 		break;
174 	case 0x40:
175 		bank |= 0x10;
176 		break;
177 	case 0x20:
178 		break;
179 	}
180 
181 	m_cpubank->set_entry(bank);
182 
183 	LOG("WPCSND: Bank set to %02x\n",bank);
184 }
185 
186 /// TODO: 74ls374@U39? plus 74LS74@U24B, replace this with a generic_latch_8 device
latch_r()187 uint8_t wpcsnd_device::latch_r()
188 {
189 	m_cpu->set_input_line(M6809_IRQ_LINE,CLEAR_LINE);
190 	return m_latch;
191 }
192 
193 /// TODO: 74ls374@U23? plus 74LS74@U24A, replace this with a generic_latch_8 device
latch_w(uint8_t data)194 void wpcsnd_device::latch_w(uint8_t data)
195 {
196 	m_reply_available = true;
197 	m_reply = data;
198 	m_reply_cb(1);
199 }
200 
201 /// TODO: actually implement this
volume_w(uint8_t data)202 void wpcsnd_device::volume_w(uint8_t data)
203 {
204 }
205