summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/sound/ap2010pcm.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/sound/ap2010pcm.cpp')
-rw-r--r--src/devices/sound/ap2010pcm.cpp224
1 files changed, 224 insertions, 0 deletions
diff --git a/src/devices/sound/ap2010pcm.cpp b/src/devices/sound/ap2010pcm.cpp
new file mode 100644
index 00000000000..7a7d9442be1
--- /dev/null
+++ b/src/devices/sound/ap2010pcm.cpp
@@ -0,0 +1,224 @@
+// license:BSD-3-Clause
+// copyright-holders:QUFB
+/**********************************************************************
+
+ PCM audio functions of the AP2010 LSI
+
+**********************************************************************/
+
+#include "emu.h"
+
+#include "ap2010pcm.h"
+
+#include <algorithm>
+
+#define VERBOSE (0)
+#include "logmacro.h"
+
+// device type definition
+DEFINE_DEVICE_TYPE(AP2010PCM, ap2010pcm_device, "ap2010pcm", "AP2010 PCM")
+
+ap2010pcm_device::ap2010pcm_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : device_t(mconfig, AP2010PCM, tag, owner, clock)
+ , device_sound_interface(mconfig, *this)
+ , m_sample_rate(0)
+ , m_fifo_size(0)
+ , m_fifo_head(0)
+ , m_fifo_tail(0)
+ , m_fifo_fast_size(0)
+ , m_fifo_fast_head(0)
+ , m_fifo_fast_tail(0)
+ , m_stream(nullptr)
+{ }
+
+void ap2010pcm_device::device_start()
+{
+ m_regs = make_unique_clear<uint32_t[]>(0x40/4);
+
+ m_sample_rate = 8000;
+
+ std::fill(std::begin(m_fifo_data), std::end(m_fifo_data), 0);
+ std::fill(std::begin(m_fifo_fast_data), std::end(m_fifo_fast_data), 0);
+
+ m_stream = stream_alloc(0, 1, m_sample_rate);
+
+ save_pointer(NAME(m_regs), 0x40/4);
+
+ save_item(NAME(m_volume));
+
+ save_item(NAME(m_sample_rate));
+
+ save_item(NAME(m_fifo_data));
+ save_item(NAME(m_fifo_size));
+ save_item(NAME(m_fifo_head));
+ save_item(NAME(m_fifo_tail));
+
+ save_item(NAME(m_fifo_fast_data));
+ save_item(NAME(m_fifo_fast_size));
+ save_item(NAME(m_fifo_fast_head));
+ save_item(NAME(m_fifo_fast_tail));
+}
+
+void ap2010pcm_device::sound_stream_update(sound_stream &stream)
+{
+ int16_t sample = 0;
+ uint16_t sample_empty_count = 0;
+ uint16_t fifo_size = m_fifo_size;
+ uint16_t fifo_fast_size = m_fifo_fast_size;
+ for (size_t i = 0; i < stream.samples(); i++) {
+ if (m_fifo_fast_size) {
+ sample = fifo_fast_pop();
+ } else if (m_fifo_size) {
+ sample = fifo_pop();
+ } else {
+ sample = 0;
+ sample_empty_count++;
+ }
+
+ stream.put_int(0, i, sample * m_volume, 32768);
+ }
+ if (fifo_size && sample_empty_count) {
+ LOG("pcm 0s = %d (had %d + fast %d, needed %d)\n", sample_empty_count, fifo_size, fifo_fast_size, stream.samples());
+ }
+}
+
+uint32_t ap2010pcm_device::reg_r(offs_t offset)
+{
+ offset &= 0x3f;
+ if (offset == 0) {
+ // PCM data (0x5001000c) only received when 0x50010000 & 1 != 0;
+ // PCM parameters (0x50010010, 0x50010018) only received when 0x50010000 & 4 != 0;
+ return (m_regs[0x4/4] != 0) ? 0x0f : 0;
+ } else if (offset == 0x4/4 && m_fifo_size > 0x1ff) {
+ // TODO: Verify in hardware, bit 1 might be cleared while busy playing?
+ return m_regs[offset] & 0xfffffffe;
+ } else if (offset == 0x1c/4) {
+ uint32_t fifo_size = m_fifo_size;
+ LOG("pcm asked size -> %d\n", fifo_size);
+
+ if (fifo_size > 0x1ff) {
+ // FIXME: Expected max length, what happens if more data is streamed?
+ fifo_size = 0x1ff;
+ } else if (fifo_size > 1) {
+ /*
+ Workaround to avoid missing samples during data stream:
+
+ while (dVar1 = read_volatile_4(PCM_CTRL), (dVar1 & 1) != 0) {
+ len = read_volatile_4(PCM_BUFLEN);
+ len_diff = 0x13e - (len & 0x1ff);
+ cVar2 = read_volatile_1(w_sound_test_var1);
+ if (cVar2 == '\x10') {
+ if (len_diff < 1) {
+ return in_lr;
+ }
+ pcm_write_1x_data();
+ }
+ ...
+ }
+ */
+ fifo_size -= 2;
+ }
+ return fifo_size;
+ }
+
+ return m_regs[offset];
+}
+
+void ap2010pcm_device::reg_w(offs_t offset, uint32_t data, uint32_t mem_mask)
+{
+ offset &= 0x3f;
+ COMBINE_DATA(&m_regs[offset]);
+
+ m_stream->update();
+
+ switch (offset) {
+ case 0x4/4:
+ if ((data & 0x78) == 0x78) {
+ m_sample_rate = 8000 * (1 + BIT(data, 1));
+ m_stream->set_sample_rate(m_sample_rate);
+
+ // When a new stream starts, stop playback of previous stream
+ m_fifo_size = 0;
+ m_fifo_head = 0;
+ m_fifo_tail = 0;
+
+ LOG("pcm stream start, rate = %d\n", m_sample_rate);
+ }
+ break;
+ case 0xc/4:
+ if (ACCESSING_BITS_16_31) {
+ fifo_push((data & 0xffff0000U) >> 16);
+ }
+ if (ACCESSING_BITS_0_15) {
+ fifo_push(data & 0x0000ffffU);
+ }
+ break;
+ // These samples are always played first
+ case 0x10/4:
+ if (ACCESSING_BITS_16_31) {
+ fifo_fast_push((data & 0xffff0000U) >> 16);
+ }
+ if (ACCESSING_BITS_0_15) {
+ fifo_fast_push(data & 0x0000ffffU);
+ }
+ break;
+ // Panning. TODO: Identify bits for each channel
+ case 0x14/4:
+ LOG("pcm pan = %08x\n", data);
+ break;
+ // Volume control. When video output is disabled, it's possible to adjust volume
+ // using the 2 touch areas on the bottom-left of the Storyware. Range 0..345
+ case 0x18/4:
+ m_volume = std::min(((data & 0x1ff00000U) >> 20) / 345.0f, 1.0f);
+ LOG("pcm vol = %08x -> %d\n", data, m_volume);
+ break;
+ }
+}
+
+uint16_t ap2010pcm_device::fifo_pop()
+{
+ uint16_t sample = m_fifo_data[m_fifo_head];
+ m_fifo_head = (m_fifo_head + 1) & (FIFO_MAX_SIZE - 1);
+ m_fifo_size--;
+ return sample;
+}
+
+uint16_t ap2010pcm_device::fifo_fast_pop()
+{
+ uint16_t sample = m_fifo_fast_data[m_fifo_fast_head];
+ m_fifo_fast_head = (m_fifo_fast_head + 1) & (FIFO_MAX_SIZE - 1);
+ m_fifo_fast_size--;
+ return sample;
+}
+
+void ap2010pcm_device::fifo_push(uint16_t sample)
+{
+ if (sample == 0) {
+ return;
+ }
+
+ // trash old data
+ if (m_fifo_size > FIFO_MAX_SIZE - 1) {
+ fifo_pop();
+ }
+
+ m_fifo_data[m_fifo_tail] = sample;
+ m_fifo_tail = (m_fifo_tail + 1) & (FIFO_MAX_SIZE - 1);
+ m_fifo_size++;
+}
+
+void ap2010pcm_device::fifo_fast_push(uint16_t sample)
+{
+ if (sample == 0) {
+ return;
+ }
+
+ // trash old data
+ if (m_fifo_fast_size > FIFO_MAX_SIZE - 1) {
+ fifo_fast_pop();
+ }
+
+ m_fifo_fast_data[m_fifo_fast_tail] = sample;
+ m_fifo_fast_tail = (m_fifo_fast_tail + 1) & (FIFO_MAX_SIZE - 1);
+ m_fifo_fast_size++;
+}