1 // license:BSD-3-Clause
2 // copyright-holders:Angelo Salese
3 /***************************************************************************
4 
5     Flower custom sound chip
6 
7     Similar to Wiping and Namco 15xx designs
8 
9     TODO:
10     - several unknown registers (effects and unknown register tied to repeat port);
11     - repeat certainly needs a cutoff, which is unknown about how it works;
12 
13 ***************************************************************************/
14 
15 #include "emu.h"
16 #include "flower.h"
17 
18 
19 //**************************************************************************
20 //  GLOBAL VARIABLES
21 //**************************************************************************
22 
23 // device type definition
24 DEFINE_DEVICE_TYPE(FLOWER_CUSTOM, flower_sound_device, "flower_sound", "Flower Custom Sound")
25 
26 // TODO: select() unsupported by DEVICE_ADDRESS_MAP, so we need a trampoline here
regs_map(address_map & map)27 void flower_sound_device::regs_map(address_map &map)
28 {
29 	map(0x00, 0x03).select(0x38).w(FUNC(flower_sound_device::frequency_w));
30 	map(0x04, 0x04).select(0x38).w(FUNC(flower_sound_device::repeat_w));
31 	map(0x05, 0x05).select(0x38).w(FUNC(flower_sound_device::unk_w));
32 	map(0x07, 0x07).select(0x38).w(FUNC(flower_sound_device::volume_w));
33 	map(0x40, 0x45).select(0x38).w(FUNC(flower_sound_device::start_address_w));
34 	map(0x47, 0x47).select(0x38).w(FUNC(flower_sound_device::sample_trigger_w));
35 }
36 
37 //**************************************************************************
38 //  LIVE DEVICE
39 //**************************************************************************
40 
41 //-------------------------------------------------
42 //  flower_sound_device - constructor
43 //-------------------------------------------------
44 
flower_sound_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)45 flower_sound_device::flower_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
46 	: device_t(mconfig, FLOWER_CUSTOM, tag, owner, clock),
47 	  device_sound_interface(mconfig, *this),
48 	  device_memory_interface(mconfig, *this),
49 	  m_io_space_config("io", ENDIANNESS_LITTLE, 8, 7, 0, address_map_constructor(FUNC(flower_sound_device::regs_map), this)),
50 	  m_stream(nullptr),
51 	  m_mixer_lookup(nullptr),
52 	  m_last_channel(nullptr)
53 {
54 }
55 
56 
57 //-------------------------------------------------
58 //  device_start - device-specific startup
59 //-------------------------------------------------
60 
device_start()61 void flower_sound_device::device_start()
62 {
63 	m_iospace = &space(AS_IO);
64 	m_stream = stream_alloc(0, 1, clock()/2);
65 
66 	m_mixer_buffer.resize(clock()/50);
67 	make_mixer_table(MAX_VOICES, defgain);
68 
69 	m_last_channel = m_channel_list + MAX_VOICES;
70 
71 	m_sample_rom = machine().root_device().memregion("samples")->base();
72 	m_volume_rom = machine().root_device().memregion("soundvol")->base();
73 
74 	for (int i = 0; i < MAX_VOICES; i++)
75 	{
76 		save_item(NAME(m_channel_list[i].start_address), i);
77 		save_item(NAME(m_channel_list[i].position), i);
78 		save_item(NAME(m_channel_list[i].frequency), i);
79 		save_item(NAME(m_channel_list[i].volume), i);
80 		save_item(NAME(m_channel_list[i].volume_bank), i);
81 		save_item(NAME(m_channel_list[i].effect), i);
82 		save_item(NAME(m_channel_list[i].enable), i);
83 		save_item(NAME(m_channel_list[i].repeat), i);
84 
85 		// assign a channel number (debugger aid)
86 		m_channel_list[i].channel_number = i;
87 	}
88 }
89 
90 
91 /* build a table to divide by the number of voices; gain is specified as gain*16 */
make_mixer_table(int voices,int gain)92 void flower_sound_device::make_mixer_table(int voices, int gain)
93 {
94 	/* allocate memory */
95 	m_mixer_table.resize(256 * voices);
96 
97 	/* find the middle of the table */
98 	m_mixer_lookup = &m_mixer_table[128 * voices];
99 
100 	/* fill in the table - 16 bit case */
101 	for (int i = 0; i < voices * 128; i++)
102 	{
103 		int val = i * gain * 16 / voices;
104 		if (val > 32767) val = 32767;
105 		m_mixer_lookup[ i] = val;
106 		m_mixer_lookup[-i] = -val;
107 	}
108 }
109 
110 
111 
112 //-------------------------------------------------
113 //  device_reset - device-specific reset
114 //-------------------------------------------------
115 
device_reset()116 void flower_sound_device::device_reset()
117 {
118 	for (fl_sound_channel *voice = m_channel_list; voice < m_last_channel; voice++)
119 	{
120 		voice->start_address = 0;
121 		voice->position = 0;
122 		voice->volume = 0;
123 		voice->enable = false;
124 		voice->repeat = false;
125 	}
126 }
127 
sound_stream_update(sound_stream & stream,std::vector<read_stream_view> const & inputs,std::vector<write_stream_view> & outputs)128 void flower_sound_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
129 {
130 	auto &buffer = outputs[0];
131 	short *mix;
132 	uint8_t raw_sample;
133 
134 	std::fill_n(&m_mixer_buffer[0], buffer.samples(), 0);
135 
136 	for (fl_sound_channel *voice = m_channel_list; voice < m_last_channel; voice++)
137 	{
138 		int ch_volume = voice->volume;
139 		int ch_frequency = voice->frequency;
140 
141 		if (voice->enable == false)
142 			continue;
143 
144 		mix = &m_mixer_buffer[0];
145 
146 		for (int i = 0; i < buffer.samples(); i++)
147 		{
148 			if (voice->repeat == true)
149 			{
150 				raw_sample = m_sample_rom[((voice->start_address >> 7) & 0x7e00) | ((voice->position >> 7) & 0x1ff)];
151 				// guess: cut off after a number of repetitions
152 				if ((voice->position >> 7) & 0x20000)
153 				{
154 					voice->enable = false;
155 					break;
156 				}
157 			}
158 			else
159 			{
160 				raw_sample = m_sample_rom[((voice->start_address + voice->position) >> 7) & 0x7fff];
161 				if (raw_sample == 0xff)
162 				{
163 					voice->enable = false;
164 					break;
165 				}
166 			}
167 			ch_volume |= voice->volume_bank;
168 
169 			*mix++ += m_volume_rom[(ch_volume << 8 | raw_sample) & 0x3fff] - 0x80;
170 			voice->position += ch_frequency;
171 		}
172 	}
173 
174 	/* mix it down */
175 	mix = &m_mixer_buffer[0];
176 	for (int i = 0; i < buffer.samples(); i++)
177 		buffer.put_int(i, m_mixer_lookup[*mix++], 32768);
178 }
179 
180 //-------------------------------------------------
181 //  memory_space_config - return a description of
182 //  any address spaces owned by this device
183 //-------------------------------------------------
184 
memory_space_config() const185 device_memory_interface::space_config_vector flower_sound_device::memory_space_config() const
186 {
187 	return space_config_vector {
188 		std::make_pair(AS_IO, &m_io_space_config)
189 	};
190 }
191 
192 //**************************************************************************
193 //  READ/WRITE HANDLERS
194 //**************************************************************************
195 
lower_write(offs_t offset,uint8_t data)196 void flower_sound_device::lower_write(offs_t offset, uint8_t data)
197 {
198 	m_stream->update();
199 	m_iospace->write_byte(offset,data);
200 }
201 
upper_write(offs_t offset,uint8_t data)202 void flower_sound_device::upper_write(offs_t offset, uint8_t data)
203 {
204 	m_stream->update();
205 	m_iospace->write_byte(offset|0x40,data);
206 }
207 
frequency_w(offs_t offset,uint8_t data)208 void flower_sound_device::frequency_w(offs_t offset, uint8_t data)
209 {
210 	uint8_t ch = (offset >> 3) & 0x7;
211 	fl_sound_channel *voice;
212 
213 	voice = &m_channel_list[ch];
214 
215 	voice->raw_frequency[offset & 3] = data & 0xf;
216 
217 	voice->frequency = voice->raw_frequency[2] << 12;
218 	voice->frequency|= voice->raw_frequency[3] << 8;
219 	voice->frequency|= voice->raw_frequency[0] << 4;
220 	voice->frequency|= voice->raw_frequency[1] << 0;
221 }
222 
repeat_w(offs_t offset,uint8_t data)223 void flower_sound_device::repeat_w(offs_t offset, uint8_t data)
224 {
225 	uint8_t ch = (offset >> 3) & 0x7;
226 	fl_sound_channel *voice;
227 
228 	voice = &m_channel_list[ch];
229 	voice->repeat = BIT(data,4);
230 }
231 
unk_w(offs_t offset,uint8_t data)232 void flower_sound_device::unk_w(offs_t offset, uint8_t data)
233 {
234 	// same as above?
235 }
236 
volume_w(offs_t offset,uint8_t data)237 void flower_sound_device::volume_w(offs_t offset, uint8_t data)
238 {
239 	uint8_t ch = (offset >> 3) & 0x7;
240 	fl_sound_channel *voice;
241 
242 	voice = &m_channel_list[ch];
243 	voice->volume = data >> 4;
244 }
245 
start_address_w(offs_t offset,uint8_t data)246 void flower_sound_device::start_address_w(offs_t offset, uint8_t data)
247 {
248 	uint8_t ch = (offset >> 3) & 0x7;
249 	fl_sound_channel *voice;
250 
251 	voice = &m_channel_list[ch];
252 	voice->start_nibbles[offset & 7] = data & 0xf;
253 	if ((offset & 7) == 4)
254 		voice->effect = data >> 4;
255 }
256 
sample_trigger_w(offs_t offset,uint8_t data)257 void flower_sound_device::sample_trigger_w(offs_t offset, uint8_t data)
258 {
259 	uint8_t ch = (offset >> 3) & 0x7;
260 	fl_sound_channel *voice;
261 
262 	voice = &m_channel_list[ch];
263 
264 	voice->enable = true;
265 	voice->volume_bank = (data & 3) << 4;
266 	voice->start_address = 0;
267 	voice->position = 0;
268 	for (int i = 5; i >= 0; i--)
269 	{
270 		voice->start_address = (voice->start_address << 4) | voice->start_nibbles[i];
271 	}
272 }
273