summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/sound/k007232.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/sound/k007232.cpp')
-rw-r--r--src/devices/sound/k007232.cpp519
1 files changed, 193 insertions, 326 deletions
diff --git a/src/devices/sound/k007232.cpp b/src/devices/sound/k007232.cpp
index 96f89ff628f..03f6f16eea0 100644
--- a/src/devices/sound/k007232.cpp
+++ b/src/devices/sound/k007232.cpp
@@ -6,153 +6,44 @@
/*
Changelog, Hiromitsu Shioya 02/05/2002
- fix start address decode timing. (sample loop bug.)
+ fixed 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.
+ 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.
+ 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
+ (NB: Should different memory regions per channel be needed, the bankswitching function can set this up).
+ Chanelog, Nicola, August 1999:
+ Added 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"
+#include "wavwrite.h"
+#include <algorithm>
-#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 K007232_LOG_PCM (0)
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)
+k007232_device::k007232_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: device_t(mconfig, K007232, tag, owner, clock)
, device_sound_interface(mconfig, *this)
+ , device_memory_interface(mconfig, *this)
+ , m_data_config("data", ENDIANNESS_LITTLE, 8, 17) // 17 bit address, 8 bit data, can be bankswitched per each voices
, m_rom(*this, DEVICE_SELF)
+ , m_pcmlimit(~0)
+ , m_bank(0)
+ , m_stream(nullptr)
, m_port_write_handler(*this)
{
+ std::fill(std::begin(m_wreg), std::end(m_wreg), 0);
}
//-------------------------------------------------
@@ -161,187 +52,159 @@ k007232_device::k007232_device(const machine_config &mconfig, const char *tag, d
void k007232_device::device_start()
{
- /* Set up the chips */
- m_pcmlimit = m_rom.bytes();
+ // assumes it can make an address mask with m_rom.length() - 1
+ assert (!m_rom.found() || !(m_rom.length() & (m_rom.length() - 1)));
- m_port_write_handler.resolve();
+ m_pcmlimit = 1 << 17;
+ // default mapping (bankswitched ROM)
+ if (m_rom.found() && !has_configured_map(0))
+ {
+ if (m_rom.bytes() > 0x20000)
+ space(0).install_read_handler(0x00000, std::min<offs_t>(0x1ffff, m_rom.bytes() - 1), read8sm_delegate(*this, FUNC(k007232_device::read_rom_default)));
+ else
+ {
+ space(0).install_rom(0x00000, m_rom.length() - 1, m_rom.target());
+ m_pcmlimit = m_rom.bytes();
+ }
+ }
+ space(0).cache(m_cache);
- for (int i = 0; i < KDAC_A_PCM_MAX; i++)
+ /* Set up the chips */
+ for (int i = 0; i < PCM_MAX; i++)
{
- m_addr[i] = 0;
- m_start[i] = 0;
- m_step[i] = 0;
- m_play[i] = 0;
- m_bank[i] = 0;
+ m_channel[i].addr = 0;
+ m_channel[i].start = 0;
+ m_channel[i].counter = 0x1000;
+ m_channel[i].step = 0;
+ m_channel[i].play = 0;
+ m_channel[i].bank = 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 */
+ m_channel[0].vol[0] = 255; /* channel A output to output A */
+ m_channel[0].vol[1] = 0;
+ m_channel[1].vol[0] = 0;
+ m_channel[1].vol[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);
+ m_stream = stream_alloc(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(STRUCT_MEMBER(m_channel, vol));
+ save_item(STRUCT_MEMBER(m_channel, addr));
+ save_item(STRUCT_MEMBER(m_channel, counter));
+ save_item(STRUCT_MEMBER(m_channel, start));
+ save_item(STRUCT_MEMBER(m_channel, step));
+ save_item(STRUCT_MEMBER(m_channel, bank));
+ save_item(STRUCT_MEMBER(m_channel, play));
save_item(NAME(m_wreg));
}
-void k007232_device::KDAC_A_make_fncode()
+//-------------------------------------------------
+// device_clock_changed - called if the clock
+// changes
+//-------------------------------------------------
+
+void k007232_device::device_clock_changed()
{
-#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++;
- }
+ m_stream->set_sample_rate(clock() / 128);
+}
- 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] );
- }
+//-------------------------------------------------
+// memory_space_config - return a description of
+// any address spaces owned by this device
+//-------------------------------------------------
-#endif
+device_memory_interface::space_config_vector k007232_device::memory_space_config() const
+{
+ return space_config_vector{ std::make_pair(0, &m_data_config) };
}
-
/************************************************/
/* Konami PCM write register */
/************************************************/
-WRITE8_MEMBER( k007232_device::write )
-{
- int r = offset;
- int v = data;
+void k007232_device::write(offs_t offset, u8 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;
+ m_wreg[offset] = data; // standard data write
- reg_port = 0;
- if (r >= 0x06){
- reg_port = 1;
- r -= 0x06;
+ if (offset == 12)
+ {
+ // external port, usually volume control
+ m_port_write_handler(0, data);
}
-
- switch (r){
- case 0x00:
- case 0x01:
+ else if (offset == 13)
{
- /**** 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;
+ // loop flag, handled by standard data write
}
- 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;
+ else
+ {
+ channel_t &channel = m_channel[(offset >= 6 ? 1 : 0)];
+ int const reg_index = (offset >= 6 ? 6 : 0);
+ if (offset >= 6)
+ offset -= 6;
+
+ switch (offset)
+ {
+ case 0: // address step, LSB
+ case 1: // address step, MSB
+ channel.step = (BIT(m_wreg[reg_index + 1], 0, 4) << 8) | m_wreg[reg_index];
+ // TODO: Bit 4-5 is frequency divider, but not implemented now
+ break;
+ case 2:
+ case 3:
+ case 4:
+ // working data for start address
+ channel.start = (BIT(m_wreg[reg_index + 4], 0) << 16) | (m_wreg[reg_index + 3] << 8) | m_wreg[reg_index + 2];
+ break;
+ case 5: // start address
+ if (channel.start < m_pcmlimit)
+ {
+ channel.play = true;
+ channel.addr = channel.start;
+ channel.counter = 0x1000;
+ }
+ break;
}
- break;
- }
}
}
/************************************************/
/* Konami PCM read register */
/************************************************/
-READ8_MEMBER( k007232_device::read )
+
+u8 k007232_device::read(offs_t offset)
{
- 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;
- }
+ if (!machine().side_effects_disabled())
+ {
+ if (offset == 5 || offset == 11)
+ {
+ channel_t &channel = m_channel[(offset == 11) ? 1 : 0];
+
+ if (channel.start < m_pcmlimit)
+ {
+ channel.play = true;
+ channel.addr = channel.start;
+ channel.counter = 0x1000;
+ }
+ }
}
return 0;
}
/*****************************************************************************/
-void k007232_device::set_volume(int channel,int volumeA,int volumeB)
+void k007232_device::set_volume(int channel, int vol_a, int vol_b)
{
- m_vol[channel][0] = volumeA;
- m_vol[channel][1] = volumeB;
+ m_channel[channel].vol[0] = vol_a;
+ m_channel[channel].vol[1] = vol_b;
}
-void k007232_device::set_bank(int chABank, int chBBank )
+void k007232_device::set_bank(int chan_a_bank, int chan_b_bank)
{
- m_bank[0] = chABank<<17;
- m_bank[1] = chBBank<<17;
+ m_channel[0].bank = chan_a_bank << 17;
+ m_channel[1].bank = chan_b_bank << 17;
}
/*****************************************************************************/
@@ -351,74 +214,78 @@ void k007232_device::set_bank(int chABank, int chBBank )
// 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)
+void k007232_device::sound_stream_update(sound_stream &stream)
{
- 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 (K007232_LOG_PCM)
{
- 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)
+ for (int i = 0; i < PCM_MAX; i++)
{
- if( (m_rom[old_addr] & 0x80) || old_addr >= m_pcmlimit )
+ channel_t &channel = m_channel[i];
+ if (channel.play)
{
- /* end of sample */
-
- if( m_wreg[0x0d]&(1<<i) )
- {
- /* loop to the beginning */
- m_start[i] =
- ((((unsigned int)m_wreg[i*0x06 + 0x04]<<16)&0x00010000) |
- (((unsigned int)m_wreg[i*0x06 + 0x03]<< 8)&0x0000ff00) |
- (((unsigned int)m_wreg[i*0x06 + 0x02] )&0x000000ff) |
- m_bank[i]);
- addr = m_start[i];
- m_addr[i] = 0;
- old_addr = addr; /* skip loop */
- }
- else
- {
- /* stop sample */
- m_play[i] = 0;
+ char filebuf[256];
+ snprintf(filebuf, 256, "pcm%08x.wav", channel.start);
+ util::wav_file_ptr file = util::wav_open(filebuf, stream.sample_rate(), 1);
+ if (file)
+ {
+ u32 addr = channel.start;
+ while (!BIT(read_sample(i, addr), 7) && addr < m_pcmlimit)
+ {
+ int16_t out = ((read_sample(i, addr) & 0x7f) - 0x40) << 7;
+ util::wav_add_data_16(*file, &out, 1);
+ addr++;
+ }
+ }
}
- break;
- }
-
- old_addr++;
}
+ }
- if (m_play[i] == 0)
- break;
-
- m_addr[i] += m_step[i];
-
- out = (m_rom[addr] & 0x7f) - 0x40;
-
- outputs[0][j] += out * volA;
- outputs[1][j] += out * volB;
+ for (int j = 0; j < stream.samples(); j++)
+ {
+ s32 lsum = 0, rsum = 0;
+ for (int i = 0; i < PCM_MAX; i++)
+ {
+ channel_t &channel = m_channel[i];
+ if (channel.play)
+ {
+ /**** PCM setup ****/
+ int const vol_a = channel.vol[0] * 2;
+ int const vol_b = channel.vol[1] * 2;
+
+ u32 addr = channel.addr & 0x1ffff;
+ while (channel.counter <= channel.step) // result : clock / (4 * (4096 - frequency))
+ {
+ if (BIT(read_sample(i, addr++), 7) || addr >= m_pcmlimit)
+ {
+ // end of sample
+ if (BIT(m_wreg[13], i))
+ {
+ /* loop to the beginning */
+ addr = channel.start;
+ }
+ else
+ {
+ /* stop sample */
+ channel.play = false;
+ break;
+ }
+ }
+ channel.counter += (0x1000 - channel.step);
+ }
+ channel.addr = addr;
+
+ if (!channel.play)
+ break;
+
+ channel.counter -= 32;
+
+ int const out = (read_sample(i, addr) & 0x7f) - 0x40;
+
+ lsum += out * vol_a;
+ rsum += out * vol_b;
+ }
}
- }
+ stream.put_int(0, j, lsum, 32768);
+ stream.put_int(1, j, rsum, 32768);
}
}