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