k007232.cpp: Updates sound routines (#6857)

k007232.cpp: Updates and cleanup [cam900]
- Use device_memory_interface for fetching sample
- Fix frequency behavior
- Allow dynamic clock changes
- Use shorter/correct type values
- Simplify update routine
This commit is contained in:
cam900 2020-07-06 06:27:22 +09:00 committed by GitHub
parent c4b9b2a3ac
commit 0fa6e8255b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 128 additions and 96 deletions

View File

@ -26,18 +26,24 @@
#include "emu.h" #include "emu.h"
#include "k007232.h" #include "k007232.h"
#include "wavwrite.h" #include "wavwrite.h"
#include <algorithm>
#define K007232_LOG_PCM (0) #define K007232_LOG_PCM (0)
#define BASE_SHIFT (12)
DEFINE_DEVICE_TYPE(K007232, k007232_device, "k007232", "K007232 PCM Controller") 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_t(mconfig, K007232, tag, owner, clock)
, device_sound_interface(mconfig, *this) , 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_rom(*this, DEVICE_SELF)
, m_pcmlimit(~0)
, m_bank(0)
, m_stream(nullptr)
, m_port_write_handler(*this) , m_port_write_handler(*this)
{ {
std::fill(std::begin(m_wreg), std::end(m_wreg), 0);
} }
//------------------------------------------------- //-------------------------------------------------
@ -46,59 +52,77 @@ k007232_device::k007232_device(const machine_config &mconfig, const char *tag, d
void k007232_device::device_start() void k007232_device::device_start()
{ {
/* Set up the chips */ m_pcmlimit = 1 << 17;
m_pcmlimit = m_rom.bytes(); // default mapping (bankswitched ROM)
if ((m_rom.target() != nullptr) && (!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.mask(), m_rom.target());
m_pcmlimit = m_rom.bytes();
}
}
space(0).cache(m_cache);
/* Set up the chips */
m_port_write_handler.resolve_safe(); m_port_write_handler.resolve_safe();
for (int i = 0; i < KDAC_A_PCM_MAX; i++) for (int i = 0; i < KDAC_A_PCM_MAX; i++)
{ {
m_addr[i] = 0; m_channel[i].addr = 0;
m_start[i] = 0; m_channel[i].start = 0;
m_step[i] = 0; m_channel[i].counter = 0x200;
m_play[i] = 0; m_channel[i].step = 0;
m_bank[i] = 0; m_channel[i].play = 0;
m_channel[i].bank = 0;
} }
m_vol[0][0] = 255; /* channel A output to output A */ m_channel[0].vol[0] = 255; /* channel A output to output A */
m_vol[0][1] = 0; m_channel[0].vol[1] = 0;
m_vol[1][0] = 0; m_channel[1].vol[0] = 0;
m_vol[1][1] = 255; /* channel B output to output B */ m_channel[1].vol[1] = 255; /* channel B output to output B */
for (auto & elem : m_wreg) for (auto & elem : m_wreg)
elem = 0; elem = 0;
m_stream = machine().sound().stream_alloc(*this, 0 , 2, clock()/128); m_stream = machine().sound().stream_alloc(*this, 0, 2, clock()/128);
make_fncodes(); save_item(STRUCT_MEMBER(m_channel, vol));
save_item(STRUCT_MEMBER(m_channel, addr));
save_item(NAME(m_vol)); save_item(STRUCT_MEMBER(m_channel, counter));
save_item(NAME(m_addr)); save_item(STRUCT_MEMBER(m_channel, start));
save_item(NAME(m_start)); save_item(STRUCT_MEMBER(m_channel, step));
save_item(NAME(m_step)); save_item(STRUCT_MEMBER(m_channel, bank));
save_item(NAME(m_bank)); save_item(STRUCT_MEMBER(m_channel, play));
save_item(NAME(m_play));
save_item(NAME(m_wreg)); save_item(NAME(m_wreg));
} }
void k007232_device::make_fncodes() //-------------------------------------------------
// device_clock_changed - called if the clock
// changes
//-------------------------------------------------
void k007232_device::device_clock_changed()
{ {
for (int i = 0; i < 0x200; i++) m_stream->set_sample_rate(clock()/128);
{
m_fncode[i] = (32 << BASE_SHIFT) / (0x200 - i);
}
} }
uint32_t k007232_device::get_start_address(int channel) //-------------------------------------------------
// memory_space_config - return a description of
// any address spaces owned by this device
//-------------------------------------------------
device_memory_interface::space_config_vector k007232_device::memory_space_config() const
{ {
const uint32_t reg_index = (channel ? 6 : 0); return space_config_vector{ std::make_pair(0, &m_data_config) };
return (BIT(m_wreg[reg_index + 4], 0) << 16) | (m_wreg[reg_index + 3] << 8) | m_wreg[reg_index + 2] | m_bank[channel];
} }
/************************************************/ /************************************************/
/* Konami PCM write register */ /* Konami PCM write register */
/************************************************/ /************************************************/
void k007232_device::write(offs_t offset, uint8_t data) void k007232_device::write(offs_t offset, u8 data)
{ {
m_stream->update(); m_stream->update();
@ -115,7 +139,7 @@ void k007232_device::write(offs_t offset, uint8_t data)
} }
else else
{ {
int channel = (offset >= 6 ? 1 : 0); channel_t *channel = &m_channel[(offset >= 6 ? 1 : 0)];
int reg_index = (offset >= 6 ? 6 : 0); int reg_index = (offset >= 6 ? 6 : 0);
if (offset >= 6) if (offset >= 6)
offset -= 6; offset -= 6;
@ -124,19 +148,20 @@ void k007232_device::write(offs_t offset, uint8_t data)
{ {
case 0: // address step, LSB case 0: // address step, LSB
case 1: // address step, MSB case 1: // address step, MSB
m_step[channel] = m_fncode[(BIT(m_wreg[reg_index + 1], 0) << 8) | m_wreg[reg_index]]; channel->step = (BIT(m_wreg[reg_index + 1], 0) << 8) | m_wreg[reg_index];
break; break;
case 2: case 2:
case 3: case 3:
case 4: case 4:
// working data for start address // 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; break;
case 5: // start address case 5: // start address
m_start[channel] = get_start_address(channel); if (channel->start < m_pcmlimit)
if (m_start[channel] < m_pcmlimit)
{ {
m_play[channel] = 1; channel->play = true;
m_addr[channel] = 0; channel->addr = channel->start;
channel->counter = 0x200;
} }
break; break;
} }
@ -147,18 +172,17 @@ void k007232_device::write(offs_t offset, uint8_t data)
/* Konami PCM read register */ /* Konami PCM read register */
/************************************************/ /************************************************/
uint8_t k007232_device::read(offs_t offset) u8 k007232_device::read(offs_t offset)
{ {
if (offset == 5 || offset == 11) if (offset == 5 || offset == 11)
{ {
int channel = (offset == 11) ? 1 : 0; channel_t *channel = &m_channel[(offset == 11) ? 1 : 0];
m_start[channel] = get_start_address(channel); if (channel->start < m_pcmlimit)
if (m_start[channel] < m_pcmlimit)
{ {
m_play[channel] = 1; channel->play = true;
m_addr[channel] = 0; channel->addr = channel->start;
channel->counter = 0x200;
} }
} }
return 0; return 0;
@ -168,14 +192,14 @@ uint8_t k007232_device::read(offs_t offset)
void k007232_device::set_volume(int channel, int vol_a, int vol_b) void k007232_device::set_volume(int channel, int vol_a, int vol_b)
{ {
m_vol[channel][0] = vol_a; m_channel[channel].vol[0] = vol_a;
m_vol[channel][1] = vol_b; m_channel[channel].vol[1] = vol_b;
} }
void k007232_device::set_bank(int chan_a_bank, int chan_b_bank) void k007232_device::set_bank(int chan_a_bank, int chan_b_bank)
{ {
m_bank[0] = chan_a_bank << 17; m_channel[0].bank = chan_a_bank << 17;
m_bank[1] = chan_b_bank << 17; m_channel[1].bank = chan_b_bank << 17;
} }
/*****************************************************************************/ /*****************************************************************************/
@ -194,17 +218,18 @@ void k007232_device::sound_stream_update(sound_stream &stream, stream_sample_t *
{ {
for (int i = 0; i < KDAC_A_PCM_MAX; i++) for (int i = 0; i < KDAC_A_PCM_MAX; i++)
{ {
if (m_play[i]) channel_t *channel = &m_channel[i];
if (channel->play)
{ {
char filebuf[256]; char filebuf[256];
snprintf(filebuf, 256, "pcm%08x.wav", m_start[i]); snprintf(filebuf, 256, "pcm%08x.wav", channel->start);
wav_file *file = wav_open(filebuf, stream.sample_rate(), 1); wav_file *file = wav_open(filebuf, stream.sample_rate(), 1);
if (file != nullptr) if (file != nullptr)
{ {
uint32_t addr = m_start[i]; u32 addr = channel->start;
while (!BIT(m_rom[addr], 7) && addr < m_pcmlimit) while (!BIT(read_sample(i, addr), 7) && addr < m_pcmlimit)
{ {
int16_t out = ((m_rom[addr] & 0x7f) - 0x40) << 7; int16_t out = ((read_sample(i, addr) & 0x7f) - 0x40) << 7;
wav_add_data_16(file, &out, 1); wav_add_data_16(file, &out, 1);
addr++; addr++;
} }
@ -214,50 +239,45 @@ void k007232_device::sound_stream_update(sound_stream &stream, stream_sample_t *
} }
} }
for (int i = 0; i < KDAC_A_PCM_MAX; i++) for (int j = 0; j < samples; j++)
{ {
if (m_play[i]) for (int i = 0; i < KDAC_A_PCM_MAX; i++)
{ {
/**** PCM setup ****/ channel_t *channel = &m_channel[i];
uint32_t addr_lsb = (m_addr[i] >> BASE_SHIFT) & 0x000fffff; if (channel->play)
uint32_t addr = m_start[i] + addr_lsb;
int vol_a = m_vol[i][0] * 2;
int vol_b = m_vol[i][1] * 2;
for (int j = 0; j < samples; j++)
{ {
uint32_t old_addr = addr; /**** PCM setup ****/
addr_lsb = (m_addr[i] >> BASE_SHIFT) & 0x000fffff; int vol_a = channel->vol[0] * 2;
addr = m_start[i] + addr_lsb; int vol_b = channel->vol[1] * 2;
while (old_addr <= addr)
u32 addr = channel->addr & 0x1ffff;
while (channel->counter <= channel->step) // result : clock / (4 * (512 - frequency))
{ {
if (BIT(m_rom[old_addr], 7) || old_addr >= m_pcmlimit) if (BIT(read_sample(i, addr++), 7) || addr >= m_pcmlimit)
{ {
// end of sample // end of sample
if (BIT(m_wreg[13], i)) if (BIT(m_wreg[13], i))
{ {
/* loop to the beginning */ /* loop to the beginning */
m_start[i] = get_start_address(i); addr = channel->start;
addr = m_start[i];
m_addr[i] = 0;
old_addr = addr; /* skip loop */
} }
else else
{ {
/* stop sample */ /* stop sample */
m_play[i] = 0; channel->play = false;
break;
} }
break;
} }
old_addr++; channel->counter += (0x200 - channel->step);
} }
channel->addr = addr;
if (m_play[i] == 0) if (!channel->play)
break; break;
m_addr[i] += m_step[i]; channel->counter -= 32;
int out = (m_rom[addr] & 0x7f) - 0x40; int out = (read_sample(i, addr) & 0x7f) - 0x40;
outputs[0][j] += out * vol_a; outputs[0][j] += out * vol_a;
outputs[1][j] += out * vol_b; outputs[1][j] += out * vol_b;

View File

@ -9,15 +9,15 @@
#pragma once #pragma once
class k007232_device : public device_t, public device_sound_interface class k007232_device : public device_t, public device_sound_interface, public device_memory_interface
{ {
public: public:
k007232_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); k007232_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
auto port_write() { return m_port_write_handler.bind(); } auto port_write() { return m_port_write_handler.bind(); }
void write(offs_t offset, uint8_t data); void write(offs_t offset, u8 data);
uint8_t read(offs_t offset); u8 read(offs_t offset);
/* /*
The 007232 has two channels and produces two outputs. The volume control The 007232 has two channels and produces two outputs. The volume control
@ -34,34 +34,46 @@ public:
protected: protected:
// device-level overrides // device-level overrides
virtual void device_start() override; virtual void device_start() override;
virtual void device_clock_changed() override;
// sound stream update overrides // sound stream update overrides
virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) override; virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) override;
void make_fncodes(); // device_memory_interface configuration
virtual space_config_vector memory_space_config() const override;
address_space_config m_data_config;
private: private:
uint32_t get_start_address(int channel);
static constexpr unsigned KDAC_A_PCM_MAX = 2; /* Channels per chip */ static constexpr unsigned KDAC_A_PCM_MAX = 2; /* Channels per chip */
// internal state // internal state
required_region_ptr<uint8_t> m_rom; memory_access<17, 0, 0, ENDIANNESS_LITTLE>::cache m_cache;
optional_region_ptr<u8> m_rom;
uint8_t m_vol[KDAC_A_PCM_MAX][2]; /* volume for the left and right channel */ struct channel_t
uint32_t m_addr[KDAC_A_PCM_MAX]; {
uint32_t m_start[KDAC_A_PCM_MAX]; u8 vol[2]; /* volume for the left and right channel */
uint32_t m_step[KDAC_A_PCM_MAX]; u32 addr;
uint32_t m_bank[KDAC_A_PCM_MAX]; int counter;
int m_play[KDAC_A_PCM_MAX]; u32 start;
u16 step;
u32 bank;
bool play;
};
uint8_t m_wreg[0x10]; /* write data */ u8 read_rom_default(offs_t offset) { return m_rom[(m_bank + (offset & 0x1ffff)) & m_rom.mask()]; }
inline u8 read_sample(int channel, u32 addr) { m_bank = m_channel[channel].bank; return m_cache.read_byte(addr & 0x1ffff); }
uint32_t m_pcmlimit; channel_t m_channel[KDAC_A_PCM_MAX]; // 2 channels
sound_stream * m_stream; u8 m_wreg[0x10]; /* write data */
uint32_t m_fncode[0x200];
devcb_write8 m_port_write_handler; u32 m_pcmlimit;
u32 m_bank;
sound_stream *m_stream;
devcb_write8 m_port_write_handler;
}; };
DECLARE_DEVICE_TYPE(K007232, k007232_device) DECLARE_DEVICE_TYPE(K007232, k007232_device)