/********************************************************* Konami 053260 PCM Sound Chip *********************************************************/ #include "mamedef.h" //#include "emu.h" #ifdef _DEBUG #include #endif #include #include #include "k053260.h" #define NULL ((void *)0) /* 2004-02-28: Fixed PPCM decoding. Games sound much better now.*/ #define LOG 0 #define BASE_SHIFT 16 typedef struct _k053260_channel k053260_channel; struct _k053260_channel { UINT32 rate; UINT32 size; UINT32 start; UINT32 bank; UINT32 volume; int play; UINT32 pan; UINT32 pos; int loop; int ppcm; /* packed PCM ( 4 bit signed ) */ int ppcm_data; UINT8 Muted; }; typedef struct _k053260_state k053260_state; struct _k053260_state { //sound_stream * channel; int mode; int regs[0x30]; UINT8 *rom; //int rom_size; UINT32 rom_size; UINT32 *delta_table; k053260_channel channels[4]; //const k053260_interface *intf; //device_t *device; }; /*INLINE k053260_state *get_safe_token(device_t *device) { assert(device != NULL); assert(device->type() == K053260); return (k053260_state *)downcast(device)->token(); }*/ static void InitDeltaTable( k053260_state *ic, int rate, int clock ) { int i; double base = ( double )rate; double max = (double)(clock); /* Hz */ UINT32 val; for( i = 0; i < 0x1000; i++ ) { double v = ( double )( 0x1000 - i ); double target = (max) / v; double fixed = ( double )( 1 << BASE_SHIFT ); if ( target && base ) { target = fixed / ( base / target ); val = ( UINT32 )target; if ( val == 0 ) val = 1; } else val = 1; ic->delta_table[i] = val; } } //static DEVICE_RESET( k053260 ) void device_reset_k053260(void *_info) { //k053260_state *ic = get_safe_token(device); k053260_state *ic = (k053260_state *)_info; int i; for( i = 0; i < 4; i++ ) { ic->channels[i].rate = 0; ic->channels[i].size = 0; ic->channels[i].start = 0; ic->channels[i].bank = 0; ic->channels[i].volume = 0; ic->channels[i].play = 0; ic->channels[i].pan = 0; ic->channels[i].pos = 0; ic->channels[i].loop = 0; ic->channels[i].ppcm = 0; ic->channels[i].ppcm_data = 0; } } INLINE int limit( int val, int max, int min ) { if ( val > max ) val = max; else if ( val < min ) val = min; return val; } //#define MAXOUT 0x7fff #define MAXOUT +0x8000 #define MINOUT -0x8000 //static STREAM_UPDATE( k053260_update ) void k053260_update(void *param, stream_sample_t **outputs, int samples) { static const INT8 dpcmcnv[] = { 0,1,2,4,8,16,32,64, -128, -64, -32, -16, -8, -4, -2, -1}; int i, j, lvol[4], rvol[4], play[4], loop[4], ppcm[4]; UINT8 *rom[4]; UINT32 delta[4], end[4], pos[4]; INT8 ppcm_data[4]; int dataL, dataR; INT8 d; k053260_state *ic = (k053260_state *)param; /* precache some values */ for ( i = 0; i < 4; i++ ) { if (ic->channels[i].Muted) { play[i] = 0; continue; } rom[i]= &ic->rom[ic->channels[i].start + ( ic->channels[i].bank << 16 )]; delta[i] = ic->delta_table[ic->channels[i].rate]; lvol[i] = ic->channels[i].volume * ic->channels[i].pan; rvol[i] = ic->channels[i].volume * ( 8 - ic->channels[i].pan ); end[i] = ic->channels[i].size; pos[i] = ic->channels[i].pos; play[i] = ic->channels[i].play; loop[i] = ic->channels[i].loop; ppcm[i] = ic->channels[i].ppcm; ppcm_data[i] = ic->channels[i].ppcm_data; if ( ppcm[i] ) delta[i] /= 2; } for ( j = 0; j < samples; j++ ) { dataL = dataR = 0; for ( i = 0; i < 4; i++ ) { /* see if the voice is on */ if ( play[i] ) { /* see if we're done */ if ( ( pos[i] >> BASE_SHIFT ) >= end[i] ) { ppcm_data[i] = 0; if ( loop[i] ) pos[i] = 0; else { play[i] = 0; continue; } } if ( ppcm[i] ) { /* Packed PCM */ /* we only update the signal if we're starting or a real sound sample has gone by */ /* this is all due to the dynamic sample rate convertion */ if ( pos[i] == 0 || ( ( pos[i] ^ ( pos[i] - delta[i] ) ) & 0x8000 ) == 0x8000 ) { int newdata; if ( pos[i] & 0x8000 ){ newdata = ((rom[i][pos[i] >> BASE_SHIFT]) >> 4) & 0x0f; /*high nybble*/ } else{ newdata = ( ( rom[i][pos[i] >> BASE_SHIFT] ) ) & 0x0f; /*low nybble*/ } /*ppcm_data[i] = (( ( ppcm_data[i] * 62 ) >> 6 ) + dpcmcnv[newdata]); if ( ppcm_data[i] > 127 ) ppcm_data[i] = 127; else if ( ppcm_data[i] < -128 ) ppcm_data[i] = -128;*/ ppcm_data[i] += dpcmcnv[newdata]; } d = ppcm_data[i]; pos[i] += delta[i]; } else { /* PCM */ d = rom[i][pos[i] >> BASE_SHIFT]; pos[i] += delta[i]; } if ( ic->mode & 2 ) { dataL += ( d * lvol[i] ) >> 2; dataR += ( d * rvol[i] ) >> 2; } } } outputs[1][j] = limit( dataL, MAXOUT, MINOUT ); outputs[0][j] = limit( dataR, MAXOUT, MINOUT ); } /* update the regs now */ for ( i = 0; i < 4; i++ ) { if (ic->channels[i].Muted) continue; ic->channels[i].pos = pos[i]; ic->channels[i].play = play[i]; ic->channels[i].ppcm_data = ppcm_data[i]; } } //static DEVICE_START( k053260 ) int device_start_k053260(void **_info, int clock) { //static const k053260_interface defintrf = { 0 }; //k053260_state *ic = get_safe_token(device); k053260_state *ic; //int rate = device->clock() / 32; int rate = clock / 32; int i; ic = (k053260_state *) calloc(1, sizeof(k053260_state)); *_info = (void *) ic; /* Initialize our chip structure */ //ic->device = device; //ic->intf = (device->static_config() != NULL) ? (const k053260_interface *)device->static_config() : &defintrf; ic->mode = 0; //const memory_region *region = (ic->intf->rgnoverride != NULL) ? device->machine().region(ic->intf->rgnoverride) : device->region(); //ic->rom = *region; //ic->rom_size = region->bytes(); ic->rom = NULL; ic->rom_size = 0x00; // has to be done by the player after calling device_start //DEVICE_RESET_CALL(k053260); for ( i = 0; i < 0x30; i++ ) ic->regs[i] = 0; //ic->delta_table = auto_alloc_array( device->machine(), UINT32, 0x1000 ); ic->delta_table = (UINT32*)malloc(0x1000 * sizeof(UINT32)); //ic->channel = device->machine().sound().stream_alloc( *device, 0, 2, rate, ic, k053260_update ); //InitDeltaTable( ic, rate, device->clock() ); InitDeltaTable( ic, rate, clock ); /* register with the save state system */ /*device->save_item(NAME(ic->mode)); device->save_item(NAME(ic->regs)); for ( i = 0; i < 4; i++ ) { device->save_item(NAME(ic->channels[i].rate), i); device->save_item(NAME(ic->channels[i].size), i); device->save_item(NAME(ic->channels[i].start), i); device->save_item(NAME(ic->channels[i].bank), i); device->save_item(NAME(ic->channels[i].volume), i); device->save_item(NAME(ic->channels[i].play), i); device->save_item(NAME(ic->channels[i].pan), i); device->save_item(NAME(ic->channels[i].pos), i); device->save_item(NAME(ic->channels[i].loop), i); device->save_item(NAME(ic->channels[i].ppcm), i); device->save_item(NAME(ic->channels[i].ppcm_data), i); }*/ /* setup SH1 timer if necessary */ //if ( ic->intf->irq ) // device->machine().scheduler().timer_pulse( attotime::from_hz(device->clock()) * 32, ic->intf->irq, "ic->intf->irq" ); for (i = 0; i < 4; i ++) ic->channels[i].Muted = 0x00; return rate; } void device_stop_k053260(void *_info) { k053260_state *ic = (k053260_state *)_info; free(ic->delta_table); free(ic->rom); ic->rom = NULL; free(ic); return; } INLINE void check_bounds( k053260_state *ic, int channel ) { int channel_start = ( ic->channels[channel].bank << 16 ) + ic->channels[channel].start; int channel_end = channel_start + ic->channels[channel].size - 1; if ( channel_start > ic->rom_size ) { logerror("K53260: Attempting to start playing past the end of the ROM ( start = %06x, end = %06x ).\n", channel_start, channel_end ); ic->channels[channel].play = 0; return; } if ( channel_end > ic->rom_size ) { logerror("K53260: Attempting to play past the end of the ROM ( start = %06x, end = %06x ).\n", channel_start, channel_end ); ic->channels[channel].size = ic->rom_size - channel_start; } if (LOG) logerror("K053260: Sample Start = %06x, Sample End = %06x, Sample rate = %04x, PPCM = %s\n", channel_start, channel_end, ic->channels[channel].rate, ic->channels[channel].ppcm ? "yes" : "no" ); } //WRITE8_DEVICE_HANDLER( k053260_w ) void k053260_w(void *_info, offs_t offset, UINT8 data) { int i, t; int r = offset; int v = data; //k053260_state *ic = get_safe_token(device); k053260_state *ic = (k053260_state *)_info; if ( r > 0x2f ) { logerror("K053260: Writing past registers\n" ); return; } //ic->channel->update(); /* before we update the regs, we need to check for a latched reg */ if ( r == 0x28 ) { t = ic->regs[r] ^ v; for ( i = 0; i < 4; i++ ) { if ( t & ( 1 << i ) ) { if ( v & ( 1 << i ) ) { ic->channels[i].play = 1; ic->channels[i].pos = 0; ic->channels[i].ppcm_data = 0; check_bounds( ic, i ); } else ic->channels[i].play = 0; } } ic->regs[r] = v; return; } /* update regs */ ic->regs[r] = v; /* communication registers */ if ( r < 8 ) return; /* channel setup */ if ( r < 0x28 ) { int channel = ( r - 8 ) / 8; switch ( ( r - 8 ) & 0x07 ) { case 0: /* sample rate low */ ic->channels[channel].rate &= 0x0f00; ic->channels[channel].rate |= v; break; case 1: /* sample rate high */ ic->channels[channel].rate &= 0x00ff; ic->channels[channel].rate |= ( v & 0x0f ) << 8; break; case 2: /* size low */ ic->channels[channel].size &= 0xff00; ic->channels[channel].size |= v; break; case 3: /* size high */ ic->channels[channel].size &= 0x00ff; ic->channels[channel].size |= v << 8; break; case 4: /* start low */ ic->channels[channel].start &= 0xff00; ic->channels[channel].start |= v; break; case 5: /* start high */ ic->channels[channel].start &= 0x00ff; ic->channels[channel].start |= v << 8; break; case 6: /* bank */ ic->channels[channel].bank = v & 0xff; break; case 7: /* volume is 7 bits. Convert to 8 bits now. */ ic->channels[channel].volume = ( ( v & 0x7f ) << 1 ) | ( v & 1 ); break; } return; } switch( r ) { case 0x2a: /* loop, ppcm */ for ( i = 0; i < 4; i++ ) ic->channels[i].loop = ( v & ( 1 << i ) ) != 0; for ( i = 4; i < 8; i++ ) ic->channels[i-4].ppcm = ( v & ( 1 << i ) ) != 0; break; case 0x2c: /* pan */ ic->channels[0].pan = v & 7; ic->channels[1].pan = ( v >> 3 ) & 7; break; case 0x2d: /* more pan */ ic->channels[2].pan = v & 7; ic->channels[3].pan = ( v >> 3 ) & 7; break; case 0x2f: /* control */ ic->mode = v & 7; /* bit 0 = read ROM */ /* bit 1 = enable sound output */ /* bit 2 = unknown */ break; } } //READ8_DEVICE_HANDLER( k053260_r ) UINT8 k053260_r(void *_info, offs_t offset) { //k053260_state *ic = get_safe_token(device); k053260_state *ic = (k053260_state *)_info; switch ( offset ) { case 0x29: /* channel status */ { int i, status = 0; for ( i = 0; i < 4; i++ ) status |= ic->channels[i].play << i; return status; } break; case 0x2e: /* read ROM */ if ( ic->mode & 1 ) { UINT32 offs = ic->channels[0].start + ( ic->channels[0].pos >> BASE_SHIFT ) + ( ic->channels[0].bank << 16 ); ic->channels[0].pos += ( 1 << 16 ); if ( offs > ic->rom_size ) { //logerror("%s: K53260: Attempting to read past ROM size in ROM Read Mode (offs = %06x, size = %06x).\n", device->machine().describe_context(),offs,ic->rom_size ); logerror("K53260: Attempting to read past ROM size in ROM Read Mode (offs = %06x, size = %06x).\n", offs,ic->rom_size ); return 0; } return ic->rom[offs]; } break; } return ic->regs[offset]; } void k053260_write_rom(void *_info, offs_t ROMSize, offs_t DataStart, offs_t DataLength, const UINT8* ROMData) { k053260_state *info = (k053260_state *)_info; if (info->rom_size != ROMSize) { info->rom = (UINT8*)realloc(info->rom, ROMSize); info->rom_size = ROMSize; memset(info->rom, 0xFF, ROMSize); } if (DataStart > ROMSize) return; if (DataStart + DataLength > ROMSize) DataLength = ROMSize - DataStart; memcpy(info->rom + DataStart, ROMData, DataLength); return; } void k053260_set_mute_mask(void *_info, UINT32 MuteMask) { k053260_state *info = (k053260_state *)_info; UINT8 CurChn; for (CurChn = 0; CurChn < 4; CurChn ++) info->channels[CurChn].Muted = (MuteMask >> CurChn) & 0x01; return; } /************************************************************************** * Generic get_info **************************************************************************/ /*DEVICE_GET_INFO( k053260 ) { switch (state) { // --- the following bits of info are returned as 64-bit signed integers --- // case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(k053260_state); break; // --- the following bits of info are returned as pointers to data or functions --- // case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( k053260 ); break; case DEVINFO_FCT_STOP: // nothing // break; case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( k053260 ); break; // --- the following bits of info are returned as NULL-terminated strings --- // case DEVINFO_STR_NAME: strcpy(info->s, "K053260"); break; case DEVINFO_STR_FAMILY: strcpy(info->s, "Konami 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(K053260, k053260);*/