summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/sound/nile.cpp
blob: 345c88f2bf02928cbe52cc4e2e66453255ed7610 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
// license:BSD-3-Clause
// copyright-holders:Tomasz Slanina
/************************************
      Seta custom Nile ST-0026 chip
      sound emulation by Tomasz Slanina
      based on ST-0016 emulation

8 voices, 16 words of config data for each:

   00
   01 - sptr  ?? (always 0)
   02 - sptr  LO
   03 - sptr  HI
   04
   05 - flags? 00000000 0000?L0?   - bit 0 loops, other bits appear to be not used by the chip
   06 - freq
   07 - lsptr LO
   08
   09 - lsptr HI
   0a - leptr LO
   0b - leptr HI
   0c - eptr  LO
   0d - eptr  HI
   0e - vol R
   0f - vol L

************************************/

#include "emu.h"
#include "nile.h"

enum
{
	NILE_REG_UNK0=0,
	NILE_REG_SPTR_TOP,
	NILE_REG_SPTR_LO,
	NILE_REG_SPTR_HI,
	NILE_REG_UNK_4,
	NILE_REG_FLAGS,
	NILE_REG_FREQ,
	NILE_REG_LSPTR_LO,
	MILE_REG_UNK_8,
	NILE_REG_LSPTR_HI,
	NILE_REG_LEPTR_LO,
	NILE_REG_LEPTR_HI,
	NILE_REG_EPTR_LO,
	NILE_REG_EPTR_HI,
	NILE_REG_VOL_R,
	NILE_REG_VOL_L
};


DEFINE_DEVICE_TYPE(NILE, nile_device, "nile", "Seta ST-0026 NiLe")

nile_device::nile_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
	: device_t(mconfig, NILE, tag, owner, clock),
		device_sound_interface(mconfig, *this),
		m_stream(nullptr),
		m_sound_ram(*this, DEVICE_SELF),
		m_ctrl(0)
{
}


//-------------------------------------------------
//  device_start - device-specific startup
//-------------------------------------------------

void nile_device::device_start()
{
	m_stream = stream_alloc(0, 2, 44100);
	save_item(NAME(m_sound_regs));
	save_item(NAME(m_vpos));
	save_item(NAME(m_frac));
	save_item(NAME(m_lponce));
	save_item(NAME(m_ctrl));
}


//-------------------------------------------------
//  sound_stream_update - handle update requests
//  for our sound stream
//-------------------------------------------------

void nile_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
{
	uint8_t *sound_ram = &m_sound_ram[0];
	int v, i, snum;
	uint16_t *slot;
	int32_t mix[4800*2];
	int32_t *mixp;
	int16_t sample;
	int sptr, eptr, freq, lsptr, leptr;

	lsptr=leptr=0;

	sound_assert(outputs[0].samples() * 2 < ARRAY_LENGTH(mix));
	std::fill_n(&mix[0], outputs[0].samples()*2, 0);

	for (v = 0; v < NILE_VOICES; v++)
	{
		slot = &m_sound_regs[v * 16];

		if (m_ctrl&(1<<v))
		{
			mixp = &mix[0];

			sptr = slot[NILE_REG_SPTR_HI]<<16 | slot[NILE_REG_SPTR_LO];
			eptr = slot[NILE_REG_EPTR_HI]<<16 | slot[NILE_REG_EPTR_LO];

			freq=slot[NILE_REG_FREQ]*14;
			lsptr = slot[NILE_REG_LSPTR_HI]<<16 | slot[NILE_REG_LSPTR_LO];
			leptr = slot[NILE_REG_LEPTR_HI]<<16 | slot[NILE_REG_LEPTR_LO];

			for (snum = 0; snum < outputs[0].samples(); snum++)
			{
				sample = sound_ram[sptr + m_vpos[v]]<<8;

				*mixp++ += (sample * (int32_t)slot[NILE_REG_VOL_R]) >> 16;
				*mixp++ += (sample * (int32_t)slot[NILE_REG_VOL_L]) >> 16;

				m_frac[v] += freq;
				m_vpos[v] += m_frac[v]>>16;
				m_frac[v] &= 0xffff;

				// stop if we're at the end
				if (m_lponce[v])
				{
					// we've looped once, check loop end rather than sample end
					if ((m_vpos[v] + sptr) >= leptr)
					{
						m_vpos[v] = (lsptr - sptr);
					}
				}
				else
				{
					// not looped yet, check sample end
					if ((m_vpos[v] + sptr) >= eptr)
					{
						// code at 11d8c:
						// if bit 2 (0x4) is set, check if loop start = loop end.
						// if they are equal, clear bit 0 and don't set the loop start/end
						// registers in the NiLe.  if they aren't, set bit 0 and set
						// the loop start/end registers in the NiLe.
						if ((slot[NILE_REG_FLAGS] & 0x5) == 0x5)
						{
							m_vpos[v] = (lsptr - sptr);
							m_lponce[v] = 1;
						}
						else
						{
							m_ctrl &= ~(1<<v);
							m_vpos[v] = (eptr - sptr);
							m_frac[v] = 0;
						}

					}
				}
			}
		}
	}
	mixp = &mix[0];
	for (i = 0; i < outputs[0].samples(); i++)
	{
		outputs[0].put_int(i, *mixp++, 32768 * 16);
		outputs[1].put_int(i, *mixp++, 32768 * 16);
	}
}


void nile_device::nile_sndctrl_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	uint16_t ctrl=m_ctrl;

	m_stream->update();

	COMBINE_DATA(&m_ctrl);

//  logerror("CTRL: %04x -> %04x %s\n", ctrl, m_ctrl, machine().describe_context());

	ctrl^=m_ctrl;
}


uint16_t nile_device::nile_sndctrl_r()
{
	m_stream->update();
	return m_ctrl;
}


uint16_t nile_device::nile_snd_r(offs_t offset)
{
	int reg=offset&0xf;

	m_stream->update();

	if(reg==2 || reg==3)
	{
		int slot=offset/16;
		int sptr = ((m_sound_regs[slot*16+3]<<16)|m_sound_regs[slot*16+2])+m_vpos[slot];

		if(reg==2)
		{
			return sptr&0xffff;
		}
		else
		{
			return sptr>>16;
		}
	}
	return m_sound_regs[offset];
}


void nile_device::nile_snd_w(offs_t offset, uint16_t data, uint16_t mem_mask)
{
	int v, r;

	m_stream->update();

	COMBINE_DATA(&m_sound_regs[offset]);

	v = offset / 16;
	r = offset % 16;

	if ((r == 2) || (r == 3))
	{
		m_vpos[v] = m_frac[v] = m_lponce[v] = 0;
	}

	//logerror("v%02d: %04x to reg %02d (PC=%x)\n", v, m_sound_regs[offset], r, machine().describe_context());
}