1 // license:BSD-3-Clause
2 // copyright-holders:Derrick Renaud,Couriersud
3 /*************************************************************************
4 
5     audio/qix.c
6 
7 *************************************************************************/
8 
9 #include "emu.h"
10 #include "includes/qix.h"
11 
12 #include "cpu/m6800/m6800.h"
13 #include "cpu/m6809/m6809.h"
14 #include "sound/discrete.h"
15 #include "sound/sn76496.h"
16 #include "speaker.h"
17 
18 
19 
20 /* Discrete Sound Input Nodes */
21 #define QIX_DAC_DATA        NODE_01
22 #define QIX_VOL_DATA        NODE_02
23 #define QIX_VOL_DATA_L      NODE_03
24 #define QIX_VOL_DATA_R      NODE_04
25 
26 
27 
28 /***************************************************************************
29 Audio handlers
30 ***************************************************************************/
31 
qix_dac_w(uint8_t data)32 void qix_state::qix_dac_w(uint8_t data)
33 {
34 	m_discrete->write(QIX_DAC_DATA, data);
35 }
36 
qix_vol_w(uint8_t data)37 void qix_state::qix_vol_w(uint8_t data)
38 {
39 	m_discrete->write(QIX_VOL_DATA, data);
40 }
41 
42 
43 /************************************************************************/
44 /* qix Sound System Analog emulation                                    */
45 /************************************************************************/
46 /*
47  * This hardware is capable of independent L/R volume control,
48  * but only sdungeon uses it for a stereo effect.
49  * Other games just use it for fixed L/R volume control.
50  *
51  * This is such a basic sound system that there is only one effect.
52  * So I won't bother keeping proper voltages levels, and will just
53  * start with the final gain.
54  */
55 
56 static const discrete_comp_adder_table qix_attn_table =
57 {
58 	DISC_COMP_P_RESISTOR, 0, 4,
59 	{RES_K(22)+250, RES_K(10)+250, RES_K(5.6)+250, RES_K(3.3)+250}
60 };
61 
62 static DISCRETE_SOUND_START(qix_discrete)
63 	/*                    NODE                      */
64 	DISCRETE_INPUTX_DATA(QIX_DAC_DATA, 128, -128*128, 128)
DISCRETE_INPUT_DATA(QIX_VOL_DATA)65 	DISCRETE_INPUT_DATA (QIX_VOL_DATA)
66 
67 	/* Separate the two 4-bit channels. */
68 	DISCRETE_TRANSFORM3(QIX_VOL_DATA_L, QIX_VOL_DATA, 16, 0x0f, "01/2&")
69 	DISCRETE_TRANSFORM2(QIX_VOL_DATA_R, QIX_VOL_DATA, 0x0f, "01&")
70 
71 	/* Work out the parallel resistance of the selected resistors. */
72 	DISCRETE_COMP_ADDER(NODE_10, QIX_VOL_DATA_L, &qix_attn_table)
73 	DISCRETE_COMP_ADDER(NODE_20, QIX_VOL_DATA_R, &qix_attn_table)
74 
75 	/* Then use it for the resistor divider network. */
76 	DISCRETE_TRANSFORM3(NODE_11, NODE_10, RES_K(10), QIX_DAC_DATA, "001+/2*")
77 	DISCRETE_TRANSFORM3(NODE_21, NODE_20, RES_K(10), QIX_DAC_DATA, "001+/2*")
78 
79 	/* If no resistors are selected (0), then the volume is full. */
80 	DISCRETE_SWITCH(NODE_12, 1, QIX_VOL_DATA_L, QIX_DAC_DATA, NODE_11)
81 	DISCRETE_SWITCH(NODE_22, 1, QIX_VOL_DATA_R, QIX_DAC_DATA, NODE_21)
82 
83 	/* Filter the DC using the lowest case filter. */
84 	DISCRETE_CRFILTER(NODE_13, NODE_12, RES_K(1.5), CAP_U(1))
85 	DISCRETE_CRFILTER(NODE_23, NODE_22, RES_K(1.5), CAP_U(1))
86 
87 	DISCRETE_OUTPUT(NODE_13, 1)
88 	DISCRETE_OUTPUT(NODE_23, 1)
89 DISCRETE_SOUND_END
90 
91 
92 
93 /*************************************
94  *
95  *  PIA handlers
96  *
97  *************************************/
98 
99 void qix_state::sndpia_2_warning_w(uint8_t data)
100 {
101 	popmessage("PIA 5 write!!");
102 }
103 
104 
TIMER_CALLBACK_MEMBER(qix_state::deferred_sndpia1_porta_w)105 TIMER_CALLBACK_MEMBER(qix_state::deferred_sndpia1_porta_w)
106 {
107 	m_sndpia1->porta_w(param);
108 }
109 
110 
sync_sndpia1_porta_w(uint8_t data)111 void qix_state::sync_sndpia1_porta_w(uint8_t data)
112 {
113 	/* we need to synchronize this so the sound CPU doesn't drop anything important */
114 	machine().scheduler().synchronize(timer_expired_delegate(FUNC(qix_state::deferred_sndpia1_porta_w), this), data);
115 }
116 
117 
slither_coinctl_w(uint8_t data)118 void qix_state::slither_coinctl_w(uint8_t data)
119 {
120 	machine().bookkeeping().coin_lockout_w(0, (~data >> 6) & 1);
121 	machine().bookkeeping().coin_counter_w(0, (data >> 5) & 1);
122 }
123 
124 
125 
126 /*************************************
127  *
128  *  IRQ generation
129  *
130  *************************************/
131 
WRITE_LINE_MEMBER(qix_state::qix_pia_dint)132 WRITE_LINE_MEMBER(qix_state::qix_pia_dint)
133 {
134 	int combined_state = m_sndpia0->irq_a_state() | m_sndpia0->irq_b_state();
135 
136 	/* DINT is connected to the data CPU's IRQ line */
137 	m_maincpu->set_input_line(M6809_IRQ_LINE, combined_state ? ASSERT_LINE : CLEAR_LINE);
138 }
139 
140 
WRITE_LINE_MEMBER(qix_state::qix_pia_sint)141 WRITE_LINE_MEMBER(qix_state::qix_pia_sint)
142 {
143 	int combined_state = m_sndpia1->irq_a_state() | m_sndpia1->irq_b_state();
144 
145 	/* SINT is connected to the sound CPU's IRQ line */
146 	m_audiocpu->set_input_line(M6802_IRQ_LINE, combined_state ? ASSERT_LINE : CLEAR_LINE);
147 }
148 
149 
150 
151 /*************************************
152  *
153  *  Audio CPU memory handlers
154  *
155  *************************************/
156 
audio_map(address_map & map)157 void qix_state::audio_map(address_map &map)
158 {
159 	map(0x2000, 0x2003).mirror(0x5ffc).rw(m_sndpia2, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
160 	map(0x4000, 0x4003).mirror(0x3ffc).rw(m_sndpia1, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
161 	map(0xd000, 0xffff).rom();
162 }
163 
164 
165 
166 
167 /*************************************
168  *
169  *  Machine drivers
170  *
171  *************************************/
172 
qix_audio(machine_config & config)173 void qix_state::qix_audio(machine_config &config)
174 {
175 	M6802(config, m_audiocpu, SOUND_CLOCK_OSC/2); /* 0.92 MHz */
176 	m_audiocpu->set_addrmap(AS_PROGRAM, &qix_state::audio_map);
177 
178 	PIA6821(config, m_sndpia0, 0);
179 	m_sndpia0->writepa_handler().set(FUNC(qix_state::sync_sndpia1_porta_w));
180 	m_sndpia0->writepb_handler().set(FUNC(qix_state::qix_vol_w));
181 	m_sndpia0->ca2_handler().set("sndpia1", FUNC(pia6821_device::ca1_w));
182 	m_sndpia0->cb2_handler().set(FUNC(qix_state::qix_flip_screen_w));
183 	m_sndpia0->irqa_handler().set(FUNC(qix_state::qix_pia_dint));
184 	m_sndpia0->irqb_handler().set(FUNC(qix_state::qix_pia_dint));
185 
186 	PIA6821(config, m_sndpia1, 0);
187 	m_sndpia1->writepa_handler().set("sndpia0", FUNC(pia6821_device::porta_w));
188 	m_sndpia1->writepb_handler().set(FUNC(qix_state::qix_dac_w));
189 	m_sndpia1->ca2_handler().set("sndpia0", FUNC(pia6821_device::ca1_w));
190 	m_sndpia1->irqa_handler().set(FUNC(qix_state::qix_pia_sint));
191 	m_sndpia1->irqb_handler().set(FUNC(qix_state::qix_pia_sint));
192 
193 	PIA6821(config, m_sndpia2, 0);
194 	m_sndpia2->writepa_handler().set(FUNC(qix_state::sndpia_2_warning_w));
195 	m_sndpia2->writepb_handler().set(FUNC(qix_state::sndpia_2_warning_w));
196 	m_sndpia2->ca2_handler().set(FUNC(qix_state::sndpia_2_warning_w));
197 	m_sndpia2->cb2_handler().set(FUNC(qix_state::sndpia_2_warning_w));
198 
199 	SPEAKER(config, "lspeaker").front_left();
200 	SPEAKER(config, "rspeaker").front_right();
201 
202 	DISCRETE(config, m_discrete, qix_discrete);
203 	m_discrete->add_route(0, "lspeaker", 1.0);
204 	m_discrete->add_route(1, "rspeaker", 1.0);
205 }
206 
slither_audio(machine_config & config)207 void qix_state::slither_audio(machine_config &config)
208 {
209 	PIA6821(config, m_sndpia0, 0);
210 	m_sndpia0->readpa_handler().set_ioport("P2");
211 	m_sndpia0->writepb_handler().set(FUNC(qix_state::slither_coinctl_w));
212 	m_sndpia0->cb2_handler().set(FUNC(qix_state::qix_flip_screen_w));
213 	m_sndpia0->irqa_handler().set(FUNC(qix_state::qix_pia_dint));
214 	m_sndpia0->irqb_handler().set(FUNC(qix_state::qix_pia_dint));
215 
216 	SPEAKER(config, "mono").front_center();
217 
218 	SN76489(config, m_sn1, SLITHER_CLOCK_OSC/4/4);
219 	m_sn1->add_route(ALL_OUTPUTS, "mono", 0.50);
220 
221 	SN76489(config, m_sn2, SLITHER_CLOCK_OSC/4/4);
222 	m_sn2->add_route(ALL_OUTPUTS, "mono", 0.50);
223 }
224