/*********************************************************/ /* ricoh RF5C68(or clone) PCM controller */ /*********************************************************/ #include "mamedef.h" #include #include //#include "sndintrf.h" //#include "streams.h" #include "rf5c68.h" #include #define NULL ((void *)0) #define NUM_CHANNELS (8) #define STEAM_STEP 0x800 typedef struct _pcm_channel pcm_channel; struct _pcm_channel { UINT8 enable; UINT8 env; UINT8 pan; UINT8 start; UINT32 addr; UINT16 step; UINT16 loopst; UINT8 Muted; }; typedef struct _mem_stream mem_stream; struct _mem_stream { UINT32 BaseAddr; UINT32 EndAddr; UINT32 CurAddr; UINT16 CurStep; const UINT8* MemPnt; }; typedef struct _rf5c68_state rf5c68_state; struct _rf5c68_state { //sound_stream * stream; pcm_channel chan[NUM_CHANNELS]; UINT8 cbank; UINT8 wbank; UINT8 enable; UINT32 datasize; UINT8* data; //void (*sample_callback)(running_device* device,int channel); mem_stream memstrm; }; static void rf5c68_mem_stream_flush(rf5c68_state *chip); /*INLINE rf5c68_state *get_safe_token(const device_config *device) { assert(device != NULL); assert(device->token != NULL); assert(device->type == SOUND); assert(sound_get_type(device) == SOUND_RF5C68); return (rf5c68_state *)device->token; }*/ /************************************************/ /* RF5C68 stream update */ /************************************************/ static void memstream_sample_check(rf5c68_state *chip, UINT32 addr, UINT16 Speed) { mem_stream* ms = &chip->memstrm; UINT32 SmplSpd; SmplSpd = (Speed >= 0x0800) ? (Speed >> 11) : 1; if (addr >= ms->CurAddr) { // Is the stream too fast? (e.g. about to catch up the output) if (addr - ms->CurAddr <= SmplSpd * 5) { // Yes - delay the stream ms->CurAddr -= SmplSpd * 4; if (ms->CurAddr < ms->BaseAddr) ms->CurAddr = ms->BaseAddr; } } else { // Is the stream too slow? (e.g. the output is about to catch up the stream) if (ms->CurAddr - addr <= SmplSpd * 5) { if (ms->CurAddr + SmplSpd * 4 >= ms->EndAddr) { rf5c68_mem_stream_flush(chip); } else { memcpy(chip->data + ms->CurAddr, ms->MemPnt + (ms->CurAddr - ms->BaseAddr), SmplSpd * 4); ms->CurAddr += SmplSpd * 4; } } } return; } //static STREAM_UPDATE( rf5c68_update ) void rf5c68_update(void *_info, stream_sample_t **outputs, int samples) { //rf5c68_state *chip = (rf5c68_state *)param; rf5c68_state *chip = (rf5c68_state *)_info; mem_stream* ms = &chip->memstrm; stream_sample_t *left = outputs[0]; stream_sample_t *right = outputs[1]; int i, j; /* start with clean buffers */ memset(left, 0, samples * sizeof(*left)); memset(right, 0, samples * sizeof(*right)); /* bail if not enabled */ if (!chip->enable) return; /* loop over channels */ for (i = 0; i < NUM_CHANNELS; i++) { pcm_channel *chan = &chip->chan[i]; /* if this channel is active, accumulate samples */ if (chan->enable && ! chan->Muted) { int lv = (chan->pan & 0x0f) * chan->env; int rv = ((chan->pan >> 4) & 0x0f) * chan->env; /* loop over the sample buffer */ for (j = 0; j < samples; j++) { int sample; /* trigger sample callback */ /*if(chip->sample_callback) { if(((chan->addr >> 11) & 0xfff) == 0xfff) chip->sample_callback(chip->device,((chan->addr >> 11)/0x2000)); }*/ memstream_sample_check(chip, (chan->addr >> 11) & 0xFFFF, chan->step); /* fetch the sample and handle looping */ sample = chip->data[(chan->addr >> 11) & 0xffff]; if (sample == 0xff) { chan->addr = chan->loopst << 11; sample = chip->data[(chan->addr >> 11) & 0xffff]; /* if we loop to a loop point, we're effectively dead */ if (sample == 0xff) break; } chan->addr += chan->step; /* add to the buffer */ if (sample & 0x80) { sample &= 0x7f; left[j] += (sample * lv) >> 5; right[j] += (sample * rv) >> 5; } else { left[j] -= (sample * lv) >> 5; right[j] -= (sample * rv) >> 5; } } } } if (samples && ms->CurAddr < ms->EndAddr) { ms->CurStep += STEAM_STEP * samples; if (ms->CurStep >= 0x0800) // 1 << 11 { i = ms->CurStep >> 11; ms->CurStep &= 0x07FF; if (ms->CurAddr + i > ms->EndAddr) i = ms->EndAddr - ms->CurAddr; memcpy(chip->data + ms->CurAddr, ms->MemPnt + (ms->CurAddr - ms->BaseAddr), i); ms->CurAddr += i; } } // I think, this is completely useless /* now clamp and shift the result (output is only 10 bits) */ /*for (j = 0; j < samples; j++) { stream_sample_t temp; temp = left[j]; if (temp > 32767) temp = 32767; else if (temp < -32768) temp = -32768; left[j] = temp & ~0x3f; temp = right[j]; if (temp > 32767) temp = 32767; else if (temp < -32768) temp = -32768; right[j] = temp & ~0x3f; }*/ } /************************************************/ /* RF5C68 start */ /************************************************/ //static DEVICE_START( rf5c68 ) int device_start_rf5c68(void **_info, int clock) { //const rf5c68_interface* intf = (const rf5c68_interface*)device->baseconfig().static_config(); /* allocate memory for the chip */ //rf5c68_state *chip = get_safe_token(device); rf5c68_state *chip; int chn; chip = (rf5c68_state *) calloc(1, sizeof(rf5c68_state)); *_info = (void *) chip; chip->datasize = 0x10000; chip->data = (UINT8*)malloc(chip->datasize); /* allocate the stream */ //chip->stream = stream_create(device, 0, 2, device->clock / 384, chip, rf5c68_update); /* set up callback */ /*if(intf != NULL) chip->sample_callback = intf->sample_end_callback; else chip->sample_callback = NULL;*/ for (chn = 0; chn < NUM_CHANNELS; chn ++) chip->chan[chn].Muted = 0x00; return (clock & 0x7FFFFFFF) / 384; } void device_stop_rf5c68(void *_info) { rf5c68_state *chip = (rf5c68_state *)_info; free(chip->data); chip->data = NULL; free(chip); return; } void device_reset_rf5c68(void *_info) { rf5c68_state *chip = (rf5c68_state *)_info; int i; pcm_channel* chan; mem_stream* ms = &chip->memstrm; // Clear the PCM memory. memset(chip->data, 0x00, chip->datasize); chip->enable = 0; chip->cbank = 0; chip->wbank = 0; /* clear channel registers */ for (i = 0; i < NUM_CHANNELS; i ++) { chan = &chip->chan[i]; chan->enable = 0; chan->env = 0; chan->pan = 0; chan->start = 0; chan->addr = 0; chan->step = 0; chan->loopst = 0; } ms->BaseAddr = 0x0000; ms->CurAddr = 0x0000; ms->EndAddr = 0x0000; ms->CurStep = 0x0000; ms->MemPnt = NULL; } /************************************************/ /* RF5C68 write register */ /************************************************/ //WRITE8_DEVICE_HANDLER( rf5c68_w ) void rf5c68_w(void *_info, offs_t offset, UINT8 data) { //rf5c68_state *chip = get_safe_token(device); rf5c68_state *chip = (rf5c68_state *)_info; pcm_channel *chan = &chip->chan[chip->cbank]; int i; /* force the stream to update first */ //stream_update(chip->stream); /* switch off the address */ switch (offset) { case 0x00: /* envelope */ chan->env = data; break; case 0x01: /* pan */ chan->pan = data; break; case 0x02: /* FDL */ chan->step = (chan->step & 0xff00) | (data & 0x00ff); break; case 0x03: /* FDH */ chan->step = (chan->step & 0x00ff) | ((data << 8) & 0xff00); break; case 0x04: /* LSL */ chan->loopst = (chan->loopst & 0xff00) | (data & 0x00ff); break; case 0x05: /* LSH */ chan->loopst = (chan->loopst & 0x00ff) | ((data << 8) & 0xff00); break; case 0x06: /* ST */ chan->start = data; if (!chan->enable) chan->addr = chan->start << (8 + 11); break; case 0x07: /* control reg */ chip->enable = (data >> 7) & 1; if (data & 0x40) chip->cbank = data & 7; else chip->wbank = data & 15; break; case 0x08: /* channel on/off reg */ for (i = 0; i < 8; i++) { chip->chan[i].enable = (~data >> i) & 1; if (!chip->chan[i].enable) chip->chan[i].addr = chip->chan[i].start << (8 + 11); } break; } } /************************************************/ /* RF5C68 read memory */ /************************************************/ //READ8_DEVICE_HANDLER( rf5c68_mem_r ) UINT8 rf5c68_mem_r(void *_info, offs_t offset) { //rf5c68_state *chip = get_safe_token(device); rf5c68_state *chip = (rf5c68_state *)_info; return chip->data[chip->wbank * 0x1000 + offset]; } /************************************************/ /* RF5C68 write memory */ /************************************************/ //WRITE8_DEVICE_HANDLER( rf5c68_mem_w ) void rf5c68_mem_w(void *_info, offs_t offset, UINT8 data) { //rf5c68_state *chip = get_safe_token(device); rf5c68_state *chip = (rf5c68_state *)_info; rf5c68_mem_stream_flush(chip); chip->data[chip->wbank * 0x1000 | offset] = data; } static void rf5c68_mem_stream_flush(rf5c68_state *chip) { mem_stream* ms = &chip->memstrm; if (ms->CurAddr >= ms->EndAddr) return; memcpy(chip->data + ms->CurAddr, ms->MemPnt + (ms->CurAddr - ms->BaseAddr), ms->EndAddr - ms->CurAddr); ms->CurAddr = ms->EndAddr; return; } void rf5c68_write_ram(void *_info, offs_t DataStart, offs_t DataLength, const UINT8* RAMData) { rf5c68_state *chip = (rf5c68_state *)_info; mem_stream* ms = &chip->memstrm; UINT16 BytCnt; DataStart |= chip->wbank * 0x1000; if (DataStart >= chip->datasize) return; if (DataStart + DataLength > chip->datasize) DataLength = chip->datasize - DataStart; //memcpy(chip->data + DataStart, RAMData, DataLength); rf5c68_mem_stream_flush(chip); ms->BaseAddr = DataStart; ms->CurAddr = ms->BaseAddr; ms->EndAddr = ms->BaseAddr + DataLength; ms->CurStep = 0x0000; ms->MemPnt = RAMData; //BytCnt = (STEAM_STEP * 32) >> 11; BytCnt = 0x40; // SegaSonic Arcade: Run! Run! Run! needs such a high value if (ms->CurAddr + BytCnt > ms->EndAddr) BytCnt = ms->EndAddr - ms->CurAddr; memcpy(chip->data + ms->CurAddr, ms->MemPnt + (ms->CurAddr - ms->BaseAddr), BytCnt); ms->CurAddr += BytCnt; return; } void rf5c68_set_mute_mask(void *_info, UINT32 MuteMask) { rf5c68_state *chip = (rf5c68_state *)_info; unsigned char CurChn; for (CurChn = 0; CurChn < NUM_CHANNELS; CurChn ++) chip->chan[CurChn].Muted = (MuteMask >> CurChn) & 0x01; return; } /************************************************************************** * Generic get_info **************************************************************************/ /*DEVICE_GET_INFO( rf5c68 ) { switch (state) { // --- the following bits of info are returned as 64-bit signed integers --- case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(rf5c68_state); break; // --- the following bits of info are returned as pointers to data or functions --- case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( rf5c68 ); 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, "RF5C68"); break; case DEVINFO_STR_FAMILY: strcpy(info->s, "Ricoh PCM"); 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; } }*/ /**************** end of file ****************/