summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/8364_paula.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/machine/8364_paula.cpp')
-rw-r--r--src/devices/machine/8364_paula.cpp193
1 files changed, 104 insertions, 89 deletions
diff --git a/src/devices/machine/8364_paula.cpp b/src/devices/machine/8364_paula.cpp
index 83c4f033661..a06eed90ca4 100644
--- a/src/devices/machine/8364_paula.cpp
+++ b/src/devices/machine/8364_paula.cpp
@@ -1,6 +1,6 @@
// license: BSD-3-Clause
// copyright-holders: Aaron Giles, Dirk Best
-/***************************************************************************
+/******************************************************************************
MOS Technology 8364 "Paula"
@@ -10,12 +10,14 @@
- low-pass filter;
- convert volume values to non-linear dB scale (cfr. )
- Verify ADKCON modulation;
- - Verify manual mode;
+ - Verify manual mode:
+ \- AGA roadkill during gameplay, which also has very long period setups,
+ extremely aliased;
- 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"
@@ -43,11 +45,11 @@ DEFINE_DEVICE_TYPE(PAULA_8364, paula_8364_device, "paula_8364", "MOS 8364 \"Paul
//-------------------------------------------------
paula_8364_device::paula_8364_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : device_t(mconfig, PAULA_8364, tag, owner, clock),
- device_sound_interface(mconfig, *this),
- m_mem_r(*this), m_int_w(*this),
- m_dmacon(0), m_adkcon(0),
- m_stream(nullptr)
+ : device_t(mconfig, PAULA_8364, tag, owner, clock)
+ , device_sound_interface(mconfig, *this)
+ , m_chipmem_r(*this, 0)
+ , m_int_w(*this)
+ , m_stream(nullptr)
{
}
@@ -57,15 +59,11 @@ paula_8364_device::paula_8364_device(const machine_config &mconfig, const char *
void paula_8364_device::device_start()
{
- // resolve callbacks
- m_mem_r.resolve_safe(0);
- m_int_w.resolve_safe();
-
// initialize channels
for (int i = 0; i < 4; i++)
{
m_channel[i].index = i;
- m_channel[i].irq_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(paula_8364_device::signal_irq), this));
+ m_channel[i].irq_timer = timer_alloc(FUNC(paula_8364_device::signal_irq), this);
}
// create the stream
@@ -74,8 +72,7 @@ void paula_8364_device::device_start()
void paula_8364_device::device_reset()
{
- m_dmacon = 0;
- m_adkcon = 0;
+ m_dma_master_enable = false;
for (auto &chan : m_channel)
{
chan.loc = 0;
@@ -87,6 +84,8 @@ void paula_8364_device::device_reset()
chan.curlocation = 0;
chan.curlength = 0;
chan.dma_enabled = false;
+ chan.atper = false;
+ chan.atvol = false;
}
}
@@ -104,76 +103,91 @@ void paula_8364_device::update()
// IMPLEMENTATION
//**************************************************************************
-uint16_t paula_8364_device::reg_r(offs_t offset)
+template <u8 ch> void paula_8364_device::audio_channel_map(address_map &map)
+{
+ // TODO: location addresses belongs to Agnus
+ map(0x00, 0x01).w(FUNC(paula_8364_device::audxlch_w<ch>));
+ map(0x02, 0x03).w(FUNC(paula_8364_device::audxlcl_w<ch>));
+ map(0x04, 0x05).w(FUNC(paula_8364_device::audxlen_w<ch>));
+ map(0x06, 0x07).w(FUNC(paula_8364_device::audxper_w<ch>));
+ map(0x08, 0x09).w(FUNC(paula_8364_device::audxvol_w<ch>));
+ map(0x0a, 0x0b).w(FUNC(paula_8364_device::audxdat_w<ch>));
+}
+
+// Instantiate channel maps
+template void paula_8364_device::audio_channel_map<0>(address_map &map);
+template void paula_8364_device::audio_channel_map<1>(address_map &map);
+template void paula_8364_device::audio_channel_map<2>(address_map &map);
+template void paula_8364_device::audio_channel_map<3>(address_map &map);
+
+template <u8 ch> void paula_8364_device::audxlch_w(u16 data)
+{
+ m_stream->update();
+ // TODO: chipmem mask
+ m_channel[ch].loc = (m_channel[ch].loc & 0x0000ffff) | ((data & 0x001f) << 16);
+}
+
+template <u8 ch> void paula_8364_device::audxlcl_w(u16 data)
+{
+ m_stream->update();
+ m_channel[ch].loc = (m_channel[ch].loc & 0xffff0000) | ((data & 0xfffe) << 0);
+}
+
+template <u8 ch> void paula_8364_device::audxlen_w(u16 data)
+{
+ m_stream->update();
+ m_channel[ch].len = data;
+}
+
+template <u8 ch> void paula_8364_device::audxper_w(u16 data)
+{
+ m_stream->update();
+ m_channel[ch].per = data;
+}
+
+template <u8 ch> void paula_8364_device::audxvol_w(u16 data)
+{
+ m_stream->update();
+ m_channel[ch].vol = data & 0x7f;
+}
+
+template <u8 ch> void paula_8364_device::audxdat_w(u16 data)
+{
+ m_stream->update();
+ m_channel[ch].dat = data;
+ m_channel[ch].manualmode = true;
+}
+
+void paula_8364_device::dmacon_set(u16 data)
{
- switch (offset)
+ m_stream->update();
+
+ m_dma_master_enable = bool(BIT(data, 9));
+
+ // 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 finally resets that length to 1
+ // after a short delay loop.
+ for (int channum = 0; channum < 4; channum++)
{
- case REG_DMACONR:
- return m_dmacon;
+ audio_channel *chan = &m_channel[channum];
+ if (!chan->dma_enabled && ((data >> channum) & 1))
+ dma_reload(chan, true);
- case REG_ADKCONR:
- return m_adkcon;
+ chan->dma_enabled = bool(BIT(data, channum));
}
-
- return 0xffff;
}
-void paula_8364_device::reg_w(offs_t offset, uint16_t data)
+void paula_8364_device::adkcon_set(u16 data)
{
- if (offset >= 0xa0 && offset <= 0xdf)
- m_stream->update();
+ m_stream->update();
- switch (offset)
+ for (int channum = 0; channum < 4; channum++)
{
- 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);
+ audio_channel *chan = &m_channel[channum];
- chan->dma_enabled = BIT(m_dmacon, channum);
- }
- break;
-
- case REG_ADKCON:
- m_stream->update();
- m_adkcon = (data & 0x8000) ? (m_adkcon | (data & 0x7fff)) : (m_adkcon & ~(data & 0x7fff));
- break;
-
- // 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
- case REG_AUD1LCH: m_channel[CHAN_1].loc = (m_channel[CHAN_1].loc & 0x0000ffff) | ((data & 0x001f) << 16); break; // 3-bit on ocs, 5-bit ecs
- case REG_AUD2LCL: m_channel[CHAN_2].loc = (m_channel[CHAN_2].loc & 0xffff0000) | ((data & 0xfffe) << 0); break; // 15-bit
- case REG_AUD2LCH: m_channel[CHAN_2].loc = (m_channel[CHAN_2].loc & 0x0000ffff) | ((data & 0x001f) << 16); break; // 3-bit on ocs, 5-bit ecs
- case REG_AUD3LCL: m_channel[CHAN_3].loc = (m_channel[CHAN_3].loc & 0xffff0000) | ((data & 0xfffe) << 0); break; // 15-bit
- case REG_AUD3LCH: m_channel[CHAN_3].loc = (m_channel[CHAN_3].loc & 0x0000ffff) | ((data & 0x001f) << 16); break; // 3-bit on ocs, 5-bit ecs
-
- // 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 & 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 & 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 & 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 & 0x7f; break;
- case REG_AUD3DAT: m_channel[CHAN_3].dat = data; m_channel[CHAN_3].manualmode = true; break;
+ chan->atper = bool(BIT(data, channum + 4));
+ chan->atvol = bool(BIT(data, channum));
}
}
@@ -193,13 +207,14 @@ TIMER_CALLBACK_MEMBER( paula_8364_device::signal_irq )
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;
+ // TODO: Unconfirmed, assume max size if length is 0.
+ // cfr. wrestlmn and videokid, where they pratically never get even close to buffer exhaustion.
+ chan->curlength = chan->len == 0 ? 0x10000 : chan->len;
// 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);
+ signal_irq(chan->index);
LOG("dma_reload(%d): offs=%06X len=%04X\n", chan->index, chan->curlocation, chan->curlength);
}
@@ -208,14 +223,14 @@ 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);
+ util::stream_format(outbuffer, "DMA master %d\n", m_dma_master_enable);
for (auto &chan : m_channel)
{
- util::stream_format(outbuffer, "%d (%d) (%d%d) REGS: %06x %04x %03x %02x %d LIVE: %06x %04x %d\n"
+ util::stream_format(outbuffer, "%d DMA (%d) ADK (%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.dma_enabled
+ , chan.atper
+ , chan.atvol
, chan.loc
, chan.len
, chan.per
@@ -239,7 +254,7 @@ void paula_8364_device::sound_stream_update(sound_stream &stream, std::vector<re
int channum, sampoffs = 0;
// if all DMA off, disable all channels
- if (BIT(m_dmacon, 9) == 0)
+ if (m_dma_master_enable == false)
{
m_channel[0].dma_enabled =
m_channel[1].dma_enabled =
@@ -289,7 +304,7 @@ void paula_8364_device::sound_stream_update(sound_stream &stream, std::vector<re
volume *= 4;
// are we modulating the period of the next channel?
- if ((m_adkcon >> channum) & 0x10)
+ if (chan->atper)
{
nextper = chan->dat;
nextvol = -1;
@@ -297,7 +312,7 @@ void paula_8364_device::sound_stream_update(sound_stream &stream, std::vector<re
}
// are we modulating the volume of the next channel?
- else if ((m_adkcon >> channum) & 0x01)
+ else if (chan->atvol)
{
nextper = -1;
nextvol = chan->dat;
@@ -329,7 +344,7 @@ void paula_8364_device::sound_stream_update(sound_stream &stream, std::vector<re
chan->curlocation++;
if (chan->dma_enabled && !(chan->curlocation & 1))
{
- chan->dat = m_mem_r(chan->curlocation);
+ chan->dat = m_chipmem_r(chan->curlocation);
if (chan->curlength != 0)
chan->curlength--;
@@ -344,7 +359,7 @@ void paula_8364_device::sound_stream_update(sound_stream &stream, std::vector<re
// 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);
+ chan->dat = m_chipmem_r(chan->curlocation);
}
}
@@ -357,7 +372,7 @@ void paula_8364_device::sound_stream_update(sound_stream &stream, std::vector<re
// if we're in manual mode, signal an interrupt once we latch the low byte
if (!chan->dma_enabled && chan->manualmode && (chan->curlocation & 1))
{
- signal_irq(nullptr, channum);
+ signal_irq(channum);
chan->manualmode = false;
}
}