1 // license:BSD-3-Clause
2 // copyright-holders:Aaron Giles
3 /*************************************************************************
4 
5     Targ hardware
6 
7 *************************************************************************/
8 
9 /* Sound channel usage
10    0 = CPU music,  Shoot
11    1 = Crash
12    2 = Spectar sound
13    3 = Tone generator
14 */
15 
16 #include "emu.h"
17 #include "includes/exidy.h"
18 #include "speaker.h"
19 
20 
21 
22 #define SPECTAR_MAXFREQ     525000
23 #define TARG_MAXFREQ        125000
24 
25 static const int16_t sine_wave[32] =
26 {
27 		0x0f0f,  0x0f0f,  0x0f0f,  0x0606,  0x0606,  0x0909,  0x0909,  0x0606,  0x0606,  0x0909,  0x0606,  0x0d0d,  0x0f0f,  0x0f0f,  0x0d0d,  0x0000,
28 	-0x191a, -0x2122, -0x1e1f, -0x191a, -0x1314, -0x191a, -0x1819, -0x1819, -0x1819, -0x1314, -0x1314, -0x1314, -0x1819, -0x1e1f, -0x1e1f, -0x1819
29 };
30 
31 
32 /* some macros to make detecting bit changes easier */
33 #define RISING_EDGE(bit)  ( (data & bit) && !(m_port_1_last & bit))
34 #define FALLING_EDGE(bit) (!(data & bit) &&  (m_port_1_last & bit))
35 
36 
adjust_sample(uint8_t freq)37 void exidy_state::adjust_sample(uint8_t freq)
38 {
39 	m_tone_freq = freq;
40 
41 	if (!m_samples->playing(3))
42 	{
43 		m_samples->set_volume(3, 0);
44 		m_samples->start_raw(3, sine_wave, 32, 1000, true);
45 	}
46 
47 	if ((m_tone_freq == 0xff) || (m_tone_freq == 0x00))
48 		m_samples->set_volume(3, 0);
49 	else
50 	{
51 		m_samples->set_frequency(3, 1.0 * m_max_freq / (0xff - m_tone_freq));
52 		m_samples->set_volume(3, m_tone_active);
53 	}
54 }
55 
56 
targ_audio_1_w(uint8_t data)57 void exidy_state::targ_audio_1_w(uint8_t data)
58 {
59 	/* CPU music */
60 	if (BIT(m_port_1_last ^ data, 0))
61 		m_dac->write(BIT(data, 0));
62 
63 	/* shot */
64 	if (FALLING_EDGE(0x02) && !m_samples->playing(0))  m_samples->start(0,1);
65 	if (RISING_EDGE(0x02)) m_samples->start(0,1);
66 
67 	/* crash */
68 	if (RISING_EDGE(0x20))
69 	{
70 		if (data & 0x40)
71 			m_samples->start(1,0);
72 		else
73 			m_samples->start(1,2);
74 	}
75 
76 	/* Sspec */
77 	if (data & 0x10)
78 		m_samples->stop(2);
79 	else
80 	{
81 		if ((data & 0x08) != (m_port_1_last & 0x08))
82 		{
83 			if (data & 0x08)
84 				m_samples->start(2,3,true);
85 			else
86 				m_samples->start(2,4,true);
87 		}
88 	}
89 
90 	/* Game (tone generator enable) */
91 	if (FALLING_EDGE(0x80))
92 	{
93 		m_tone_pointer = 0;
94 		m_tone_active = 0;
95 
96 		adjust_sample(m_tone_freq);
97 	}
98 
99 	if (RISING_EDGE(0x80))
100 		m_tone_active=1;
101 
102 	m_port_1_last = data;
103 }
104 
105 
targ_audio_2_w(uint8_t data)106 void exidy_state::targ_audio_2_w(uint8_t data)
107 {
108 	if ((data & 0x01) && !(m_port_2_last & 0x01))
109 	{
110 		uint8_t *prom = memregion("targ")->base();
111 
112 		m_tone_pointer = (m_tone_pointer + 1) & 0x0f;
113 
114 		adjust_sample(prom[((data & 0x02) << 3) | m_tone_pointer]);
115 	}
116 
117 	m_port_2_last = data;
118 }
119 
120 
spectar_audio_2_w(uint8_t data)121 void exidy_state::spectar_audio_2_w(uint8_t data)
122 {
123 	adjust_sample(data);
124 }
125 
126 
127 static const char *const sample_names[] =
128 {
129 	"*targ",
130 	"expl",
131 	"shot",
132 	"sexpl",
133 	"spslow",
134 	"spfast",
135 	nullptr
136 };
137 
138 
139 
common_audio_start(int freq)140 void exidy_state::common_audio_start(int freq)
141 {
142 	m_max_freq = freq;
143 
144 	m_tone_freq = 0;
145 	m_tone_active = 0;
146 
147 	/* start_raw can't be called here: chan.source will be set by
148 	samples_device::device_start and then nulled out by samples_device::device_reset
149 	at the soft_reset stage of init_machine() and will never be set again.
150 	Thus, I've moved it to exidy_state::adjust_sample() were it will be set after
151 	machine initialization. */
152 	//m_samples->set_volume(3, 0);
153 	//m_samples->start_raw(3, sine_wave, 32, 1000, true);
154 
155 	save_item(NAME(m_port_1_last));
156 	save_item(NAME(m_port_2_last));
157 	save_item(NAME(m_tone_freq));
158 	save_item(NAME(m_tone_active));
159 }
160 
161 
SAMPLES_START_CB_MEMBER(exidy_state::spectar_audio_start)162 SAMPLES_START_CB_MEMBER(exidy_state::spectar_audio_start)
163 {
164 	common_audio_start(SPECTAR_MAXFREQ);
165 }
166 
167 
SAMPLES_START_CB_MEMBER(exidy_state::targ_audio_start)168 SAMPLES_START_CB_MEMBER(exidy_state::targ_audio_start)
169 {
170 	common_audio_start(TARG_MAXFREQ);
171 
172 	m_tone_pointer = 0;
173 
174 	save_item(NAME(m_tone_pointer));
175 }
176 
177 
spectar_audio(machine_config & config)178 void exidy_state::spectar_audio(machine_config &config)
179 {
180 	SPEAKER(config, "speaker").front_center();
181 
182 	SAMPLES(config, m_samples);
183 	m_samples->set_channels(4);
184 	m_samples->set_samples_names(sample_names);
185 	m_samples->set_samples_start_callback(FUNC(exidy_state::spectar_audio_start));
186 	m_samples->add_route(ALL_OUTPUTS, "speaker", 0.25);
187 
188 	DAC_1BIT(config, m_dac, 0).add_route(ALL_OUTPUTS, "speaker", 0.99);
189 }
190 
targ_audio(machine_config & config)191 void exidy_state::targ_audio(machine_config &config)
192 {
193 	SPEAKER(config, "speaker").front_center();
194 
195 	SAMPLES(config, m_samples);
196 	m_samples->set_channels(4);
197 	m_samples->set_samples_names(sample_names);
198 	m_samples->set_samples_start_callback(FUNC(exidy_state::targ_audio_start));
199 	m_samples->add_route(ALL_OUTPUTS, "speaker", 0.25);
200 
201 	DAC_1BIT(config, m_dac, 0).add_route(ALL_OUTPUTS, "speaker", 0.99);
202 }
203