diff options
Diffstat (limited to 'src/devices/machine/8364_paula.cpp')
-rw-r--r-- | src/devices/machine/8364_paula.cpp | 125 |
1 files changed, 100 insertions, 25 deletions
diff --git a/src/devices/machine/8364_paula.cpp b/src/devices/machine/8364_paula.cpp index 6869ffb3395..83c4f033661 100644 --- a/src/devices/machine/8364_paula.cpp +++ b/src/devices/machine/8364_paula.cpp @@ -2,22 +2,36 @@ // copyright-holders: Aaron Giles, Dirk Best /*************************************************************************** - Commodore 8364 "Paula" + MOS Technology 8364 "Paula" + + TODO: + - Inherit FDC, serial and irq controller to here; + - Move Agnus "location" logic from here; + - low-pass filter; + - convert volume values to non-linear dB scale (cfr. ) + - Verify ADKCON modulation; + - Verify manual mode; + - When a DMA stop occurs, is the correlated channel playback stopped + at the end of the current cycle or as soon as possible like current + implementation? ***************************************************************************/ #include "emu.h" #include "8364_paula.h" +#define LIVE_AUDIO_VIEW 0 + //#define VERBOSE 1 #include "logmacro.h" +#include <cstring> //************************************************************************** // DEVICE DEFINITIONS //************************************************************************** -DEFINE_DEVICE_TYPE(PAULA_8364, paula_8364_device, "paula_8364", "8364 Paula") +DEFINE_DEVICE_TYPE(PAULA_8364, paula_8364_device, "paula_8364", "MOS 8364 \"Paula\"") //************************************************************************* @@ -51,9 +65,6 @@ void paula_8364_device::device_start() for (int i = 0; i < 4; i++) { m_channel[i].index = i; - m_channel[i].curticks = 0; - m_channel[i].manualmode = false; - m_channel[i].curlocation = 0; m_channel[i].irq_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(paula_8364_device::signal_irq), this)); } @@ -61,6 +72,24 @@ void paula_8364_device::device_start() m_stream = stream_alloc(0, 4, clock() / CLOCK_DIVIDER); } +void paula_8364_device::device_reset() +{ + m_dmacon = 0; + m_adkcon = 0; + for (auto &chan : m_channel) + { + chan.loc = 0; + chan.len = 0; + chan.per = 0; + chan.vol = 0; + chan.curticks = 0; + chan.manualmode = false; + chan.curlocation = 0; + chan.curlength = 0; + chan.dma_enabled = false; + } +} + //------------------------------------------------- // update - stream updater //------------------------------------------------- @@ -99,6 +128,18 @@ void paula_8364_device::reg_w(offs_t offset, uint16_t data) case REG_DMACON: m_stream->update(); m_dmacon = (data & 0x8000) ? (m_dmacon | (data & 0x021f)) : (m_dmacon & ~(data & 0x021f)); // only bits 15, 9 and 5 to 0 + // update the DMA latches on each channel and reload if fresh + // This holds true particularly for Ocean games (bchvolly, lostpatr, pang) and waylildr: + // they sets a DMA length for a channel then enable DMA then resets that length to 1 + // after a short delay loop. + for (int channum = 0; channum < 4; channum++) + { + audio_channel *chan = &m_channel[channum]; + if (!chan->dma_enabled && ((m_dmacon >> channum) & 1)) + dma_reload(chan, true); + + chan->dma_enabled = BIT(m_dmacon, channum); + } break; case REG_ADKCON: @@ -106,7 +147,7 @@ void paula_8364_device::reg_w(offs_t offset, uint16_t data) m_adkcon = (data & 0x8000) ? (m_adkcon | (data & 0x7fff)) : (m_adkcon & ~(data & 0x7fff)); break; - // to be moved + // FIXME: location belongs to Agnus case REG_AUD0LCL: m_channel[CHAN_0].loc = (m_channel[CHAN_0].loc & 0xffff0000) | ((data & 0xfffe) << 0); break; // 15-bit case REG_AUD0LCH: m_channel[CHAN_0].loc = (m_channel[CHAN_0].loc & 0x0000ffff) | ((data & 0x001f) << 16); break; // 3-bit on ocs, 5-bit ecs case REG_AUD1LCL: m_channel[CHAN_1].loc = (m_channel[CHAN_1].loc & 0xffff0000) | ((data & 0xfffe) << 0); break; // 15-bit @@ -119,19 +160,19 @@ void paula_8364_device::reg_w(offs_t offset, uint16_t data) // audio data case REG_AUD0LEN: m_channel[CHAN_0].len = data; break; case REG_AUD0PER: m_channel[CHAN_0].per = data; break; - case REG_AUD0VOL: m_channel[CHAN_0].vol = data; break; + case REG_AUD0VOL: m_channel[CHAN_0].vol = data & 0x7f; break; case REG_AUD0DAT: m_channel[CHAN_0].dat = data; m_channel[CHAN_0].manualmode = true; break; case REG_AUD1LEN: m_channel[CHAN_1].len = data; break; case REG_AUD1PER: m_channel[CHAN_1].per = data; break; - case REG_AUD1VOL: m_channel[CHAN_1].vol = data; break; + case REG_AUD1VOL: m_channel[CHAN_1].vol = data & 0x7f; break; case REG_AUD1DAT: m_channel[CHAN_1].dat = data; m_channel[CHAN_1].manualmode = true; break; case REG_AUD2LEN: m_channel[CHAN_2].len = data; break; case REG_AUD2PER: m_channel[CHAN_2].per = data; break; - case REG_AUD2VOL: m_channel[CHAN_2].vol = data; break; + case REG_AUD2VOL: m_channel[CHAN_2].vol = data & 0x7f; break; case REG_AUD2DAT: m_channel[CHAN_2].dat = data; m_channel[CHAN_2].manualmode = true; break; case REG_AUD3LEN: m_channel[CHAN_3].len = data; break; case REG_AUD3PER: m_channel[CHAN_3].per = data; break; - case REG_AUD3VOL: m_channel[CHAN_3].vol = data; break; + case REG_AUD3VOL: m_channel[CHAN_3].vol = data & 0x7f; break; case REG_AUD3DAT: m_channel[CHAN_3].dat = data; m_channel[CHAN_3].manualmode = true; break; } } @@ -142,20 +183,51 @@ void paula_8364_device::reg_w(offs_t offset, uint16_t data) TIMER_CALLBACK_MEMBER( paula_8364_device::signal_irq ) { - m_int_w(param); + m_int_w(param, 1); } //------------------------------------------------- // dma_reload //------------------------------------------------- -void paula_8364_device::dma_reload(audio_channel *chan) +void paula_8364_device::dma_reload(audio_channel *chan, bool startup) { chan->curlocation = chan->loc; + // TODO: how to treat length == 0? chan->curlength = chan->len; - chan->irq_timer->adjust(attotime::from_hz(15750), chan->index); // clock() / 227 + // TODO: on startup=false irq should be delayed two cycles + if (startup) + chan->irq_timer->adjust(attotime::from_hz(15750), chan->index); // clock() / 227 + else + signal_irq(nullptr, chan->index); - LOG("dma_reload(%d): offs=%05X len=%04X\n", chan->index, chan->curlocation, chan->curlength); + LOG("dma_reload(%d): offs=%06X len=%04X\n", chan->index, chan->curlocation, chan->curlength); +} + +std::string paula_8364_device::print_audio_state() +{ + std::ostringstream outbuffer; + + util::stream_format(outbuffer, "DMACON: %04x (%d) ADKCON %04x\n", m_dmacon, BIT(m_dmacon, 9), m_adkcon); + for (auto &chan : m_channel) + { + util::stream_format(outbuffer, "%d (%d) (%d%d) REGS: %06x %04x %03x %02x %d LIVE: %06x %04x %d\n" + , chan.index + , BIT(m_dmacon, chan.index) + , BIT(m_adkcon, chan.index+4) + , BIT(m_adkcon, chan.index) + , chan.loc + , chan.len + , chan.per + , chan.vol + , chan.manualmode + , chan.curlocation + , chan.curlength + , chan.dma_enabled + ); + } + + return outbuffer.str(); } //------------------------------------------------- @@ -182,15 +254,8 @@ void paula_8364_device::sound_stream_update(sound_stream &stream, std::vector<re int samples = outputs[0].samples() * CLOCK_DIVIDER; - // update the DMA states on each channel and reload if fresh - for (channum = 0; channum < 4; channum++) - { - audio_channel *chan = &m_channel[channum]; - if (!chan->dma_enabled && ((m_dmacon >> channum) & 1)) - dma_reload(chan); - - chan->dma_enabled = BIT(m_dmacon, channum); - } + if (LIVE_AUDIO_VIEW) + popmessage(print_audio_state()); // loop until done while (samples > 0) @@ -219,6 +284,7 @@ void paula_8364_device::sound_stream_update(sound_stream &stream, std::vector<re int i; // normalize the volume value + // FIXME: definitely not linear volume = (volume & 0x40) ? 64 : (volume & 0x3f); volume *= 4; @@ -268,9 +334,18 @@ void paula_8364_device::sound_stream_update(sound_stream &stream, std::vector<re if (chan->curlength != 0) chan->curlength--; - // if we run out of data, reload the dma + // if we run out of data, reload the dma and signal an IRQ, + // gpmaster/asparmgp definitely expects this + // (uses channel 3 as a sequencer, changing the start address on the fly) if (chan->curlength == 0) - dma_reload(chan); + { + dma_reload(chan, false); + // reload the data pointer, otherwise aliasing / buzzing outside the given buffer will be heard + // For example: Xenon 2 sets up location=0x63298 length=0x20 + // for silencing channels on-the-fly without relying on irqs. + // Without this the location will read at 0x632d8 (data=0x7a7d), causing annoying buzzing. + chan->dat = m_mem_r(chan->curlocation); + } } // latch the next byte of the sample |