diff options
Diffstat (limited to 'trunk/src/emu/sound/zsg2.c')
-rw-r--r-- | trunk/src/emu/sound/zsg2.c | 260 |
1 files changed, 260 insertions, 0 deletions
diff --git a/trunk/src/emu/sound/zsg2.c b/trunk/src/emu/sound/zsg2.c new file mode 100644 index 00000000000..856fe49ecfe --- /dev/null +++ b/trunk/src/emu/sound/zsg2.c @@ -0,0 +1,260 @@ +/* + 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 "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(device_t *device) +{ + assert(device != NULL); + assert(device->type() == ZSG2); + return (zsg2_state *)downcast<legacy_device_base *>(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 + + info->stream->update(); + + 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->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 = device->machine().sound().stream_alloc(*device, 0, 2, info->sample_rate, info, update_stereo); + + info->bank_samples = device->machine().region(intf->samplergn)->base(); +} + +/************************************************************************** + * 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; + } +} + + +DEFINE_LEGACY_SOUND_DEVICE(ZSG2, zsg2); |