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.cpp125
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