// license:BSD-3-Clause // copyright-holders:Nicola Salmoria,Hiromitsu Shioya /*********************************************************/ /* Konami PCM controller */ /*********************************************************/ /* Changelog, Hiromitsu Shioya 02/05/2002 fix start address decode timing. (sample loop bug.) Changelog, Mish, August 1999: Removed interface support for different memory regions per channel. Removed interface support for differing channel volume. Added bankswitching. Added support for multiple chips. (Nb: Should different memory regions per channel be needed the bankswitching function can set this up). NS990821 support for the k007232_VOL() macro. added external port callback, and functions to set the volume of the channels */ #include "emu.h" #include "k007232.h" #define BASE_SHIFT (12) #if 0 static const int kdac_note[] = { 261.63/8, 277.18/8, 293.67/8, 311.13/8, 329.63/8, 349.23/8, 369.99/8, 392.00/8, 415.31/8, 440.00/8, 466.16/8, 493.88/8, 523.25/8, }; static const float kdaca_fn[][2] = { /* B */ { 0x03f, 493.88/8 }, /* ?? */ { 0x11f, 493.88/4 }, /* ?? */ { 0x18f, 493.88/2 }, /* ?? */ { 0x1c7, 493.88 }, { 0x1e3, 493.88*2 }, { 0x1f1, 493.88*4 }, /* ?? */ { 0x1f8, 493.88*8 }, /* ?? */ /* A+ */ { 0x020, 466.16/8 }, /* ?? */ { 0x110, 466.16/4 }, /* ?? */ { 0x188, 466.16/2 }, { 0x1c4, 466.16 }, { 0x1e2, 466.16*2 }, { 0x1f1, 466.16*4 }, /* ?? */ { 0x1f8, 466.16*8 }, /* ?? */ /* A */ { 0x000, 440.00/8 }, /* ?? */ { 0x100, 440.00/4 }, /* ?? */ { 0x180, 440.00/2 }, { 0x1c0, 440.00 }, { 0x1e0, 440.00*2 }, { 0x1f0, 440.00*4 }, /* ?? */ { 0x1f8, 440.00*8 }, /* ?? */ { 0x1fc, 440.00*16}, /* ?? */ { 0x1fe, 440.00*32}, /* ?? */ { 0x1ff, 440.00*64}, /* ?? */ /* G+ */ { 0x0f2, 415.31/4 }, { 0x179, 415.31/2 }, { 0x1bc, 415.31 }, { 0x1de, 415.31*2 }, { 0x1ef, 415.31*4 }, /* ?? */ { 0x1f7, 415.31*8 }, /* ?? */ /* G */ { 0x0e2, 392.00/4 }, { 0x171, 392.00/2 }, { 0x1b8, 392.00 }, { 0x1dc, 392.00*2 }, { 0x1ee, 392.00*4 }, /* ?? */ { 0x1f7, 392.00*8 }, /* ?? */ /* F+ */ { 0x0d0, 369.99/4 }, /* ?? */ { 0x168, 369.99/2 }, { 0x1b4, 369.99 }, { 0x1da, 369.99*2 }, { 0x1ed, 369.99*4 }, /* ?? */ { 0x1f6, 369.99*8 }, /* ?? */ /* F */ { 0x0bf, 349.23/4 }, /* ?? */ { 0x15f, 349.23/2 }, { 0x1af, 349.23 }, { 0x1d7, 349.23*2 }, { 0x1eb, 349.23*4 }, /* ?? */ { 0x1f5, 349.23*8 }, /* ?? */ /* E */ { 0x0ac, 329.63/4 }, { 0x155, 329.63/2 }, /* ?? */ { 0x1ab, 329.63 }, { 0x1d5, 329.63*2 }, { 0x1ea, 329.63*4 }, /* ?? */ { 0x1f4, 329.63*8 }, /* ?? */ /* D+ */ { 0x098, 311.13/4 }, /* ?? */ { 0x14c, 311.13/2 }, { 0x1a6, 311.13 }, { 0x1d3, 311.13*2 }, { 0x1e9, 311.13*4 }, /* ?? */ { 0x1f4, 311.13*8 }, /* ?? */ /* D */ { 0x080, 293.67/4 }, /* ?? */ { 0x140, 293.67/2 }, /* ?? */ { 0x1a0, 293.67 }, { 0x1d0, 293.67*2 }, { 0x1e8, 293.67*4 }, /* ?? */ { 0x1f4, 293.67*8 }, /* ?? */ { 0x1fa, 293.67*16}, /* ?? */ { 0x1fd, 293.67*32}, /* ?? */ /* C+ */ { 0x06d, 277.18/4 }, /* ?? */ { 0x135, 277.18/2 }, /* ?? */ { 0x19b, 277.18 }, { 0x1cd, 277.18*2 }, { 0x1e6, 277.18*4 }, /* ?? */ { 0x1f2, 277.18*8 }, /* ?? */ /* C */ { 0x054, 261.63/4 }, { 0x12a, 261.63/2 }, { 0x195, 261.63 }, { 0x1ca, 261.63*2 }, { 0x1e5, 261.63*4 }, { 0x1f2, 261.63*8 }, /* ?? */ { -1, -1 }, }; #endif /*************************************************************/ DEFINE_DEVICE_TYPE(K007232, k007232_device, "k007232", "K007232 PCM Controller") k007232_device::k007232_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, K007232, tag, owner, clock) , device_sound_interface(mconfig, *this) , m_rom(*this, DEVICE_SELF) , m_port_write_handler(*this) { } //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void k007232_device::device_start() { /* Set up the chips */ m_pcmlimit = m_rom.bytes(); m_port_write_handler.resolve(); for (int i = 0; i < KDAC_A_PCM_MAX; i++) { m_addr[i] = 0; m_start[i] = 0; m_step[i] = 0; m_play[i] = 0; m_bank[i] = 0; } m_vol[0][0] = 255; /* channel A output to output A */ m_vol[0][1] = 0; m_vol[1][0] = 0; m_vol[1][1] = 255; /* channel B output to output B */ for (auto & elem : m_wreg) elem = 0; m_stream = machine().sound().stream_alloc(*this, 0 , 2, clock()/128); KDAC_A_make_fncode(); save_item(NAME(m_vol)); save_item(NAME(m_addr)); save_item(NAME(m_start)); save_item(NAME(m_step)); save_item(NAME(m_bank)); save_item(NAME(m_play)); save_item(NAME(m_wreg)); } void k007232_device::KDAC_A_make_fncode() { #if 0 int i, j, k; float fn; for( i = 0; i < 0x200; i++ ) fncode[i] = 0; i = 0; while( (int)kdaca_fn[i][0] != -1 ){ fncode[(int)kdaca_fn[i][0]] = kdaca_fn[i][1]; i++; } i = j = 0; while( i < 0x200 ){ if( fncode[i] != 0 ){ if( i != j ){ fn = (fncode[i] - fncode[j]) / (i - j); for( k = 1; k < (i-j); k++ ) fncode[k+j] = fncode[j] + fn*k; j = i; } } i++; } #if 0 for( i = 0; i < 0x200; i++ ) logerror("fncode[%04x] = %.2f\n", i, fncode[i] ); #endif #else int i; for( i = 0; i < 0x200; i++ ) { //fncode[i] = (0x200 * 55) / (0x200 - i); m_fncode[i] = (32 << BASE_SHIFT) / (0x200 - i); // m_fncode[i] = ((0x200 * 55.2 / 880) / (0x200 - i)); // = 512 * 55.2 / 220 / (512 - i) = 128 / (512 - i) // logerror("2 : fncode[%04x] = %.2f\n", i, fncode[i] ); } #endif } /************************************************/ /* Konami PCM write register */ /************************************************/ WRITE8_MEMBER( k007232_device::write ) { int r = offset; int v = data; m_stream->update(); m_wreg[r] = v; /* stock write data */ if (r == 0x0c){ /* external port, usually volume control */ if (!m_port_write_handler.isnull()) m_port_write_handler(0, v, mem_mask); return; } else if( r == 0x0d ){ /* loopflag. */ return; } else{ int reg_port; reg_port = 0; if (r >= 0x06){ reg_port = 1; r -= 0x06; } switch (r){ case 0x00: case 0x01: { /**** address step ****/ int idx = (((((unsigned int)m_wreg[reg_port*0x06 + 0x01])<<8)&0x0100) | (((unsigned int)m_wreg[reg_port*0x06 + 0x00])&0x00ff)); #if 0 if( !reg_port && r == 1 ) logerror("%04x\n" ,idx ); #endif m_step[reg_port] = m_fncode[idx]; break; } case 0x02: case 0x03: case 0x04: break; case 0x05: /**** start address ****/ m_start[reg_port] = ((((unsigned int)m_wreg[reg_port*0x06 + 0x04]<<16)&0x00010000) | (((unsigned int)m_wreg[reg_port*0x06 + 0x03]<< 8)&0x0000ff00) | (((unsigned int)m_wreg[reg_port*0x06 + 0x02] )&0x000000ff) | m_bank[reg_port]); if (m_start[reg_port] < m_pcmlimit ){ m_play[reg_port] = 1; m_addr[reg_port] = 0; } break; } } } /************************************************/ /* Konami PCM read register */ /************************************************/ READ8_MEMBER( k007232_device::read ) { int r = offset; int ch; if( r == 0x0005 || r == 0x000b ){ ch = r/0x0006; r = ch * 0x0006; m_start[ch] = ((((unsigned int)m_wreg[r + 0x04]<<16)&0x00010000) | (((unsigned int)m_wreg[r + 0x03]<< 8)&0x0000ff00) | (((unsigned int)m_wreg[r + 0x02] )&0x000000ff) | m_bank[ch]); if (m_start[ch] < m_pcmlimit ){ m_play[ch] = 1; m_addr[ch] = 0; } } return 0; } /*****************************************************************************/ void k007232_device::set_volume(int channel,int volumeA,int volumeB) { m_vol[channel][0] = volumeA; m_vol[channel][1] = volumeB; } void k007232_device::set_bank(int chABank, int chBBank ) { m_bank[0] = chABank<<17; m_bank[1] = chBBank<<17; } /*****************************************************************************/ //------------------------------------------------- // sound_stream_update - handle a stream update //------------------------------------------------- void k007232_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) { int i; memset(outputs[0],0,samples * sizeof(*outputs[0])); memset(outputs[1],0,samples * sizeof(*outputs[1])); for( i = 0; i < KDAC_A_PCM_MAX; i++ ) { if (m_play[i]) { int volA,volB,j,out; unsigned int addr, old_addr; //int cen; /**** PCM setup ****/ addr = m_start[i] + ((m_addr[i]>>BASE_SHIFT)&0x000fffff); volA = m_vol[i][0] * 2; volB = m_vol[i][1] * 2; #if 0 cen = (volA + volB) / 2; volA = (volA + cen) < 0x1fe ? (volA + cen) : 0x1fe; volB = (volB + cen) < 0x1fe ? (volB + cen) : 0x1fe; #endif for( j = 0; j < samples; j++ ) { old_addr = addr; addr = m_start[i] + ((m_addr[i]>>BASE_SHIFT)&0x000fffff); while (old_addr <= addr) { if( (m_rom[old_addr] & 0x80) || old_addr >= m_pcmlimit ) { /* end of sample */ if( m_wreg[0x0d]&(1<