summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/sound/zsg2.c
blob: 5118a73212688545eeb4238ef40daabe37ff37a0 (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
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
/*
    ZOOM ZSG-2 custom wavetable synthesizer

    Written by Olivier Galibert
    MAME conversion by R. Belmont

    Copyright Nicola Salmoria and the MAME Team.
    Visit http://mamedev.org for licensing and usage restrictions.

    ---------------------------------------------------------

    Additional notes on the sample format, reverse-engineered
    by Olivier Galibert and David Haywood:

    The zoom sample rom is decomposed in 0x40000 bytes pages.  Each page
    starts by a header and is followed by compressed samples.

    The header is a vector of 16 bytes structures composed of 4 32bits
    little-endian values representing:
    - sample start position in bytes, always a multiple of 4
    - sample end position in bytes, minus 4, always...
    - loop position in bytes, always....
    - flags, probably

    It is interesting to note that this header is *not* parsed by the
    ZSG.  The main program reads the rom through appropriate ZSG
    commands, and use the results in subsequent register setups.  It's
    not even obvious that the ZSG cares about the pages, it may just
    see the address space as linear.  In the same line, the
    interpretation of the flags is obviously dependant on the main
    program, not the ZSG, but some of the bits are directly copied to
    some of the registers.

    The samples are compressed with a 2:1 ratio.  Each bloc of 4-bytes
    becomes 4 16-bits samples.  Reading the 4 bytes as a *little-endian*
    32bits values, the structure is:
    
    1444 4444 1333 3333 1222 2222 ssss1111

    's' is a 4-bit scale value.  '1', '2', '3', '4' are signed 7-bits
    values corresponding to the 4 samples.  To compute the final 16bits
    value just shift left by (9-s).  Yes, that simple.

*/

#include "emu.h"
#include "streams.h"
#include "zsg2.h"

// 16 registers per channel, 48 channels
typedef struct _zchan zchan;
struct _zchan
{
	UINT16 v[16];
};

typedef struct _zsg2_state zsg2_state;
struct _zsg2_state
{
	zchan zc[48];
	UINT16 act[3];
	UINT16 alow, ahigh;
	UINT8 *bank_samples;

	int sample_rate;
	sound_stream *stream;
};

INLINE zsg2_state *get_safe_token(running_device *device)
{
	assert(device != NULL);
	assert(device->token != NULL);
	assert(device->type == SOUND);
	assert(sound_get_type(device) == SOUND_ZSG2);
	return (zsg2_state *)device->token;
}

static STREAM_UPDATE( update_stereo )
{
//	zsg2_state *info = (zsg2_state *)param;
	stream_sample_t *dest1 = outputs[0];
	stream_sample_t *dest2 = outputs[1];

	memset(dest1, 0, sizeof(stream_sample_t) * samples);
	memset(dest2, 0, sizeof(stream_sample_t) * samples);
}

static void chan_w(zsg2_state *info, int chan, int reg, UINT16 data)
{
  info->zc[chan].v[reg] = data;
  //  log_event("ZOOMCHAN", "chan %02x reg %x = %04x", chan, reg, data);
}

static UINT16 chan_r(zsg2_state *info, int chan, int reg)
{
  //  log_event("ZOOMCHAN", "chan %02x read reg %x: %04x", chan, reg, zc[chan].v[reg]);
  return info->zc[chan].v[reg];
}

static void check_channel(zsg2_state *info, int chan)
{
  //  log_event("ZOOM", "chan %02x e=%04x f=%04x", chan, zc[chan].v[14], zc[chan].v[15]);
}

static void keyon(zsg2_state *info, int chan)
{
#if 0
  log_event("ZOOM", "keyon %02x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x",
	    chan,
	    info->zc[chan].v[0x0], info->zc[chan].v[0x1], info->zc[chan].v[0x2], info->zc[chan].v[0x3],
	    info->zc[chan].v[0x4], info->zc[chan].v[0x5], info->zc[chan].v[0x6], info->zc[chan].v[0x7],
	    info->zc[chan].v[0x8], info->zc[chan].v[0x9], info->zc[chan].v[0xa], info->zc[chan].v[0xb],
	    info->zc[chan].v[0xc], info->zc[chan].v[0xd], info->zc[chan].v[0xe], info->zc[chan].v[0xf]);
#endif
}

static void control_w(zsg2_state *info, int reg, UINT16 data)
{
	switch(reg) 
	{
		case 0x00: case 0x02: case 0x04: 
		{
			int base = (reg & 6) << 3;
			int i;
			for(i=0; i<16; i++)
				if(data & (1<<i))
					keyon(info, base+i);
			break;
		}

		case 0x08: case 0x0a: case 0x0c: 
		{
			int base = (reg & 6) << 3;
			int i;
			for(i=0; i<16; i++)
				if(data & (1<<i))
					check_channel(info, base+i);
			break;
		}

		case 0x30:
			break;

		case 0x38:
			info->alow = data;
			break;

		case 0x3a:
			info->ahigh = data;
			break;

		default:
		//      log_event("ZOOMCTRL", "%02x = %04x", reg, data);
			break;
	}    
}

static UINT16 control_r(zsg2_state *info, int reg)
{
	switch(reg) 
	{
		case 0x28:
			return 0xff00;

		case 0x3c: case 0x3e: 
		{
			UINT32 adr = (info->ahigh << 16) | info->alow;
			UINT32 val = *(unsigned int *)(info->bank_samples+adr);
//			log_event("ZOOMCTRL", "rom read.%c %06x = %08x", reg == 0x3e ? 'h' : 'l', adr, val);
			return (reg == 0x3e) ? (val >> 16) : val;
		}
	}

//	log_event("ZOOMCTRL", "read %02x", reg);

	return 0xffff;
}

WRITE16_DEVICE_HANDLER( zsg2_w )
{
	zsg2_state *info = get_safe_token(device);
	int adr = offset * 2;

	assert(mem_mask == 0xffff);	// we only support full 16-bit accesses

	stream_update(info->stream);

	if (adr < 0x600)
	{
		int chan = adr >> 5;
		int reg = (adr >> 1) & 15;

		chan_w(info, chan, reg, data);
	} 
	else
	{
		control_w(info, adr - 0x600, data);
	}
}

READ16_DEVICE_HANDLER( zsg2_r )
{
	zsg2_state *info = get_safe_token(device);
	int adr = offset * 2;

	assert(mem_mask == 0xffff);	// we only support full 16-bit accesses

	if (adr < 0x600) 
	{
		int chan = adr >> 5;
		int reg = (adr >> 1) & 15;
		return chan_r(info, chan, reg);
	} 
	else
	{
		return control_r(info, adr - 0x600);
	}

	return 0;
}

static DEVICE_START( zsg2 )
{
	const zsg2_interface *intf = (const zsg2_interface *)device->baseconfig().static_config;
	zsg2_state *info = get_safe_token(device);

	info->sample_rate = device->clock;

	memset(&info->zc, 0, sizeof(info->zc));
	memset(&info->act, 0, sizeof(info->act));

	info->stream = stream_create(device, 0, 2, info->sample_rate, info, update_stereo);

	info->bank_samples = memory_region(device->machine, intf->samplergn);
}

/**************************************************************************
 * Generic get_info
 **************************************************************************/
							   
DEVICE_GET_INFO( zsg2 )
{
	switch (state)
	{
		/* --- the following bits of info are returned as 64-bit signed integers --- */
		case DEVINFO_INT_TOKEN_BYTES:					info->i = sizeof(zsg2_state);			break;

		/* --- the following bits of info are returned as pointers to data or functions --- */
		case DEVINFO_FCT_START:							info->start = DEVICE_START_NAME( zsg2 );		break;
		case DEVINFO_FCT_STOP:							/* nothing */								break;
		case DEVINFO_FCT_RESET:							/* nothing */								break;

		/* --- the following bits of info are returned as NULL-terminated strings --- */
		case DEVINFO_STR_NAME:						strcpy(info->s, "ZSG-2");					break;
		case DEVINFO_STR_FAMILY:					strcpy(info->s, "Zoom custom");				break;
		case DEVINFO_STR_VERSION:					strcpy(info->s, "1.0");						break;
		case DEVINFO_STR_SOURCE_FILE:					strcpy(info->s, __FILE__);					break;
		case DEVINFO_STR_CREDITS:					strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break;
	}
}